Procházet zdrojové kódy

porter.yaml v2 in the works!

Mohammed Nafees před 3 roky
rodič
revize
19b9830757

+ 26 - 5
cli/cmd/apply.go

@@ -20,6 +20,7 @@ import (
 	"github.com/porter-dev/porter/cli/cmd/deploy"
 	"github.com/porter-dev/porter/cli/cmd/deploy/wait"
 	"github.com/porter-dev/porter/cli/cmd/preview"
+	previewV2 "github.com/porter-dev/porter/cli/cmd/preview/v2"
 	previewInt "github.com/porter-dev/porter/internal/integrations/preview"
 	"github.com/porter-dev/porter/internal/templater/utils"
 	"github.com/porter-dev/switchboard/pkg/drivers"
@@ -29,6 +30,7 @@ import (
 	switchboardWorker "github.com/porter-dev/switchboard/pkg/worker"
 	"github.com/rs/zerolog"
 	"github.com/spf13/cobra"
+	"gopkg.in/yaml.v2"
 )
 
 // applyCmd represents the "porter apply" base command when called
@@ -105,18 +107,37 @@ func init() {
 }
 
 func apply(_ *types.GetAuthenticatedUserResponse, client *api.Client, _ []string) error {
-	if _, ok := os.LookupEnv("PORTER_VALIDATE_YAML"); ok {
-		err := applyValidate()
+	// read the porter.yaml file
+	fileBytes, err := ioutil.ReadFile(porterYAML)
+
+	if err != nil {
+		return fmt.Errorf("error reading porter.yaml: %w", err)
+	}
+
+	var porterYAMLVersion struct{ Version string }
+
+	err = yaml.Unmarshal(fileBytes, &porterYAMLVersion)
+
+	if err != nil {
+		return fmt.Errorf("error reading porter.yaml version: %w", err)
+	}
+
+	if porterYAMLVersion.Version == "v2" {
+		applier, err := previewV2.NewApplier(client, fileBytes, namespace)
 
 		if err != nil {
 			return err
 		}
+
+		return applier.Apply()
 	}
 
-	fileBytes, err := ioutil.ReadFile(porterYAML)
+	if _, ok := os.LookupEnv("PORTER_VALIDATE_YAML"); ok {
+		err := applyValidate()
 
-	if err != nil {
-		return fmt.Errorf("error reading porter.yaml: %w", err)
+		if err != nil {
+			return err
+		}
 	}
 
 	resGroup, err := parser.ParseRawBytes(fileBytes)

+ 152 - 0
cli/cmd/preview/v2/apply.go

@@ -0,0 +1,152 @@
+package v2
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/fatih/color"
+	api "github.com/porter-dev/porter/api/client"
+	apiTypes "github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/cli/cmd/config"
+	parser "github.com/porter-dev/switchboard/v2/pkg/parser"
+	types "github.com/porter-dev/switchboard/v2/pkg/types"
+	validator "github.com/porter-dev/switchboard/v2/pkg/validator"
+)
+
+const (
+	contantsEnvGroup = "preview-env-constants"
+
+	defaultCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()_+-={}[]"
+)
+
+type PreviewApplier struct {
+	apiClient *api.Client
+	rawBytes  []byte
+	namespace string
+	parsed    *types.ParsedPorterYAML
+}
+
+func NewApplier(client *api.Client, raw []byte, namespace string) (*PreviewApplier, error) {
+	parsed, err := parser.ParseRawBytes(raw)
+
+	if err != nil {
+		return nil, err
+	}
+
+	err = validator.ValidatePorterYAML(parsed)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &PreviewApplier{
+		apiClient: client,
+		rawBytes:  raw,
+		namespace: namespace,
+		parsed:    parsed,
+	}, nil
+}
+
+func (a *PreviewApplier) Apply() error {
+	err := a.processVariables()
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (a *PreviewApplier) processVariables() error {
+	color.New(color.FgBlue).Println("[porter.yaml] Processing variables") // FIXME: use a scoped logger
+
+	constantsMap := make(map[string]string)
+	variablesMap := make(map[string]string)
+
+	for _, v := range a.parsed.PorterYAML.Variables.GetValue() {
+		if v.Once.GetValue() {
+			// a constant which should be stored in the env group on first run
+			if exists, err := a.constantExistsInEnvGroup(v.Name.GetValue()); err == nil {
+				if exists == nil {
+					// this should not happen
+					return fmt.Errorf("internal error: please let the Porter team know about this and quote the following " +
+						"error:\n-----\nERROR: checking for constant existence in env group returned nil with no error")
+				}
+
+				val := *exists
+
+				if !val {
+					// create the constant in the env group
+					if v.Value.GetValue() != "" {
+						constantsMap[v.Name.GetValue()] = v.Value.GetValue()
+					} else if v.Random.GetValue() {
+						constantsMap[v.Name.GetValue()] = randomString(v.Length.GetValue(), defaultCharset)
+					} else {
+						// this should not happen
+						return fmt.Errorf("internal error: please let the Porter team know about this and quote the following "+
+							"error:\n-----\nERROR: for variable '%s', random is false and value is empty", v.Name.GetValue())
+					}
+				}
+			} else {
+				return fmt.Errorf("error checking for existence of constant %s: %w", v.Name.GetValue(), err)
+			}
+		} else {
+			if v.Value.GetValue() != "" {
+				variablesMap[v.Name.GetValue()] = v.Value.GetValue()
+			} else if v.Random.GetValue() {
+				variablesMap[v.Name.GetValue()] = randomString(v.Length.GetValue(), defaultCharset)
+			} else {
+				// this should not happen
+				return fmt.Errorf("internal error: please let the Porter team know about this and quote the following "+
+					"error:\n-----\nERROR: for variable '%s', random is false and value is empty", v.Name.GetValue())
+			}
+		}
+	}
+
+	if len(constantsMap) > 0 {
+		// we need to create these constants in the env group
+		_, err := a.apiClient.CreateEnvGroup(
+			context.Background(),
+			config.GetCLIConfig().Project,
+			config.GetCLIConfig().Cluster,
+			a.namespace,
+			&apiTypes.CreateEnvGroupRequest{
+				Name:      contantsEnvGroup,
+				Variables: constantsMap,
+			},
+		)
+
+		if err != nil {
+			return fmt.Errorf("error creating constants (variables with once set to true) in env group: %w", err)
+		}
+
+		for k, v := range constantsMap {
+			variablesMap[k] = v
+		}
+	}
+
+	return nil
+}
+
+func (a *PreviewApplier) constantExistsInEnvGroup(name string) (*bool, error) {
+	apiResponse, err := a.apiClient.GetEnvGroup(
+		context.Background(),
+		config.GetCLIConfig().Project,
+		config.GetCLIConfig().Cluster,
+		a.namespace,
+		&apiTypes.GetEnvGroupRequest{
+			Name: contantsEnvGroup,
+			// we do not care about the version because it always needs to be the latest
+		},
+	)
+
+	if err != nil {
+		return nil, err
+	}
+
+	if _, ok := apiResponse.Variables[name]; ok {
+		return boolean(true), nil
+	}
+
+	return boolean(false), nil
+}

+ 4 - 0
cli/cmd/preview/v2/default_driver.go

@@ -0,0 +1,4 @@
+package v2
+
+type DefaultDriver struct {
+}

+ 18 - 0
cli/cmd/preview/v2/hooks.go

@@ -0,0 +1,18 @@
+package v2
+
+import (
+	api "github.com/porter-dev/porter/api/client"
+	switchboardTypes "github.com/porter-dev/switchboard/pkg/types"
+)
+
+type VariablesHook struct {
+	client   *api.Client
+	resGroup *switchboardTypes.ResourceGroup
+}
+
+func NewCloneEnvGroupHook(client *api.Client, resourceGroup *switchboardTypes.ResourceGroup) *VariablesHook {
+	return &VariablesHook{
+		client:   client,
+		resGroup: resourceGroup,
+	}
+}

+ 18 - 0
cli/cmd/preview/v2/utils.go

@@ -0,0 +1,18 @@
+package v2
+
+import "crypto/rand"
+
+func boolean(v bool) *bool {
+	copy := v
+	return &copy
+}
+
+func randomString(length uint, charset string) string {
+	ll := len(charset)
+	b := make([]byte, length)
+	rand.Read(b) // generates len(b) random bytes
+	for i := uint(0); i < length; i++ {
+		b[i] = charset[int(b[i])%ll]
+	}
+	return string(b)
+}

+ 2 - 1
go.mod

@@ -118,9 +118,10 @@ require (
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/open-policy-agent/opa v0.44.0 // indirect
 	github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
+	github.com/porter-dev/switchboard/v2 v2.0.0-20221027143410-a81838caabbe // indirect
 	github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
 	github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect
-	github.com/santhosh-tekuri/jsonschema/v5 v5.0.1 // indirect
+	github.com/santhosh-tekuri/jsonschema/v5 v5.0.2 // indirect
 	github.com/tchap/go-patricia/v2 v2.3.1 // indirect
 	github.com/tfkhsr/jsonschema v0.0.0-20180218143334-273afdd5a88c // indirect
 	github.com/xanzy/go-gitlab v0.68.0 // indirect

+ 6 - 0
go.sum

@@ -1731,6 +1731,10 @@ github.com/porter-dev/switchboard v0.0.0-20220628112428-7665a0121e4f h1:REYJSDm2
 github.com/porter-dev/switchboard v0.0.0-20220628112428-7665a0121e4f/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
 github.com/porter-dev/switchboard v0.0.0-20221019155755-67ff2bf04935 h1:hfb3nt3AJXIBbevu6ARTg9SdOkMP6WLbKBiG5hT5rcc=
 github.com/porter-dev/switchboard v0.0.0-20221019155755-67ff2bf04935/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
+github.com/porter-dev/switchboard/v2 v2.0.0-20221027143410-a81838caabbe h1:VuU+EUPFi1gyKQa9nhv75GhcbWkyOxJh5JkByQ2GcyE=
+github.com/porter-dev/switchboard/v2 v2.0.0-20221027143410-a81838caabbe/go.mod h1:I/oKy2BmfIVtirJXM28W76Yqih0lR8EiaZ4lE5E5lY0=
+github.com/porter-dev/switchboard/v2 v2.0.0 h1:mg4c6mMHFNFBgprcac7ZcPxTPJeqZtfmH1O6pPt1h3U=
+github.com/porter-dev/switchboard/v2 v2.0.0/go.mod h1:I/oKy2BmfIVtirJXM28W76Yqih0lR8EiaZ4lE5E5lY0=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
 github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU=
@@ -1850,6 +1854,8 @@ github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKP
 github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4=
 github.com/santhosh-tekuri/jsonschema/v5 v5.0.1 h1:HNLA3HtUIROrQwG1cuu5EYuqk3UEoJ61Dr/9xkd6sok=
 github.com/santhosh-tekuri/jsonschema/v5 v5.0.1/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
+github.com/santhosh-tekuri/jsonschema/v5 v5.0.2 h1:zOYFITq/5SO7YOv39/Taw8s1skb0Py39K5V2XvCEP48=
+github.com/santhosh-tekuri/jsonschema/v5 v5.0.2/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
 github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=