From 15698eb3dd39f87452a9983176d1735738cd78f8 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Tue, 8 Mar 2022 22:46:56 +0100 Subject: [PATCH 1/9] Handle multiple backends for the TypeInstance --- .../images/jinja2/jinja2-cli/jinja2cli/cli.py | 2 + hack/images/merger/merger.sh | 8 +- internal/installation/capact_register.go | 16 +- pkg/argo-actions/errors.go | 5 + pkg/argo-actions/upload_type_instances.go | 27 ++- pkg/runner/common.go | 19 ++ pkg/runner/helm/output.go | 12 +- pkg/runner/terraform/runner.go | 21 ++- test/e2e/action_test.go | 175 +++++++++++++++++- test/e2e/cluster_test.go | 1 + 10 files changed, 271 insertions(+), 15 deletions(-) diff --git a/hack/images/jinja2/jinja2-cli/jinja2cli/cli.py b/hack/images/jinja2/jinja2-cli/jinja2cli/cli.py index 3cfc17925..3f134f36d 100644 --- a/hack/images/jinja2/jinja2-cli/jinja2cli/cli.py +++ b/hack/images/jinja2/jinja2-cli/jinja2cli/cli.py @@ -314,6 +314,8 @@ def cli(opts, args, config): # noqa: C901 if config.get("prefix") is not None and len(parsed_data) != 0: parsed_data = {config["prefix"]: parsed_data} + if config.get("strip-value") is True and len(parsed_data) != 0 and "value" in parsed_data: + parsed_data = parsed_data.get("value", {}) template_path = os.path.abspath(template_path) out.write(render(template_path, parsed_data, extensions, opts.filters, opts.strict)) diff --git a/hack/images/merger/merger.sh b/hack/images/merger/merger.sh index 321fcf3b2..ef312934c 100755 --- a/hack/images/merger/merger.sh +++ b/hack/images/merger/merger.sh @@ -3,10 +3,16 @@ SRC=${SRC:-"/yamls"} OUT=${OUT:-"/merged.yaml"} -# prefix each file with its filename for filename in "${SRC}"/*; do filename=$(basename -- "$filename") prefix="${filename%.*}" + + # remove value key if exists + if [[ $(yq e 'has("value")' "${SRC}"/"${filename}") == "true" ]]; then + yq e '.value' -i "${SRC}"/"${filename}" + fi + + # prefix each file with its filename yq e -i "{\"${prefix}\": . }" "${SRC}"/"${filename}" done diff --git a/internal/installation/capact_register.go b/internal/installation/capact_register.go index 41f4dfebc..132ee63b8 100644 --- a/internal/installation/capact_register.go +++ b/internal/installation/capact_register.go @@ -177,11 +177,14 @@ func (i *CapactRegister) produceHelmReleaseTypeInstance(helmRelease *release.Rel return nil, errors.Wrap(err, "while producing Helm release definition") } - var unmarshalled interface{} + var unmarshalled map[string]interface{} err = yaml.Unmarshal(releaseOut, &unmarshalled) if err != nil { return nil, errors.Wrap(err, "while unmarshaling bytes") } + if _, ok := unmarshalled["value"]; !ok { + return nil, errors.Wrap(err, "while getting value from unmarshalled additional output") + } return &gqllocalapi.CreateTypeInstanceInput{ Alias: ptr.String(helmRelease.Name), @@ -189,7 +192,7 @@ func (i *CapactRegister) produceHelmReleaseTypeInstance(helmRelease *release.Rel Path: helmReleaseTypeRefPath, Revision: "0.1.0", }, - Value: unmarshalled, + Value: unmarshalled["value"], }, nil } @@ -207,17 +210,22 @@ func (i *CapactRegister) produceConfigTypeInstance(ownerName string, helmRelease return nil, errors.Wrap(err, "while producing additional info") } - var unmarshalled interface{} + var unmarshalled map[string]interface{} err = yaml.Unmarshal(data, &unmarshalled) if err != nil { return nil, errors.Wrap(err, "while unmarshaling bytes") } + + if _, ok := unmarshalled["value"]; !ok { + return nil, errors.Wrap(err, "while getting value from unmarshalled additional output") + } + return &gqllocalapi.CreateTypeInstanceInput{ Alias: ptr.String(ownerName), TypeRef: &gqllocalapi.TypeInstanceTypeReferenceInput{ Path: capactTypeRefPath, Revision: "0.1.0", }, - Value: unmarshalled, + Value: unmarshalled["value"], }, nil } diff --git a/pkg/argo-actions/errors.go b/pkg/argo-actions/errors.go index c878848af..fa4a03f07 100644 --- a/pkg/argo-actions/errors.go +++ b/pkg/argo-actions/errors.go @@ -11,3 +11,8 @@ func ErrMissingTypeInstanceValue(typeInstanceName string) error { func ErrMissingResourceVersion() error { return errors.Errorf("resourceVersion is missing") } + +// ErrTypeConversion returns an error indicating incorrect type conversion. +func ErrTypeConversion(field string, targetType string, typeInstanceName string) error { + return errors.Errorf("%s cannot be converted to %s for TypeInstances %s", field, targetType, typeInstanceName) +} diff --git a/pkg/argo-actions/upload_type_instances.go b/pkg/argo-actions/upload_type_instances.go index a65c31fda..be53963bd 100644 --- a/pkg/argo-actions/upload_type_instances.go +++ b/pkg/argo-actions/upload_type_instances.go @@ -119,7 +119,32 @@ func (u *Upload) render(payload *graphqllocal.CreateTypeInstancesInput, values m return ErrMissingTypeInstanceValue(*typeInstance.Alias) } - typeInstance.Value = value + // render value + if _, ok = value["value"]; ok { + typeInstanceValue, ok := value["value"].(map[string]interface{}) + if !ok { + return ErrTypeConversion("value", "map[string]interface{}", *typeInstance.Alias) + } + typeInstance.Value = typeInstanceValue + } else { + // for backward compatibility, if there is an artifact without value/backend syntax, + // treat it as a value for TypeInstance + typeInstance.Value = value + continue + } + + // render backend + if _, ok := value["backend"]; ok { + typeInstanceBackend, ok := value["backend"].(map[string]interface{}) + if !ok { + return ErrTypeConversion("backend", "map[string]interface{}", *typeInstance.Alias) + } + backendContext, ok := typeInstanceBackend["context"].(map[string]interface{}) + if !ok { + return ErrTypeConversion("backend.context", "map[string]interface{}", *typeInstance.Alias) + } + typeInstance.Backend.Context = backendContext + } } return nil } diff --git a/pkg/runner/common.go b/pkg/runner/common.go index bed808ba7..562556f81 100644 --- a/pkg/runner/common.go +++ b/pkg/runner/common.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "github.com/pkg/errors" + "gopkg.in/yaml.v3" ) // DefaultFilePermissions are the default file permissions @@ -18,3 +19,21 @@ func SaveToFile(path string, bytes []byte) error { } return nil } + +// NestingOutputUnderValue write output to "value" key in YAML. +func NestingOutputUnderValue(output []byte) ([]byte, error) { + unmarshalled := map[string]interface{}{} + err := yaml.Unmarshal([]byte(output), &unmarshalled) + if err != nil { + return nil, errors.Wrap(err, "while unmarshalling output to map[string]interface{}") + } + + nestingOutputValue := map[string]interface{}{ + "value": unmarshalled, + } + result, err := yaml.Marshal(&nestingOutputValue) + if err != nil { + return nil, errors.Wrap(err, "while marshaling output under a value key") + } + return result, nil +} diff --git a/pkg/runner/helm/output.go b/pkg/runner/helm/output.go index 7dd170397..9626ce96d 100644 --- a/pkg/runner/helm/output.go +++ b/pkg/runner/helm/output.go @@ -3,6 +3,7 @@ package helm import ( "strings" + "capact.io/capact/pkg/runner" "github.com/pkg/errors" "go.uber.org/zap" "helm.sh/helm/v3/pkg/chart" @@ -39,7 +40,11 @@ func (o *Outputter) ProduceHelmRelease(repository string, helmRelease *release.R }, } - bytes, err := yaml.Marshal(&releaseData) + nestingValue := map[string]interface{}{ + "value": releaseData, + } + + bytes, err := yaml.Marshal(&nestingValue) if err != nil { return nil, errors.Wrap(err, "while marshaling yaml") } @@ -60,5 +65,10 @@ func (o *Outputter) ProduceAdditional(args OutputArgs, chrt *chart.Chart, rel *r return nil, errors.Wrap(err, "while rendering additional output") } + bytes, err = runner.NestingOutputUnderValue(bytes) + if err != nil { + return nil, errors.Wrap(err, "while nesting output under a value key") + } + return bytes, nil } diff --git a/pkg/runner/terraform/runner.go b/pkg/runner/terraform/runner.go index 11af36b36..af422c5c4 100644 --- a/pkg/runner/terraform/runner.go +++ b/pkg/runner/terraform/runner.go @@ -223,7 +223,12 @@ func (r *terraformRunner) mergeInputVariables(variables string) error { func (r *terraformRunner) saveOutput(out Output) error { if out.Release != nil { r.log.Debug("Saving terraform release output", zap.String("path", r.cfg.Output.TerraformReleaseFilePath)) - err := runner.SaveToFile(r.cfg.Output.TerraformReleaseFilePath, out.Release) + bytesRelease, err := runner.NestingOutputUnderValue(out.Release) + if err != nil { + return errors.Wrap(err, "while nesting Terrafrom release under value") + } + + err = runner.SaveToFile(r.cfg.Output.TerraformReleaseFilePath, bytesRelease) if err != nil { return errors.Wrap(err, "while saving terraform release output") } @@ -231,7 +236,12 @@ func (r *terraformRunner) saveOutput(out Output) error { if out.Additional != nil { r.log.Debug("Saving additional output", zap.String("path", r.cfg.Output.AdditionalFilePath)) - err := runner.SaveToFile(r.cfg.Output.AdditionalFilePath, out.Additional) + bytesAdditional, err := runner.NestingOutputUnderValue(out.Additional) + if err != nil { + return errors.Wrap(err, "while nesting Terrafrom additional under value") + } + + err = runner.SaveToFile(r.cfg.Output.AdditionalFilePath, bytesAdditional) if err != nil { return errors.Wrap(err, "while saving default output") } @@ -243,7 +253,12 @@ func (r *terraformRunner) saveOutput(out Output) error { return errors.Wrap(err, "while marshaling state") } - err = runner.SaveToFile(r.cfg.Output.TfstateFilePath, stateData) + nestingStateData, err := runner.NestingOutputUnderValue(stateData) + if err != nil { + return errors.Wrap(err, "while nesting Terrafrom state data under value") + } + + err = runner.SaveToFile(r.cfg.Output.TfstateFilePath, nestingStateData) if err != nil { return errors.Wrap(err, "while saving tfstate output") } diff --git a/test/e2e/action_test.go b/test/e2e/action_test.go index ad8ebaf66..3308f1654 100644 --- a/test/e2e/action_test.go +++ b/test/e2e/action_test.go @@ -72,7 +72,7 @@ var _ = Describe("Action", func() { By("1. Preparing input Type Instances") By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(implIndicatorValue) + download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() @@ -148,7 +148,7 @@ var _ = Describe("Action", func() { // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 By("1. Preparing input Type Instances") By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(implIndicatorValue) + download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() @@ -210,7 +210,7 @@ var _ = Describe("Action", func() { // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 By("1. Preparing input Type Instances") By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(implIndicatorValue) + download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() @@ -265,6 +265,138 @@ var _ = Describe("Action", func() { assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(uploadedTIOutput), HaveLen(1))) }) + It("should propagate context provider to storage backend", func() { + implIndicatorValue := "Implementation C" + + // TODO: This can be extracted after switching to ginkgo v2 + // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 + By("1. Preparing input Type Instances") + By("1.1 Creating TypeInstance which will be downloaded") + download := getTypeInstanceInputForDownload(map[string]interface{}{ + "value": map[string]interface{}{ + "key": implIndicatorValue, + }, + "backend": map[string]interface{}{ + "context": map[string]interface{}{ + "provider": "dotenv", + }, + }, + }) + downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) + defer downloadTICleanup() + + By("1.2 Creating TypeInstance which will be downloaded and updated") + update := getTypeInstanceInputForUpdate() + updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) + defer updateTICleanup() + + By("1.3 Creating TypeInstance that describes Validation storage") + validationStorage := fixValidationStorageTypeInstanceCreateInput("secret-storage-backend") + validationStorageTI, validationStorageTICleanup := createTypeInstance(ctx, hubClient, validationStorage) + defer validationStorageTICleanup() + + By("1.4 Create TypeInstance which is required for Implementation C to be picked based on Policy") + typeInstanceValue := getTypeInstanceInputForPolicy() + injectTypeInstance, tiCleanupFn := createTypeInstance(ctx, hubClient, typeInstanceValue) + defer tiCleanupFn() + + inputData := &enginegraphql.ActionInputData{ + TypeInstances: []*enginegraphql.InputTypeInstanceData{ + {Name: "testInput", ID: downloadTI.ID}, + {Name: "testUpdate", ID: updateTI.ID}, + }, + } + + By("2. Modifying default Policy to pick Implementation C...") + globalPolicyRequiredTypeInstances := []*enginegraphql.RequiredTypeInstanceReferenceInput{ + { + ID: injectTypeInstance.ID, + Description: ptr.String("Test TypeInstance"), + }, + { + ID: validationStorageTI.ID, + Description: ptr.String("Validation storage backend TypeInstance"), + }, + } + setGlobalTestPolicy(ctx, engineClient, addInterfacePolicyDefaultInjectionForPassingActionInterface(globalPolicyRequiredTypeInstances)) + + By("3. Expecting Implementation C is picked and injected Validation storage is used...") + action := createActionAndWaitForReadyToRunPhase(ctx, engineClient, actionName, actionPassingInterfacePath, inputData) + assertActionRenderedWorkflowContains(action, "echo '%s'", implIndicatorValue) + runActionAndWaitForSucceeded(ctx, engineClient, actionName) + + By("4.1 Check uploaded TypeInstances") + expUploadTIBackend := &hublocalgraphql.TypeInstanceBackendReference{ID: validationStorageTI.ID, Abstract: false} + fmt.Println("expUploadTIBackend", expUploadTIBackend.ID) + uploadedTI, cleanupUploaded := getUploadedTypeInstanceByValue(ctx, hubClient, implIndicatorValue) + defer cleanupUploaded() + Expect(uploadedTI.Backend).Should(Equal(expUploadTIBackend)) + + By("4.2 Check Action output TypeInstances") + uploadedTIOutput := mapToOutputTypeInstanceDetails(uploadedTI, expUploadTIBackend) + assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(uploadedTIOutput), HaveLen(1))) + }) + + It("should fail due to incorrect storage provider", func() { + implIndicatorValue := "Implementation C" + + // TODO: This can be extracted after switching to ginkgo v2 + // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 + By("1. Preparing input Type Instances") + By("1.1 Creating TypeInstance which will be downloaded") + download := getTypeInstanceInputForDownload(map[string]interface{}{ + "value": map[string]interface{}{ + "key": implIndicatorValue, + }, + "backend": map[string]interface{}{ + "context": map[string]interface{}{ + "provider": "incorrect", + }, + }, + }) + downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) + defer downloadTICleanup() + + By("1.2 Creating TypeInstance which will be downloaded and updated") + update := getTypeInstanceInputForUpdate() + updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) + defer updateTICleanup() + + By("1.3 Creating TypeInstance that describes Validation storage") + validationStorage := fixValidationStorageTypeInstanceCreateInput("secret-storage-backend") + validationStorageTI, validationStorageTICleanup := createTypeInstance(ctx, hubClient, validationStorage) + defer validationStorageTICleanup() + + By("1.4 Create TypeInstance which is required for Implementation C to be picked based on Policy") + typeInstanceValue := getTypeInstanceInputForPolicy() + injectTypeInstance, tiCleanupFn := createTypeInstance(ctx, hubClient, typeInstanceValue) + defer tiCleanupFn() + + inputData := &enginegraphql.ActionInputData{ + TypeInstances: []*enginegraphql.InputTypeInstanceData{ + {Name: "testInput", ID: downloadTI.ID}, + {Name: "testUpdate", ID: updateTI.ID}, + }, + } + + By("2. Modifying default Policy to pick Implementation C...") + globalPolicyRequiredTypeInstances := []*enginegraphql.RequiredTypeInstanceReferenceInput{ + { + ID: injectTypeInstance.ID, + Description: ptr.String("Test TypeInstance"), + }, + { + ID: validationStorageTI.ID, + Description: ptr.String("Validation storage backend TypeInstance"), + }, + } + setGlobalTestPolicy(ctx, engineClient, addInterfacePolicyDefaultInjectionForPassingActionInterface(globalPolicyRequiredTypeInstances)) + + By("3. Create action and wait for a status phase failed") + createActionAndWaitForReadyToRunPhase(ctx, engineClient, actionName, actionPassingInterfacePath, inputData) + runActionAndWaitForStatus(ctx, engineClient, actionName, enginegraphql.ActionStatusPhaseFailed) + }) + It("should have failed status after a failed workflow", func() { _, err := engineClient.CreateAction(ctx, &enginegraphql.ActionDetailsInput{ Name: failingActionName, @@ -424,13 +556,13 @@ func getTypeInstanceInputForPolicy() *hublocalgraphql.CreateTypeInstanceInput { } } -func getTypeInstanceInputForDownload(testValue string) *hublocalgraphql.CreateTypeInstanceInput { +func getTypeInstanceInputForDownload(testValues map[string]interface{}) *hublocalgraphql.CreateTypeInstanceInput { return &hublocalgraphql.CreateTypeInstanceInput{ TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ Path: "cap.type.capactio.capact.validation.download", Revision: "0.1.0", }, - Value: map[string]interface{}{"key": testValue}, + Value: testValues, Attributes: []*hublocalgraphql.AttributeReferenceInput{ { Path: "cap.attribute.capactio.capact.attribute1", @@ -456,6 +588,39 @@ func getTypeInstanceInputForUpdate() *hublocalgraphql.CreateTypeInstanceInput { } } +func fixValidationStorageTypeInstanceCreateInput(deploymentName string) *hublocalgraphql.CreateTypeInstanceInput { + return &hublocalgraphql.CreateTypeInstanceInput{ + TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ + Path: "cap.type.capactio.capact.validation.storage", + Revision: "0.1.0", + }, + Attributes: []*hublocalgraphql.AttributeReferenceInput{}, + Value: map[string]interface{}{ + "url": deploymentName + ".capact-system:50051", + "acceptValue": true, + "contextSchema": heredoc.Doc(` + { + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "required": [ + "provider" + ], + "properties": { + "provider": { + "$id": "#/properties/contextSchema/properties/name", + "type": "string" + "enum": [ + "aws_secretsmanager", + "dotenv" + ] + } + }, + "additionalProperties": false + }`), + }, + } +} + func fixHelmStorageTypeInstanceCreateInput() *hublocalgraphql.CreateTypeInstanceInput { return &hublocalgraphql.CreateTypeInstanceInput{ TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ diff --git a/test/e2e/cluster_test.go b/test/e2e/cluster_test.go index 916862b5a..e65cd2113 100644 --- a/test/e2e/cluster_test.go +++ b/test/e2e/cluster_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package e2e From d87db66968e8940277bf645dc33274cafb37ad18 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Thu, 10 Mar 2022 23:27:01 +0100 Subject: [PATCH 2/9] Fix tests after merge --- hack/lib/const.sh | 4 +-- test/e2e/action_test.go | 68 ++++++++++++----------------------------- 2 files changed, 22 insertions(+), 50 deletions(-) diff --git a/hack/lib/const.sh b/hack/lib/const.sh index 4baeef414..1b8cc329a 100644 --- a/hack/lib/const.sh +++ b/hack/lib/const.sh @@ -50,7 +50,7 @@ readonly CAPACT_USE_TEST_SETUP="false" # readonly CAPACT_INCREASE_RESOURCE_LIMITS="true" -readonly CAPACT_HUB_MANIFESTS_SOURCE_REPO_URL="github.com/capactio/hub-manifests" +readonly CAPACT_HUB_MANIFESTS_SOURCE_REPO_URL="github.com/mkuziemko/hub-manifests" # The git ref to checkout. It can point to a commit SHA, a branch name, or a tag. # If you want to use your forked version, remember to update CAPACT_HUB_MANIFESTS_SOURCE_REPO_URL respectively. -readonly CAPACT_HUB_MANIFESTS_SOURCE_REPO_REF="main" +readonly CAPACT_HUB_MANIFESTS_SOURCE_REPO_REF="context_handling" diff --git a/test/e2e/action_test.go b/test/e2e/action_test.go index 3308f1654..32a6b5528 100644 --- a/test/e2e/action_test.go +++ b/test/e2e/action_test.go @@ -32,6 +32,7 @@ const ( actionPassingInterfacePath = "cap.interface.capactio.capact.validation.action.passing" uploadTypePath = "cap.type.capactio.capact.validation.upload" singleKeyTypePath = "cap.type.capactio.capact.validation.single-key" + testStorageBackendPath = "cap.type.capactio.capact.validation.storage" ) func getActionName() string { @@ -267,6 +268,7 @@ var _ = Describe("Action", func() { It("should propagate context provider to storage backend", func() { implIndicatorValue := "Implementation C" + testStorageBackendTI := getDefaultTestStorageTypeInstance(ctx, hubClient) // TODO: This can be extracted after switching to ginkgo v2 // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 @@ -290,12 +292,7 @@ var _ = Describe("Action", func() { updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() - By("1.3 Creating TypeInstance that describes Validation storage") - validationStorage := fixValidationStorageTypeInstanceCreateInput("secret-storage-backend") - validationStorageTI, validationStorageTICleanup := createTypeInstance(ctx, hubClient, validationStorage) - defer validationStorageTICleanup() - - By("1.4 Create TypeInstance which is required for Implementation C to be picked based on Policy") + By("1.3 Create TypeInstance which is required for Implementation C to be picked based on Policy") typeInstanceValue := getTypeInstanceInputForPolicy() injectTypeInstance, tiCleanupFn := createTypeInstance(ctx, hubClient, typeInstanceValue) defer tiCleanupFn() @@ -314,7 +311,7 @@ var _ = Describe("Action", func() { Description: ptr.String("Test TypeInstance"), }, { - ID: validationStorageTI.ID, + ID: testStorageBackendTI.ID, Description: ptr.String("Validation storage backend TypeInstance"), }, } @@ -326,7 +323,7 @@ var _ = Describe("Action", func() { runActionAndWaitForSucceeded(ctx, engineClient, actionName) By("4.1 Check uploaded TypeInstances") - expUploadTIBackend := &hublocalgraphql.TypeInstanceBackendReference{ID: validationStorageTI.ID, Abstract: false} + expUploadTIBackend := &hublocalgraphql.TypeInstanceBackendReference{ID: testStorageBackendTI.ID, Abstract: false} fmt.Println("expUploadTIBackend", expUploadTIBackend.ID) uploadedTI, cleanupUploaded := getUploadedTypeInstanceByValue(ctx, hubClient, implIndicatorValue) defer cleanupUploaded() @@ -339,6 +336,7 @@ var _ = Describe("Action", func() { It("should fail due to incorrect storage provider", func() { implIndicatorValue := "Implementation C" + testStorageBackendTI := getDefaultTestStorageTypeInstance(ctx, hubClient) // TODO: This can be extracted after switching to ginkgo v2 // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 @@ -362,12 +360,7 @@ var _ = Describe("Action", func() { updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() - By("1.3 Creating TypeInstance that describes Validation storage") - validationStorage := fixValidationStorageTypeInstanceCreateInput("secret-storage-backend") - validationStorageTI, validationStorageTICleanup := createTypeInstance(ctx, hubClient, validationStorage) - defer validationStorageTICleanup() - - By("1.4 Create TypeInstance which is required for Implementation C to be picked based on Policy") + By("1.3 Create TypeInstance which is required for Implementation C to be picked based on Policy") typeInstanceValue := getTypeInstanceInputForPolicy() injectTypeInstance, tiCleanupFn := createTypeInstance(ctx, hubClient, typeInstanceValue) defer tiCleanupFn() @@ -386,7 +379,7 @@ var _ = Describe("Action", func() { Description: ptr.String("Test TypeInstance"), }, { - ID: validationStorageTI.ID, + ID: testStorageBackendTI.ID, Description: ptr.String("Validation storage backend TypeInstance"), }, } @@ -588,39 +581,6 @@ func getTypeInstanceInputForUpdate() *hublocalgraphql.CreateTypeInstanceInput { } } -func fixValidationStorageTypeInstanceCreateInput(deploymentName string) *hublocalgraphql.CreateTypeInstanceInput { - return &hublocalgraphql.CreateTypeInstanceInput{ - TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ - Path: "cap.type.capactio.capact.validation.storage", - Revision: "0.1.0", - }, - Attributes: []*hublocalgraphql.AttributeReferenceInput{}, - Value: map[string]interface{}{ - "url": deploymentName + ".capact-system:50051", - "acceptValue": true, - "contextSchema": heredoc.Doc(` - { - "$schema": "http://json-schema.org/draft-07/schema", - "type": "object", - "required": [ - "provider" - ], - "properties": { - "provider": { - "$id": "#/properties/contextSchema/properties/name", - "type": "string" - "enum": [ - "aws_secretsmanager", - "dotenv" - ] - } - }, - "additionalProperties": false - }`), - }, - } -} - func fixHelmStorageTypeInstanceCreateInput() *hublocalgraphql.CreateTypeInstanceInput { return &hublocalgraphql.CreateTypeInstanceInput{ TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ @@ -827,6 +787,18 @@ func getTypeInstanceByIDAndValue(ctx context.Context, hubClient *hubclient.Clien return updateTI } +func getDefaultTestStorageTypeInstance(ctx context.Context, hubClient *hubclient.Client) *hublocalgraphql.TypeInstance { + storage, err := hubClient.ListTypeInstances(ctx, &hublocalgraphql.TypeInstanceFilter{ + TypeRef: &hublocalgraphql.TypeRefFilterInput{ + Path: testStorageBackendPath, + Revision: ptr.String("0.1.0"), + }, + }) + Expect(err).ToNot(HaveOccurred()) + Expect(len(storage)).Should(Equal(1)) + return &storage[0] +} + func getUploadedTypeInstanceByValue(ctx context.Context, hubClient *hubclient.Client, expValue string) (*hublocalgraphql.TypeInstance, func()) { uploaded, err := hubClient.ListTypeInstances(ctx, &hublocalgraphql.TypeInstanceFilter{ TypeRef: &hublocalgraphql.TypeRefFilterInput{ From b5eb0f2aae2b9f6c746b35aa1c3f3107bf9bfec9 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Tue, 15 Mar 2022 01:03:51 +0100 Subject: [PATCH 3/9] Fixes after review --- hack/images/jinja2/README.md | 8 ++ .../images/jinja2/jinja2-cli/jinja2cli/cli.py | 17 ++- .../jinja2/jinja2-cli/tests/test_jinja2cli.py | 59 +++++++--- hack/images/merger/README.md | 3 +- internal/installation/capact_register.go | 15 +-- pkg/argo-actions/download_type_instances.go | 24 +++- pkg/argo-actions/errors.go | 5 - pkg/argo-actions/update_type_instances.go | 24 +++- pkg/argo-actions/upload_type_instances.go | 41 ++++--- pkg/runner/helm/helm.go | 12 +- pkg/runner/helm/output.go | 12 +- test/e2e/action_test.go | 103 +++++------------- 12 files changed, 176 insertions(+), 147 deletions(-) diff --git a/hack/images/jinja2/README.md b/hack/images/jinja2/README.md index c08c0a4f3..4b2ece47e 100644 --- a/hack/images/jinja2/README.md +++ b/hack/images/jinja2/README.md @@ -59,6 +59,14 @@ postgres Prefix for db was removed. This is Jinja limitation. It shouldn't be a big problem as long as there is no need to render the template twice with the same prefix. +### Configuration + +There is a possibility of pre-processing data by setting options in the configuration file. +List of supported operations: +| Name | Default | Description | +| ------------------------- | ------------ | ---------------------------------------------------------------------------------------------------| +| prefix | "" | Adds a prefix to inputted data. The data will be accessible using the set prefix. | +| unpackValue | False | If the `value` prefix is set in the inputted data then the data will be unpacked from that prefix. | ## Prerequisites diff --git a/hack/images/jinja2/jinja2-cli/jinja2cli/cli.py b/hack/images/jinja2/jinja2-cli/jinja2cli/cli.py index 3f134f36d..56bf36bc3 100644 --- a/hack/images/jinja2/jinja2-cli/jinja2cli/cli.py +++ b/hack/images/jinja2/jinja2-cli/jinja2cli/cli.py @@ -312,17 +312,13 @@ def cli(opts, args, config): # noqa: C901 out = codecs.getwriter("utf8")(out) - if config.get("prefix") is not None and len(parsed_data) != 0: - parsed_data = {config["prefix"]: parsed_data} - if config.get("strip-value") is True and len(parsed_data) != 0 and "value" in parsed_data: - parsed_data = parsed_data.get("value", {}) + parsed_data = preprocessing_data(config, parsed_data) template_path = os.path.abspath(template_path) out.write(render(template_path, parsed_data, extensions, opts.filters, opts.strict)) out.flush() return 0 - def parse_kv_string(pairs): dict_ = {} for pair in pairs: @@ -331,6 +327,17 @@ def parse_kv_string(pairs): return dict_ +def preprocessing_data(config, data): + '''Return preprocessed data based on the applied configuration.''' + + if config.get("unpackValue") is True and len(data) != 0 and "value" in data: + data = data.get("value", {}) + + if config.get("prefix") is not None and len(data) != 0: + data = {config["prefix"]: data} + + return data + class LazyHelpOption(Option): "An Option class that resolves help from a callable" diff --git a/hack/images/jinja2/jinja2-cli/tests/test_jinja2cli.py b/hack/images/jinja2/jinja2-cli/tests/test_jinja2cli.py index f2534176d..043f3bb9c 100644 --- a/hack/images/jinja2/jinja2-cli/tests/test_jinja2cli.py +++ b/hack/images/jinja2/jinja2-cli/tests/test_jinja2cli.py @@ -12,76 +12,82 @@ @dataclass -class TestCase: +class RenderTestCase: name: str template: str data: typing.Dict[str, typing.Any] result: str +@dataclass +class PreprocessingDataTestCase: + name: str + config: typing.Dict[str, typing.Any] + data: typing.Dict[str, typing.Any] + result: typing.Dict[str, typing.Any] render_testcases = [ - TestCase(name="empty", template="", data={}, result=""), - TestCase( + RenderTestCase(name="empty", template="", data={}, result=""), + RenderTestCase( name="simple", template="<@ title @>", data={"title": b"\xc3\xb8".decode("utf8")}, result=b"\xc3\xb8".decode("utf8"), ), - TestCase( + RenderTestCase( name="prefix", template="<@ input.key @>", data={"input": {"key": "value"}}, result="value", ), - TestCase( + RenderTestCase( name="two prefixes but one provided", template="<@ input.key @>/<@ additionalinput.key @>", data={"input": {"key": "value"}}, result="value/<@ additionalinput.key @>", ), - TestCase( + RenderTestCase( name="missing prefix", template="<@ input.key @>", data={}, result="<@ input.key @>", ), - TestCase( + RenderTestCase( name="items before attrs", template="<@ input.values.key @>", data={"input": {"values": {"key": "value"}}}, result="value", ), - TestCase( + RenderTestCase( name="attrs still working", template="<@ input.values() @>", data={"input": {}}, result="dict_values([])", ), - TestCase( + RenderTestCase( name="key with dot", template="<@ input['foo.bar'] @>", data={"input": {"foo.bar": "value"}}, result="value", ), - TestCase( + RenderTestCase( name="missing key with dot", template='<@ input["foo.bar"] @>', data={}, result='<@ input["foo.bar"] @>', ), - TestCase( + RenderTestCase( name="use default value", template='<@ input["foo.bar"] | default("hello") @>', data={}, result="hello", ), - TestCase( + RenderTestCase( name="multiple dotted values", template='<@ input.key.key["foo.bar/baz"] | default("hello") @>', data={}, result="hello", ), - TestCase( + RenderTestCase( name="multiline strings", template="""<@ input.key.key["foo.bar/baz"] | default('hello hello') @>""", @@ -100,6 +106,33 @@ def test_render(tmp_path, case): assert output == case.result +preprocessing_data_testcases = [ + PreprocessingDataTestCase( + name="set prefix in the config should prefix the data", + config={"prefix": "testprefix"}, + data = {"test": "test"}, + result={"testprefix": {"test": "test"}} + ), + PreprocessingDataTestCase( + name="set unpackValue in the config should remove the value prefix", + config={"unpackValue": True}, + data = {"value": {"test": "test"}}, + result={"test": "test"} + ), + PreprocessingDataTestCase( + name="set unpackValue and prefix should output correct results", + config={"prefix": "testprefix", "unpackValue": True}, + data = {"value": {"test": "test"}}, + result={"testprefix": {"test": "test"}} + ) +] + +@pytest.mark.parametrize("case", preprocessing_data_testcases) +def test_preprocessing_data(case): + output = cli.preprocessing_data(case.config,case.data) + assert output == case.result + + def test_random_password(tmp_path): random_pass_path = tmp_path / "random.template" random_pass_path.write_text("<@ random_password(length=4) @>") diff --git a/hack/images/merger/README.md b/hack/images/merger/README.md index b00dcb834..de71eee8e 100755 --- a/hack/images/merger/README.md +++ b/hack/images/merger/README.md @@ -4,8 +4,7 @@ This folder contains the Docker image which merges multiple input YAML files into a single one. -The Docker image contains the `merger.sh` helper script. The script is an entrypoint of the image, and it is used to prefix and merge all YAML files found in `$SRC` directory. -Each file is prefixed with a file name without extension. +The Docker image contains the `merger.sh` helper script. The script is an entrypoint of the image, and it is used to prefix and merge all YAML files found in `$SRC` directory. Additionally, if the YAML file contains the `value` key, then it is unpacked from that key. Each file is prefixed with a file name without extension. ## Installation diff --git a/internal/installation/capact_register.go b/internal/installation/capact_register.go index 132ee63b8..2bd95b06a 100644 --- a/internal/installation/capact_register.go +++ b/internal/installation/capact_register.go @@ -177,14 +177,11 @@ func (i *CapactRegister) produceHelmReleaseTypeInstance(helmRelease *release.Rel return nil, errors.Wrap(err, "while producing Helm release definition") } - var unmarshalled map[string]interface{} + var unmarshalled interface{} err = yaml.Unmarshal(releaseOut, &unmarshalled) if err != nil { return nil, errors.Wrap(err, "while unmarshaling bytes") } - if _, ok := unmarshalled["value"]; !ok { - return nil, errors.Wrap(err, "while getting value from unmarshalled additional output") - } return &gqllocalapi.CreateTypeInstanceInput{ Alias: ptr.String(helmRelease.Name), @@ -192,7 +189,7 @@ func (i *CapactRegister) produceHelmReleaseTypeInstance(helmRelease *release.Rel Path: helmReleaseTypeRefPath, Revision: "0.1.0", }, - Value: unmarshalled["value"], + Value: unmarshalled, }, nil } @@ -210,22 +207,18 @@ func (i *CapactRegister) produceConfigTypeInstance(ownerName string, helmRelease return nil, errors.Wrap(err, "while producing additional info") } - var unmarshalled map[string]interface{} + var unmarshalled interface{} err = yaml.Unmarshal(data, &unmarshalled) if err != nil { return nil, errors.Wrap(err, "while unmarshaling bytes") } - if _, ok := unmarshalled["value"]; !ok { - return nil, errors.Wrap(err, "while getting value from unmarshalled additional output") - } - return &gqllocalapi.CreateTypeInstanceInput{ Alias: ptr.String(ownerName), TypeRef: &gqllocalapi.TypeInstanceTypeReferenceInput{ Path: capactTypeRefPath, Revision: "0.1.0", }, - Value: unmarshalled["value"], + Value: unmarshalled, }, nil } diff --git a/pkg/argo-actions/download_type_instances.go b/pkg/argo-actions/download_type_instances.go index 7ef5c0c7b..de0e3b5b6 100644 --- a/pkg/argo-actions/download_type_instances.go +++ b/pkg/argo-actions/download_type_instances.go @@ -28,6 +28,17 @@ type Download struct { client *hubclient.Client } +// DownloadTypeInstanceData represents the TypeInstance data to download. +type DownloadTypeInstanceData struct { + Value interface{} `json:"value"` + Backend *Backend `json:"backend,omitempty"` +} + +// Backend represents the TypeInstance Backend. +type Backend struct { + Context interface{} `json:"context,omitempty"` +} + // NewDownloadAction returns a new Download instance. func NewDownloadAction(log *zap.Logger, client *hubclient.Client, cfg []DownloadConfig) Action { return &Download{ @@ -49,7 +60,18 @@ func (d *Download) Do(ctx context.Context) error { return fmt.Errorf("failed to find TypeInstance with ID %q", config.ID) } - data, err := yaml.Marshal(typeInstance.LatestResourceVersion.Spec.Value) + typeInstanceData := DownloadTypeInstanceData{ + Value: typeInstance.LatestResourceVersion.Spec.Value, + } + + if typeInstance.LatestResourceVersion.Spec.Backend != nil && + typeInstance.LatestResourceVersion.Spec.Backend.Context != nil { + typeInstanceData.Backend = &Backend{ + Context: typeInstance.LatestResourceVersion.Spec.Backend.Context, + } + } + + data, err := yaml.Marshal(typeInstanceData) if err != nil { return errors.Wrap(err, "while marshaling TypeInstance to YAML") } diff --git a/pkg/argo-actions/errors.go b/pkg/argo-actions/errors.go index fa4a03f07..c878848af 100644 --- a/pkg/argo-actions/errors.go +++ b/pkg/argo-actions/errors.go @@ -11,8 +11,3 @@ func ErrMissingTypeInstanceValue(typeInstanceName string) error { func ErrMissingResourceVersion() error { return errors.Errorf("resourceVersion is missing") } - -// ErrTypeConversion returns an error indicating incorrect type conversion. -func ErrTypeConversion(field string, targetType string, typeInstanceName string) error { - return errors.Errorf("%s cannot be converted to %s for TypeInstances %s", field, targetType, typeInstanceName) -} diff --git a/pkg/argo-actions/update_type_instances.go b/pkg/argo-actions/update_type_instances.go index 522796593..92c81b3fe 100644 --- a/pkg/argo-actions/update_type_instances.go +++ b/pkg/argo-actions/update_type_instances.go @@ -2,6 +2,7 @@ package argoactions import ( "context" + "encoding/json" "io/ioutil" "path" "path/filepath" @@ -116,7 +117,28 @@ func (u *Update) render(payload []graphqllocal.UpdateTypeInstancesInput, values return ErrMissingTypeInstanceValue(typeInstance.ID) } - typeInstance.TypeInstance.Value = value + if _, ok = value["value"]; !ok { + // for backward compatibility, if there is an artifact without value/backend syntax, + // treat it as a value for TypeInstance + typeInstance.TypeInstance.Value = value + continue + } + + data, err := json.Marshal(value) + if err != nil { + return errors.Wrap(err, "while marshaling TypeInstance") + } + + unmarshalledTI := graphqllocal.UpdateTypeInstanceInput{} + err = json.Unmarshal(data, &unmarshalledTI) + if err != nil { + return errors.Wrap(err, "while unmarshaling TypeInstance") + } + + typeInstance.TypeInstance.Value = unmarshalledTI.Value + if unmarshalledTI.Backend != nil { + typeInstance.TypeInstance.Backend = unmarshalledTI.Backend + } } return nil } diff --git a/pkg/argo-actions/upload_type_instances.go b/pkg/argo-actions/upload_type_instances.go index be53963bd..a2fb23783 100644 --- a/pkg/argo-actions/upload_type_instances.go +++ b/pkg/argo-actions/upload_type_instances.go @@ -2,6 +2,7 @@ package argoactions import ( "context" + "encoding/json" "fmt" "io/ioutil" "path/filepath" @@ -32,6 +33,12 @@ type Upload struct { cfg UploadConfig } +//UploadTypeInstanceData represents the TypeInstance data to upload. +type UploadTypeInstanceData struct { + Value interface{} `json:"value"` + Backend *Backend `json:"backend,omitempty"` +} + // NewUploadAction returns a new Upload instance. func NewUploadAction(log *zap.Logger, client *hubclient.Client, cfg UploadConfig) Action { return &Upload{ @@ -119,31 +126,29 @@ func (u *Upload) render(payload *graphqllocal.CreateTypeInstancesInput, values m return ErrMissingTypeInstanceValue(*typeInstance.Alias) } - // render value - if _, ok = value["value"]; ok { - typeInstanceValue, ok := value["value"].(map[string]interface{}) - if !ok { - return ErrTypeConversion("value", "map[string]interface{}", *typeInstance.Alias) - } - typeInstance.Value = typeInstanceValue - } else { + if _, ok = value["value"]; !ok { // for backward compatibility, if there is an artifact without value/backend syntax, // treat it as a value for TypeInstance typeInstance.Value = value continue } - // render backend - if _, ok := value["backend"]; ok { - typeInstanceBackend, ok := value["backend"].(map[string]interface{}) - if !ok { - return ErrTypeConversion("backend", "map[string]interface{}", *typeInstance.Alias) - } - backendContext, ok := typeInstanceBackend["context"].(map[string]interface{}) - if !ok { - return ErrTypeConversion("backend.context", "map[string]interface{}", *typeInstance.Alias) + data, err := json.Marshal(value) + if err != nil { + return errors.Wrap(err, "while marshaling TypeInstance") + } + + unmarshalledTI := UploadTypeInstanceData{} + err = json.Unmarshal(data, &unmarshalledTI) + if err != nil { + return errors.Wrap(err, "while unmarshaling TypeInstance") + } + + typeInstance.Value = unmarshalledTI.Value + if unmarshalledTI.Backend != nil { + typeInstance.Backend = &graphqllocal.TypeInstanceBackendInput{ + Context: unmarshalledTI.Backend.Context, } - typeInstance.Backend.Context = backendContext } } return nil diff --git a/pkg/runner/helm/helm.go b/pkg/runner/helm/helm.go index df21a7ec6..94c7aae5b 100644 --- a/pkg/runner/helm/helm.go +++ b/pkg/runner/helm/helm.go @@ -122,7 +122,11 @@ func (r *helmRunner) readCommandData(in runner.StartInput) (Input, error) { func (r *helmRunner) saveOutput(out Output) error { r.log.Debug("Saving Helm release output", zap.String("path", r.cfg.Output.HelmReleaseFilePath)) - err := runner.SaveToFile(r.cfg.Output.HelmReleaseFilePath, out.Release) + bytesRelease, err := runner.NestingOutputUnderValue(out.Release) + if err != nil { + return errors.Wrap(err, "while nesting Terrafrom release under value") + } + err = runner.SaveToFile(r.cfg.Output.HelmReleaseFilePath, bytesRelease) if err != nil { return errors.Wrap(err, "while saving Helm release output") } @@ -132,7 +136,11 @@ func (r *helmRunner) saveOutput(out Output) error { } r.log.Debug("Saving additional output", zap.String("path", r.cfg.Output.AdditionalFilePath)) - err = runner.SaveToFile(r.cfg.Output.AdditionalFilePath, out.Additional) + bytesAdditional, err := runner.NestingOutputUnderValue(out.Additional) + if err != nil { + return errors.Wrap(err, "while nesting Terrafrom additional under value") + } + err = runner.SaveToFile(r.cfg.Output.AdditionalFilePath, bytesAdditional) if err != nil { return errors.Wrap(err, "while saving default output") } diff --git a/pkg/runner/helm/output.go b/pkg/runner/helm/output.go index 9626ce96d..7dd170397 100644 --- a/pkg/runner/helm/output.go +++ b/pkg/runner/helm/output.go @@ -3,7 +3,6 @@ package helm import ( "strings" - "capact.io/capact/pkg/runner" "github.com/pkg/errors" "go.uber.org/zap" "helm.sh/helm/v3/pkg/chart" @@ -40,11 +39,7 @@ func (o *Outputter) ProduceHelmRelease(repository string, helmRelease *release.R }, } - nestingValue := map[string]interface{}{ - "value": releaseData, - } - - bytes, err := yaml.Marshal(&nestingValue) + bytes, err := yaml.Marshal(&releaseData) if err != nil { return nil, errors.Wrap(err, "while marshaling yaml") } @@ -65,10 +60,5 @@ func (o *Outputter) ProduceAdditional(args OutputArgs, chrt *chart.Chart, rel *r return nil, errors.Wrap(err, "while rendering additional output") } - bytes, err = runner.NestingOutputUnderValue(bytes) - if err != nil { - return nil, errors.Wrap(err, "while nesting output under a value key") - } - return bytes, nil } diff --git a/test/e2e/action_test.go b/test/e2e/action_test.go index 672d413ea..cdfee501a 100644 --- a/test/e2e/action_test.go +++ b/test/e2e/action_test.go @@ -73,12 +73,12 @@ var _ = Describe("Action", func() { By("1. Preparing input Type Instances") By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}) + download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, nil) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() By("1.2 Creating TypeInstance which will be downloaded and updated") - update := getTypeInstanceInputForUpdate() + update := getTypeInstanceInputForUpdate(nil) updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() @@ -154,12 +154,12 @@ var _ = Describe("Action", func() { // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 By("1. Preparing input Type Instances") By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}) + download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, nil) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() By("1.2 Creating TypeInstance which will be downloaded and updated") - update := getTypeInstanceInputForUpdate() + update := getTypeInstanceInputForUpdate(nil) updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() @@ -221,12 +221,12 @@ var _ = Describe("Action", func() { // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 By("1. Preparing input Type Instances") By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}) + download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, nil) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() By("1.2 Creating TypeInstance which will be downloaded and updated") - update := getTypeInstanceInputForUpdate() + update := getTypeInstanceInputForUpdate(nil) updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() @@ -289,21 +289,22 @@ var _ = Describe("Action", func() { // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 By("1. Preparing input Type Instances") By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(map[string]interface{}{ - "value": map[string]interface{}{ - "key": implIndicatorValue, + backendInput := hublocalgraphql.TypeInstanceBackendInput{ + ID: testStorageBackendTI.ID, + Context: map[string]interface{}{ + "provider": "dotenv", }, - "backend": map[string]interface{}{ - "context": map[string]interface{}{ - "provider": "dotenv", - }, + } + download := getTypeInstanceInputForDownload( + map[string]interface{}{ + "key": implIndicatorValue, }, - }) + &backendInput) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() By("1.2 Creating TypeInstance which will be downloaded and updated") - update := getTypeInstanceInputForUpdate() + update := getTypeInstanceInputForUpdate(&backendInput) updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() @@ -339,70 +340,14 @@ var _ = Describe("Action", func() { By("4.1 Check uploaded TypeInstances") expUploadTIBackend := &hublocalgraphql.TypeInstanceBackendReference{ID: testStorageBackendTI.ID, Abstract: false} - fmt.Println("expUploadTIBackend", expUploadTIBackend.ID) uploadedTI, cleanupUploaded := getUploadedTypeInstanceByValue(ctx, hubClient, implIndicatorValue) defer cleanupUploaded() Expect(uploadedTI.Backend).Should(Equal(expUploadTIBackend)) By("4.2 Check Action output TypeInstances") + updateTIOutput := mapToOutputTypeInstanceDetails(updateTI, expUploadTIBackend) uploadedTIOutput := mapToOutputTypeInstanceDetails(uploadedTI, expUploadTIBackend) - assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(uploadedTIOutput), HaveLen(1))) - }) - - It("should fail due to incorrect storage provider", func() { - implIndicatorValue := "Implementation C" - testStorageBackendTI := getDefaultTestStorageTypeInstance(ctx, hubClient) - - // TODO: This can be extracted after switching to ginkgo v2 - // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 - By("1. Preparing input Type Instances") - By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(map[string]interface{}{ - "value": map[string]interface{}{ - "key": implIndicatorValue, - }, - "backend": map[string]interface{}{ - "context": map[string]interface{}{ - "provider": "incorrect", - }, - }, - }) - downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) - defer downloadTICleanup() - - By("1.2 Creating TypeInstance which will be downloaded and updated") - update := getTypeInstanceInputForUpdate() - updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) - defer updateTICleanup() - - By("1.3 Create TypeInstance which is required for Implementation C to be picked based on Policy") - typeInstanceValue := getTypeInstanceInputForPolicy() - injectTypeInstance, tiCleanupFn := createTypeInstance(ctx, hubClient, typeInstanceValue) - defer tiCleanupFn() - - inputData := &enginegraphql.ActionInputData{ - TypeInstances: []*enginegraphql.InputTypeInstanceData{ - {Name: "testInput", ID: downloadTI.ID}, - {Name: "testUpdate", ID: updateTI.ID}, - }, - } - - By("2. Modifying default Policy to pick Implementation C...") - globalPolicyRequiredTypeInstances := []*enginegraphql.RequiredTypeInstanceReferenceInput{ - { - ID: injectTypeInstance.ID, - Description: ptr.String("Test TypeInstance"), - }, - { - ID: testStorageBackendTI.ID, - Description: ptr.String("Validation storage backend TypeInstance"), - }, - } - setGlobalTestPolicy(ctx, engineClient, addInterfacePolicyDefaultInjectionForPassingActionInterface(globalPolicyRequiredTypeInstances)) - - By("3. Create action and wait for a status phase failed") - createActionAndWaitForReadyToRunPhase(ctx, engineClient, actionName, actionPassingInterfacePath, inputData) - runActionAndWaitForStatus(ctx, engineClient, actionName, enginegraphql.ActionStatusPhaseFailed) + assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(updateTIOutput, uploadedTIOutput), HaveLen(2))) }) It("should have failed status after a failed workflow", func() { @@ -436,7 +381,7 @@ var _ = Describe("Action", func() { By("Prepare TypeInstance to update") - update := getTypeInstanceInputForUpdate() + update := getTypeInstanceInputForUpdate(nil) updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() @@ -564,13 +509,14 @@ func getTypeInstanceInputForPolicy() *hublocalgraphql.CreateTypeInstanceInput { } } -func getTypeInstanceInputForDownload(testValues map[string]interface{}) *hublocalgraphql.CreateTypeInstanceInput { +func getTypeInstanceInputForDownload(testValues map[string]interface{}, backendInput *hublocalgraphql.TypeInstanceBackendInput) *hublocalgraphql.CreateTypeInstanceInput { return &hublocalgraphql.CreateTypeInstanceInput{ TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ Path: "cap.type.capactio.capact.validation.download", Revision: "0.1.0", }, - Value: testValues, + Value: testValues, + Backend: backendInput, Attributes: []*hublocalgraphql.AttributeReferenceInput{ { Path: "cap.attribute.capactio.capact.attribute1", @@ -580,13 +526,14 @@ func getTypeInstanceInputForDownload(testValues map[string]interface{}) *hubloca } } -func getTypeInstanceInputForUpdate() *hublocalgraphql.CreateTypeInstanceInput { +func getTypeInstanceInputForUpdate(backendInput *hublocalgraphql.TypeInstanceBackendInput) *hublocalgraphql.CreateTypeInstanceInput { return &hublocalgraphql.CreateTypeInstanceInput{ TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ Path: "cap.type.capactio.capact.validation.update", Revision: "0.1.0", }, - Value: map[string]interface{}{"key": "random text to update"}, + Value: map[string]interface{}{"key": "random text to update"}, + Backend: backendInput, Attributes: []*hublocalgraphql.AttributeReferenceInput{ { Path: "cap.attribute.capactio.capact.attribute1", From abe8522c2a68b1204f7ec92dbfe1c4e335cfdb35 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Thu, 17 Mar 2022 11:38:26 +0100 Subject: [PATCH 4/9] Refactor integration tests --- hub-js/src/local/storage/service.ts | 54 +------ pkg/argo-actions/update_type_instances.go | 4 +- pkg/argo-actions/upload_type_instances.go | 33 +++- pkg/runner/common.go | 2 +- test/e2e/action_test.go | 182 ++++++++-------------- 5 files changed, 92 insertions(+), 183 deletions(-) diff --git a/hub-js/src/local/storage/service.ts b/hub-js/src/local/storage/service.ts index 139e086a5..de1937cd1 100644 --- a/hub-js/src/local/storage/service.ts +++ b/hub-js/src/local/storage/service.ts @@ -12,12 +12,6 @@ import { Driver } from "neo4j-driver"; import { TypeInstanceBackendInput } from "../types/type-instance"; import { logger } from "../../logger"; -// TODO(https://github.com/capactio/capact/issues/634): -// Represents the fake storage backend URL that should be ignored -// as the backend server is not deployed. -// It should be removed after a real backend is used in `test/e2e/action_test.go` scenarios. -export const FAKE_TEST_URL = "e2e-test-backend-mock-url:50051"; - type StorageClient = Client; export interface StorageInstanceDetails { @@ -105,11 +99,6 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const cli = await this.getClient(input.backend.id); - if (!cli) { - // TODO: remove after using a real backend in e2e tests. - continue; - } - const req: OnCreateRequest = { typeInstanceId: input.typeInstance.id, value: this.encode(input.typeInstance.value), @@ -147,11 +136,6 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const cli = await this.getClient(input.backend.id); - if (!cli) { - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend in e2e tests. - continue; - } - const req: OnUpdateRequest = { typeInstanceId: input.typeInstance.id, newResourceVersion: input.typeInstance.newResourceVersion, @@ -181,17 +165,6 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const cli = await this.getClient(input.backend.id); - if (!cli) { - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend in e2e tests. - result = { - ...result, - [input.typeInstance.id]: { - key: input.backend.id, - }, - }; - continue; - } - const req: GetValueRequest = { typeInstanceId: input.typeInstance.id, resourceVersion: input.typeInstance.resourceVersion, @@ -228,11 +201,6 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const cli = await this.getClient(input.backend.id); - if (!cli) { - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend in e2e tests. - continue; - } - const req: OnDeleteRequest = { typeInstanceId: input.typeInstance.id, context: this.encode(input.backend.context), @@ -255,11 +223,6 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const cli = await this.getClient(input.backend.id); - if (!cli) { - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend in e2e tests. - continue; - } - const req: OnLockRequest = { typeInstanceId: input.typeInstance.id, lockedBy: input.typeInstance.lockedBy, @@ -282,11 +245,6 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const cli = await this.getClient(input.backend.id); - if (!cli) { - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend in e2e tests. - continue; - } - const req: OnUnlockRequest = { typeInstanceId: input.typeInstance.id, context: this.encode(input.backend.context), @@ -337,17 +295,9 @@ export default class DelegatedStorageService { } } - private async getClient(id: string): Promise { + private async getClient(id: string): Promise { if (!this.registeredClients.has(id)) { const { url } = await this.storageInstanceDetailsFetcher(id); - if (url === FAKE_TEST_URL) { - logger.debug( - "Skipping a real call as backend was classified as a fake one" - ); - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend in e2e tests. - return undefined; - } - logger.debug("Initialize gRPC client", { backend: id, url, @@ -360,7 +310,7 @@ export default class DelegatedStorageService { this.registeredClients.set(id, client); } - return this.registeredClients.get(id); + return this.registeredClients.get(id) as StorageClient; } private static convertToJSONIfObject(val: unknown): string | undefined { diff --git a/pkg/argo-actions/update_type_instances.go b/pkg/argo-actions/update_type_instances.go index 92c81b3fe..3e43219b2 100644 --- a/pkg/argo-actions/update_type_instances.go +++ b/pkg/argo-actions/update_type_instances.go @@ -117,9 +117,7 @@ func (u *Update) render(payload []graphqllocal.UpdateTypeInstancesInput, values return ErrMissingTypeInstanceValue(typeInstance.ID) } - if _, ok = value["value"]; !ok { - // for backward compatibility, if there is an artifact without value/backend syntax, - // treat it as a value for TypeInstance + if isTypeInstanceWithLegacySyntax(u.log, value) { typeInstance.TypeInstance.Value = value continue } diff --git a/pkg/argo-actions/upload_type_instances.go b/pkg/argo-actions/upload_type_instances.go index a2fb23783..4313c11a4 100644 --- a/pkg/argo-actions/upload_type_instances.go +++ b/pkg/argo-actions/upload_type_instances.go @@ -16,8 +16,12 @@ import ( "sigs.k8s.io/yaml" ) -// UploadAction represents the upload TypeInstances action. -const UploadAction = "UploadAction" +const ( + // UploadAction represents the upload TypeInstances action. + UploadAction = "UploadAction" + valueKey = "value" + backendKey = "backend" +) // UploadConfig stores the configuration parameters for the upload TypeInstances action. type UploadConfig struct { @@ -126,9 +130,7 @@ func (u *Upload) render(payload *graphqllocal.CreateTypeInstancesInput, values m return ErrMissingTypeInstanceValue(*typeInstance.Alias) } - if _, ok = value["value"]; !ok { - // for backward compatibility, if there is an artifact without value/backend syntax, - // treat it as a value for TypeInstance + if isTypeInstanceWithLegacySyntax(u.log, value) { typeInstance.Value = value continue } @@ -146,8 +148,12 @@ func (u *Upload) render(payload *graphqllocal.CreateTypeInstancesInput, values m typeInstance.Value = unmarshalledTI.Value if unmarshalledTI.Backend != nil { - typeInstance.Backend = &graphqllocal.TypeInstanceBackendInput{ - Context: unmarshalledTI.Backend.Context, + if typeInstance.Backend != nil { + typeInstance.Backend.Context = unmarshalledTI.Backend.Context + } else { + typeInstance.Backend = &graphqllocal.TypeInstanceBackendInput{ + Context: unmarshalledTI.Backend.Context, + } } } } @@ -157,3 +163,16 @@ func (u *Upload) render(payload *graphqllocal.CreateTypeInstancesInput, values m func (u *Upload) uploadTypeInstances(ctx context.Context, in *graphqllocal.CreateTypeInstancesInput) ([]graphqllocal.CreateTypeInstanceOutput, error) { return u.client.Local.CreateTypeInstances(ctx, in) } + +func isTypeInstanceWithLegacySyntax(logger *zap.Logger, value map[string]interface{}) bool { + _, hasValue := value[valueKey] + _, hasBackend := value[backendKey] + if !hasValue && !hasBackend { + // for backward compatibility, if there is an artifact without value/backend syntax, + // treat it as a value for TypeInstance + logger.Info(fmt.Sprintf("Found legacy TypeInstance syntax without '%s' and '%s' syntax", valueKey, backendKey)) + return true + } + logger.Info(fmt.Sprintf("Processing TypeInstance '%s' and '%s'", valueKey, backendKey), zap.Bool("hasValue", hasValue), zap.Bool("hasBackend", hasBackend)) + return false +} diff --git a/pkg/runner/common.go b/pkg/runner/common.go index 562556f81..7de70de7d 100644 --- a/pkg/runner/common.go +++ b/pkg/runner/common.go @@ -23,7 +23,7 @@ func SaveToFile(path string, bytes []byte) error { // NestingOutputUnderValue write output to "value" key in YAML. func NestingOutputUnderValue(output []byte) ([]byte, error) { unmarshalled := map[string]interface{}{} - err := yaml.Unmarshal([]byte(output), &unmarshalled) + err := yaml.Unmarshal(output, &unmarshalled) if err != nil { return nil, errors.Wrap(err, "while unmarshalling output to map[string]interface{}") } diff --git a/test/e2e/action_test.go b/test/e2e/action_test.go index cdfee501a..5550a9efe 100644 --- a/test/e2e/action_test.go +++ b/test/e2e/action_test.go @@ -67,6 +67,13 @@ var _ = Describe("Action", func() { It("should pick Implementation A", func() { implIndicatorValue := "Implementation A" + testStorageBackendTI := getDefaultTestStorageTypeInstance(ctx, hubClient) + backendInput := hublocalgraphql.TypeInstanceBackendInput{ + ID: testStorageBackendTI.ID, + Context: map[string]interface{}{ + "provider": "dotenv", + }, + } // TODO: This can be extracted after switching to ginkgo v2 // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 @@ -82,11 +89,6 @@ var _ = Describe("Action", func() { updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() - By("1.3 Creating TypeInstance that describes Helm storage") - helmStorage := fixHelmStorageTypeInstanceCreateInput() - helmStorageTI, helmStorageTICleanup := createTypeInstance(ctx, hubClient, helmStorage) - defer helmStorageTICleanup() - inputData := &enginegraphql.ActionInputData{ TypeInstances: []*enginegraphql.InputTypeInstanceData{ {Name: "testInput", ID: downloadTI.ID}, @@ -124,118 +126,68 @@ var _ = Describe("Action", func() { waitForActionDeleted(ctx, engineClient, actionName) By("6. Modifying Policy to change backend storage for uploaded TypeInstance via TypeRef...") - setGlobalTestPolicy(ctx, engineClient, withHelmBackendForUploadTypeRef(helmStorageTI.ID)) - - By("7. Expecting Implementation A is picked and the Helm storage is used for uploaded TypeInstance...") - action = createActionAndWaitForReadyToRunPhase(ctx, engineClient, actionName, actionPassingInterfacePath, inputData) - assertActionRenderedWorkflowContains(action, "echo '%s'", implIndicatorValue) - runActionAndWaitForSucceeded(ctx, engineClient, actionName) - - By("8.1 Check uploaded TypeInstances") - expUploadTIBackend = &hublocalgraphql.TypeInstanceBackendReference{ID: helmStorageTI.ID, Abstract: false} + setGlobalTestPolicy(ctx, engineClient, withTestBackendForUploadTypeRef(testStorageBackendTI.ID)) - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend. - // for now, the Local Hub returns backend id under `key` property. - implIndicatorValue = expUploadTIBackend.ID + By("7 Creating TypeInstance which used test storage with dotenv provider") - uploadedTI, cleanupUploaded = getUploadedTypeInstanceByValue(ctx, hubClient, implIndicatorValue) - defer cleanupUploaded() // We need to clean it up as it's not deleted when Action is deleted. - Expect(uploadedTI.Backend).Should(Equal(expUploadTIBackend)) + By("7.1 Creating TypeInstance which will be downloaded") + download2 := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, &backendInput) + downloadTI2, downloadTICleanup2 := createTypeInstance(ctx, hubClient, download2) + defer downloadTICleanup2() - By("8.2 Check Action output TypeInstances") - uploadedTIOutput = mapToOutputTypeInstanceDetails(uploadedTI, expUploadTIBackend) - assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(expUpdatedTIOutput, uploadedTIOutput), HaveLen(2))) - }) + By("7.2 Creating TypeInstance which will be downloaded and updated") + update2 := getTypeInstanceInputForUpdate(&backendInput) + updateTI2, updateTICleanup2 := createTypeInstance(ctx, hubClient, update2) + defer updateTICleanup2() - It("should pick Implementation B based on Policy rule", func() { - implIndicatorValue := "Implementation B" - - // TODO: This can be extracted after switching to ginkgo v2 - // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 - By("1. Preparing input Type Instances") - By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, nil) - downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) - defer downloadTICleanup() - - By("1.2 Creating TypeInstance which will be downloaded and updated") - update := getTypeInstanceInputForUpdate(nil) - updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) - defer updateTICleanup() - - By("1.3 Creating TypeInstance that describes Helm storage") - helmStorage := fixHelmStorageTypeInstanceCreateInput() - helmStorageTI, helmStorageTICleanup := createTypeInstance(ctx, hubClient, helmStorage) - defer helmStorageTICleanup() - - By("1.4 Create TypeInstance which is required for Implementation B to be picked based on Policy") - typeInstanceValue := getTypeInstanceInputForPolicy() - injectTypeInstance, tiCleanupFn := createTypeInstance(ctx, hubClient, typeInstanceValue) - defer tiCleanupFn() - - inputData := &enginegraphql.ActionInputData{ + inputData = &enginegraphql.ActionInputData{ TypeInstances: []*enginegraphql.InputTypeInstanceData{ - {Name: "testInput", ID: downloadTI.ID}, - {Name: "testUpdate", ID: updateTI.ID}, - }, - } - - By("2. Modifying rule Policy to pick Implementation B...") - globalPolicyRequiredTypeInstances := []*enginegraphql.RequiredTypeInstanceReferenceInput{ - { - ID: injectTypeInstance.ID, - Description: ptr.String("Test TypeInstance"), - }, - { - ID: helmStorageTI.ID, - Description: ptr.String("Helm backend TypeInstance"), + {Name: "testInput", ID: downloadTI2.ID}, + {Name: "testUpdate", ID: updateTI2.ID}, }, } - setGlobalTestPolicy(ctx, engineClient, prependInjectRuleForPassingActionInterface(globalPolicyRequiredTypeInstances)) - By("3. Expecting Implementation B is picked and injected Helm storage is used...") - action := createActionAndWaitForReadyToRunPhase(ctx, engineClient, actionName, actionPassingInterfacePath, inputData) + By("8. Expecting Implementation A is picked and the test storage is used for uploaded TypeInstance...") + action = createActionAndWaitForReadyToRunPhase(ctx, engineClient, actionName, actionPassingInterfacePath, inputData) assertActionRenderedWorkflowContains(action, "echo '%s'", implIndicatorValue) runActionAndWaitForSucceeded(ctx, engineClient, actionName) - By("4.1 Check uploaded TypeInstances") - expUploadTIBackend := &hublocalgraphql.TypeInstanceBackendReference{ID: helmStorageTI.ID, Abstract: false} + By("9.1 Check uploaded TypeInstances") + expUploadTIBackend = &hublocalgraphql.TypeInstanceBackendReference{ID: testStorageBackendTI.ID, Abstract: false} + uploadedTI2, cleanupUploaded2 := getUploadedTypeInstanceByValue(ctx, hubClient, implIndicatorValue) + defer cleanupUploaded2() + Expect(uploadedTI2.Backend).Should(Equal(expUploadTIBackend)) - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend. - // for now, the Local Hub returns backend id under `key` property. - implIndicatorValue = expUploadTIBackend.ID - - uploadedTI, cleanupUploaded := getUploadedTypeInstanceByValue(ctx, hubClient, implIndicatorValue) - defer cleanupUploaded() // We need to clean it up as it's not deleted when Action is deleted. - Expect(uploadedTI.Backend).Should(Equal(expUploadTIBackend)) - - By("4.2 Check Action output TypeInstances") - uploadedTIOutput := mapToOutputTypeInstanceDetails(uploadedTI, expUploadTIBackend) - assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(uploadedTIOutput), HaveLen(1))) + By("9.2 Check Action output TypeInstances") + updateTIOutput := mapToOutputTypeInstanceDetails(updateTI2, expUploadTIBackend) + uploadedTIOutput = mapToOutputTypeInstanceDetails(uploadedTI2, expUploadTIBackend) + assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(updateTIOutput, uploadedTIOutput), HaveLen(2))) }) - It("should pick Implementation B based on Interface default", func() { + It("should pick Implementation B based on Policy rule", func() { implIndicatorValue := "Implementation B" + testStorageBackendTI := getDefaultTestStorageTypeInstance(ctx, hubClient) + backendInput := hublocalgraphql.TypeInstanceBackendInput{ + ID: testStorageBackendTI.ID, + Context: map[string]interface{}{ + "provider": "dotenv", + }, + } // TODO: This can be extracted after switching to ginkgo v2 // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 By("1. Preparing input Type Instances") By("1.1 Creating TypeInstance which will be downloaded") - download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, nil) + download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, &backendInput) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() By("1.2 Creating TypeInstance which will be downloaded and updated") - update := getTypeInstanceInputForUpdate(nil) + update := getTypeInstanceInputForUpdate(&backendInput) updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() - By("1.3 Creating TypeInstance that describes Helm storage") - helmStorage := fixHelmStorageTypeInstanceCreateInput() - helmStorageTI, helmStorageTICleanup := createTypeInstance(ctx, hubClient, helmStorage) - defer helmStorageTICleanup() - - By("1.4 Create TypeInstance which is required for Implementation B to be picked based on Policy") + By("1.3 Create TypeInstance which is required for Implementation B to be picked based on Policy") typeInstanceValue := getTypeInstanceInputForPolicy() injectTypeInstance, tiCleanupFn := createTypeInstance(ctx, hubClient, typeInstanceValue) defer tiCleanupFn() @@ -247,33 +199,28 @@ var _ = Describe("Action", func() { }, } - By("2. Modifying default Policy to pick Implementation B...") + By("2. Modifying rule Policy to pick Implementation B...") globalPolicyRequiredTypeInstances := []*enginegraphql.RequiredTypeInstanceReferenceInput{ { ID: injectTypeInstance.ID, Description: ptr.String("Test TypeInstance"), }, { - ID: helmStorageTI.ID, + ID: testStorageBackendTI.ID, Description: ptr.String("Helm backend TypeInstance"), }, } - setGlobalTestPolicy(ctx, engineClient, addInterfacePolicyDefaultInjectionForPassingActionInterface(globalPolicyRequiredTypeInstances)) + setGlobalTestPolicy(ctx, engineClient, prependInjectRuleForPassingActionInterface(globalPolicyRequiredTypeInstances)) - By("3. Expecting Implementation B is picked and injected Helm storage is used...") + By("3. Expecting Implementation B is picked and injected test storage is used...") action := createActionAndWaitForReadyToRunPhase(ctx, engineClient, actionName, actionPassingInterfacePath, inputData) assertActionRenderedWorkflowContains(action, "echo '%s'", implIndicatorValue) runActionAndWaitForSucceeded(ctx, engineClient, actionName) By("4.1 Check uploaded TypeInstances") - expUploadTIBackend := &hublocalgraphql.TypeInstanceBackendReference{ID: helmStorageTI.ID, Abstract: false} - - // TODO(https://github.com/capactio/capact/issues/634): remove after using a real backend. - // for now, the Local Hub returns backend id under `key` property. - implIndicatorValue = expUploadTIBackend.ID - + expUploadTIBackend := &hublocalgraphql.TypeInstanceBackendReference{ID: testStorageBackendTI.ID, Abstract: false} uploadedTI, cleanupUploaded := getUploadedTypeInstanceByValue(ctx, hubClient, implIndicatorValue) - defer cleanupUploaded() // We need to clean it up as it's not deleted when Action is deleted. + defer cleanupUploaded() Expect(uploadedTI.Backend).Should(Equal(expUploadTIBackend)) By("4.2 Check Action output TypeInstances") @@ -281,25 +228,21 @@ var _ = Describe("Action", func() { assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(uploadedTIOutput), HaveLen(1))) }) - It("should propagate context provider to storage backend", func() { - implIndicatorValue := "Implementation C" + It("should pick Implementation B based on Interface default", func() { + implIndicatorValue := "Implementation B" testStorageBackendTI := getDefaultTestStorageTypeInstance(ctx, hubClient) - - // TODO: This can be extracted after switching to ginkgo v2 - // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 - By("1. Preparing input Type Instances") - By("1.1 Creating TypeInstance which will be downloaded") backendInput := hublocalgraphql.TypeInstanceBackendInput{ ID: testStorageBackendTI.ID, Context: map[string]interface{}{ "provider": "dotenv", }, } - download := getTypeInstanceInputForDownload( - map[string]interface{}{ - "key": implIndicatorValue, - }, - &backendInput) + + // TODO: This can be extracted after switching to ginkgo v2 + // see: https://github.com/onsi/ginkgo/issues/70#issuecomment-924250145 + By("1. Preparing input Type Instances") + By("1.1 Creating TypeInstance which will be downloaded") + download := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, &backendInput) downloadTI, downloadTICleanup := createTypeInstance(ctx, hubClient, download) defer downloadTICleanup() @@ -308,7 +251,7 @@ var _ = Describe("Action", func() { updateTI, updateTICleanup := createTypeInstance(ctx, hubClient, update) defer updateTICleanup() - By("1.3 Create TypeInstance which is required for Implementation C to be picked based on Policy") + By("1.3 Create TypeInstance which is required for Implementation B to be picked based on Policy") typeInstanceValue := getTypeInstanceInputForPolicy() injectTypeInstance, tiCleanupFn := createTypeInstance(ctx, hubClient, typeInstanceValue) defer tiCleanupFn() @@ -320,7 +263,7 @@ var _ = Describe("Action", func() { }, } - By("2. Modifying default Policy to pick Implementation C...") + By("2. Modifying default Policy to pick Implementation B...") globalPolicyRequiredTypeInstances := []*enginegraphql.RequiredTypeInstanceReferenceInput{ { ID: injectTypeInstance.ID, @@ -328,12 +271,12 @@ var _ = Describe("Action", func() { }, { ID: testStorageBackendTI.ID, - Description: ptr.String("Validation storage backend TypeInstance"), + Description: ptr.String("Helm backend TypeInstance"), }, } setGlobalTestPolicy(ctx, engineClient, addInterfacePolicyDefaultInjectionForPassingActionInterface(globalPolicyRequiredTypeInstances)) - By("3. Expecting Implementation C is picked and injected Validation storage is used...") + By("3. Expecting Implementation B is picked and injected test storage is used...") action := createActionAndWaitForReadyToRunPhase(ctx, engineClient, actionName, actionPassingInterfacePath, inputData) assertActionRenderedWorkflowContains(action, "echo '%s'", implIndicatorValue) runActionAndWaitForSucceeded(ctx, engineClient, actionName) @@ -345,9 +288,8 @@ var _ = Describe("Action", func() { Expect(uploadedTI.Backend).Should(Equal(expUploadTIBackend)) By("4.2 Check Action output TypeInstances") - updateTIOutput := mapToOutputTypeInstanceDetails(updateTI, expUploadTIBackend) uploadedTIOutput := mapToOutputTypeInstanceDetails(uploadedTI, expUploadTIBackend) - assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(updateTIOutput, uploadedTIOutput), HaveLen(2))) + assertOutputTypeInstancesInActionStatus(ctx, engineClient, action.Name, And(ContainElements(uploadedTIOutput), HaveLen(1))) }) It("should have failed status after a failed workflow", func() { @@ -651,7 +593,7 @@ func createTypeInstance(ctx context.Context, hubClient *hubclient.Client, in *hu type policyOption func(*enginegraphql.PolicyInput) -func withHelmBackendForUploadTypeRef(backendID string) policyOption { +func withTestBackendForUploadTypeRef(backendID string) policyOption { return func(policy *enginegraphql.PolicyInput) { policy.TypeInstance = &enginegraphql.TypeInstancePolicyInput{ Rules: []*enginegraphql.RulesForTypeInstanceInput{ From 6d0e3f6686f76eea7372a92833f8d4cfae881ab5 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Fri, 18 Mar 2022 08:49:17 +0100 Subject: [PATCH 5/9] fix typo --- pkg/argo-actions/upload_type_instances.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/argo-actions/upload_type_instances.go b/pkg/argo-actions/upload_type_instances.go index 4313c11a4..3b6a1ad34 100644 --- a/pkg/argo-actions/upload_type_instances.go +++ b/pkg/argo-actions/upload_type_instances.go @@ -170,7 +170,7 @@ func isTypeInstanceWithLegacySyntax(logger *zap.Logger, value map[string]interfa if !hasValue && !hasBackend { // for backward compatibility, if there is an artifact without value/backend syntax, // treat it as a value for TypeInstance - logger.Info(fmt.Sprintf("Found legacy TypeInstance syntax without '%s' and '%s' syntax", valueKey, backendKey)) + logger.Info(fmt.Sprintf("Found legacy TypeInstance syntax without '%s' and '%s'", valueKey, backendKey)) return true } logger.Info(fmt.Sprintf("Processing TypeInstance '%s' and '%s'", valueKey, backendKey), zap.Bool("hasValue", hasValue), zap.Bool("hasBackend", hasBackend)) From ac92303d862e64575aa92e4f7742ba188fe1da9e Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Mon, 21 Mar 2022 13:54:14 +0100 Subject: [PATCH 6/9] Fix internal storage TI --- hub-js/graphql/local/schema.graphql | 2 +- .../resolver/mutation/delete-type-instance.ts | 2 +- .../resolver/mutation/lock-type-instances.ts | 2 +- hub-js/src/local/storage/service.ts | 14 +++--- internal/cli/testing/storage_backend.go | 15 ++----- test/e2e/action_test.go | 43 ++----------------- 6 files changed, 17 insertions(+), 61 deletions(-) diff --git a/hub-js/graphql/local/schema.graphql b/hub-js/graphql/local/schema.graphql index db6c754db..6892ede2b 100644 --- a/hub-js/graphql/local/schema.graphql +++ b/hub-js/graphql/local/schema.graphql @@ -123,7 +123,7 @@ type TypeInstanceResourceVersionSpec { abstract: backendRef.abstract, fetchInput: { typeInstance: { resourceVersion: rev.resourceVersion, id: ti.id }, - backend: { context: backendCtx.context, id: backendRef.id} + backend: { context: apoc.convert.fromJsonMap(backendCtx.context), id: backendRef.id} } } AS value RETURN value diff --git a/hub-js/src/local/resolver/mutation/delete-type-instance.ts b/hub-js/src/local/resolver/mutation/delete-type-instance.ts index 7cec7e530..7158947af 100644 --- a/hub-js/src/local/resolver/mutation/delete-type-instance.ts +++ b/hub-js/src/local/resolver/mutation/delete-type-instance.ts @@ -58,7 +58,7 @@ export async function deleteTypeInstance( // NOTE: Need to be preserved with 'WITH' statement, otherwise we won't be able // to access node's properties after 'DETACH DELETE' statement. - WITH *, {id: ti.id, backend: { id: backendRef.id, context: specBackend.context, abstract: backendRef.abstract}} as out + WITH *, {id: ti.id, backend: { id: backendRef.id, context: apoc.convert.fromJsonMap(specBackend.context), abstract: backendRef.abstract}} as out DETACH DELETE ti, metadata, spec, tirs, specBackend WITH * diff --git a/hub-js/src/local/resolver/mutation/lock-type-instances.ts b/hub-js/src/local/resolver/mutation/lock-type-instances.ts index fbddc8b6c..37d318635 100644 --- a/hub-js/src/local/resolver/mutation/lock-type-instances.ts +++ b/hub-js/src/local/resolver/mutation/lock-type-instances.ts @@ -180,7 +180,7 @@ export async function getTypeInstanceStoredExternally( WITH { typeInstanceId: ti.id, - backend: { context: backendCtx.context, id: backendRef.id, abstract: backendRef.abstract} + backend: { context: apoc.convert.fromJsonMap(backendCtx.context), id: backendRef.id, abstract: backendRef.abstract} } AS value RETURN value `, diff --git a/hub-js/src/local/storage/service.ts b/hub-js/src/local/storage/service.ts index 0c6dcbd3c..9e031a92e 100644 --- a/hub-js/src/local/storage/service.ts +++ b/hub-js/src/local/storage/service.ts @@ -125,7 +125,7 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const backend = await this.getBackendContainer(input.backend.id); - + const validateErr = this.validateInput(input, backend.validateSpec); if (validateErr) { throw Error( @@ -205,7 +205,7 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const backend = await this.getBackendContainer(input.backend.id); - + const validateErr = this.validateInput(input, backend.validateSpec); if (validateErr) { throw Error( @@ -249,7 +249,7 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const backend = await this.getBackendContainer(input.backend.id); - + const validateErr = this.validateInput(input, backend.validateSpec); if (validateErr) { throw Error( @@ -278,7 +278,7 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const backend = await this.getBackendContainer(input.backend.id); - + const validateErr = this.validateInput(input, backend.validateSpec); if (validateErr) { throw Error( @@ -308,7 +308,7 @@ export default class DelegatedStorageService { backendId: input.backend.id, }); const backend = await this.getBackendContainer(input.backend.id); - + const validateErr = this.validateInput(input, backend.validateSpec); if (validateErr) { throw Error( @@ -371,9 +371,7 @@ export default class DelegatedStorageService { } } - private async getBackendContainer( - id: string - ): Promise { + private async getBackendContainer(id: string): Promise { if (!this.registeredClients.has(id)) { const spec = await this.storageInstanceDetailsFetcher(id); logger.debug("Initialize gRPC BackendContainer", { diff --git a/internal/cli/testing/storage_backend.go b/internal/cli/testing/storage_backend.go index cea21cfbb..6c27a2d73 100644 --- a/internal/cli/testing/storage_backend.go +++ b/internal/cli/testing/storage_backend.go @@ -2,7 +2,6 @@ package testing import ( "context" - "encoding/json" "capact.io/capact/internal/logger" "capact.io/capact/internal/ptr" @@ -44,9 +43,9 @@ const testStorageTypeContextSchema = ` ` type typeInstanceValue struct { - URL string `json:"url"` - AcceptValue bool `json:"acceptValue"` - ContextSchema interface{} `json:"contextSchema"` + URL string `json:"url"` + AcceptValue bool `json:"acceptValue"` + ContextSchema string `json:"contextSchema"` } // StorageBackendRegister provides functionality to produce and upload test storage backend TypeInstance. @@ -80,12 +79,6 @@ func NewStorageBackendRegister() (*StorageBackendRegister, error) { // RegisterTypeInstances produces and uploads TypeInstances which describe Test storage backend. func (i *StorageBackendRegister) RegisterTypeInstances(ctx context.Context) error { - var contextSchema interface{} - err := json.Unmarshal([]byte(testStorageTypeContextSchema), &contextSchema) - if err != nil { - return errors.Wrap(err, "while unmarshaling contextSchema") - } - in := &hublocalgraphql.CreateTypeInstanceInput{ CreatedBy: ptr.String("populator/test-storage-backend-registration"), TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ @@ -95,7 +88,7 @@ func (i *StorageBackendRegister) RegisterTypeInstances(ctx context.Context) erro Value: typeInstanceValue{ URL: i.cfg.TestStorageBackendURL, AcceptValue: true, - ContextSchema: contextSchema, + ContextSchema: testStorageTypeContextSchema, }, } diff --git a/test/e2e/action_test.go b/test/e2e/action_test.go index 5550a9efe..c0e75f516 100644 --- a/test/e2e/action_test.go +++ b/test/e2e/action_test.go @@ -13,7 +13,6 @@ import ( "strings" "time" - "capact.io/capact/internal/cli/heredoc" "capact.io/capact/internal/ptr" enginegraphql "capact.io/capact/pkg/engine/api/graphql" engine "capact.io/capact/pkg/engine/client" @@ -128,7 +127,7 @@ var _ = Describe("Action", func() { By("6. Modifying Policy to change backend storage for uploaded TypeInstance via TypeRef...") setGlobalTestPolicy(ctx, engineClient, withTestBackendForUploadTypeRef(testStorageBackendTI.ID)) - By("7 Creating TypeInstance which used test storage with dotenv provider") + By("7. Creating TypeInstance which will be used by test storage with dotenv provider") By("7.1 Creating TypeInstance which will be downloaded") download2 := getTypeInstanceInputForDownload(map[string]interface{}{"key": implIndicatorValue}, &backendInput) @@ -207,7 +206,7 @@ var _ = Describe("Action", func() { }, { ID: testStorageBackendTI.ID, - Description: ptr.String("Helm backend TypeInstance"), + Description: ptr.String("Dotenv storage backend TypeInstance"), }, } setGlobalTestPolicy(ctx, engineClient, prependInjectRuleForPassingActionInterface(globalPolicyRequiredTypeInstances)) @@ -271,7 +270,7 @@ var _ = Describe("Action", func() { }, { ID: testStorageBackendTI.ID, - Description: ptr.String("Helm backend TypeInstance"), + Description: ptr.String("Dotenv storage backend TypeInstance"), }, } setGlobalTestPolicy(ctx, engineClient, addInterfacePolicyDefaultInjectionForPassingActionInterface(globalPolicyRequiredTypeInstances)) @@ -284,7 +283,7 @@ var _ = Describe("Action", func() { By("4.1 Check uploaded TypeInstances") expUploadTIBackend := &hublocalgraphql.TypeInstanceBackendReference{ID: testStorageBackendTI.ID, Abstract: false} uploadedTI, cleanupUploaded := getUploadedTypeInstanceByValue(ctx, hubClient, implIndicatorValue) - defer cleanupUploaded() + defer cleanupUploaded() // We need to clean it up as it's not deleted when Action is deleted. Expect(uploadedTI.Backend).Should(Equal(expUploadTIBackend)) By("4.2 Check Action output TypeInstances") @@ -485,40 +484,6 @@ func getTypeInstanceInputForUpdate(backendInput *hublocalgraphql.TypeInstanceBac } } -func fixHelmStorageTypeInstanceCreateInput() *hublocalgraphql.CreateTypeInstanceInput { - return &hublocalgraphql.CreateTypeInstanceInput{ - TypeRef: &hublocalgraphql.TypeInstanceTypeReferenceInput{ - Path: "cap.type.helm.storage", - Revision: "0.1.0", - }, - Attributes: []*hublocalgraphql.AttributeReferenceInput{}, - Value: map[string]interface{}{ - "url": "e2e-test-backend-mock-url:50051", - "acceptValue": true, - "contextSchema": heredoc.Doc(` - { - "$id": "#/properties/contextSchema", - "type": "object", - "required": [ - "name", - "namespace" - ], - "properties": { - "name": { - "$id": "#/properties/contextSchema/properties/name", - "type": "string" - }, - "namespace": { - "$id": "#/properties/contextSchema/properties/namespace", - "type": "string" - } - }, - "additionalProperties": false - }`), - }, - } -} - func createActionAndWaitForReadyToRunPhase(ctx context.Context, engineClient *engine.Client, actionName, actionPath string, input *enginegraphql.ActionInputData) *enginegraphql.Action { _, err := engineClient.CreateAction(ctx, &enginegraphql.ActionDetailsInput{ Name: actionName, From 7f663c7b5af4ddbf15d900a55286728430a8fb96 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Mon, 21 Mar 2022 14:56:31 +0100 Subject: [PATCH 7/9] Add pkg/hub/api/graphql/local/schema_gen.go --- pkg/hub/api/graphql/local/schema_gen.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/hub/api/graphql/local/schema_gen.go b/pkg/hub/api/graphql/local/schema_gen.go index b572a979b..53c69f9b6 100644 --- a/pkg/hub/api/graphql/local/schema_gen.go +++ b/pkg/hub/api/graphql/local/schema_gen.go @@ -723,7 +723,7 @@ type TypeInstanceResourceVersionSpec { abstract: backendRef.abstract, fetchInput: { typeInstance: { resourceVersion: rev.resourceVersion, id: ti.id }, - backend: { context: backendCtx.context, id: backendRef.id} + backend: { context: apoc.convert.fromJsonMap(backendCtx.context), id: backendRef.id} } } AS value RETURN value @@ -3438,7 +3438,7 @@ func (ec *executionContext) _TypeInstanceResourceVersionSpec_value(ctx context.C return obj.Value, nil } directive1 := func(ctx context.Context) (interface{}, error) { - statement, err := ec.unmarshalOString2áš–string(ctx, "MATCH (this)<-[:SPECIFIED_BY]-(rev:TypeInstanceResourceVersion)<-[:CONTAINS]-(ti:TypeInstance)\nMATCH (this)-[:WITH_BACKEND]->(backendCtx)\nMATCH (ti)-[:STORED_IN]->(backendRef)\nWITH *\nCALL apoc.when(\n backendRef.abstract,\n '\n WITH {\n abstract: backendRef.abstract,\n builtinValue: apoc.convert.fromJsonMap(spec.value)\n } AS value\n RETURN value\n ',\n '\n WITH {\n abstract: backendRef.abstract,\n fetchInput: {\n typeInstance: { resourceVersion: rev.resourceVersion, id: ti.id },\n backend: { context: backendCtx.context, id: backendRef.id}\n }\n } AS value\n RETURN value\n ',\n {spec: this, rev: rev, ti: ti, backendRef: backendRef, backendCtx: backendCtx}\n) YIELD value as out\n\nRETURN out.value") + statement, err := ec.unmarshalOString2áš–string(ctx, "MATCH (this)<-[:SPECIFIED_BY]-(rev:TypeInstanceResourceVersion)<-[:CONTAINS]-(ti:TypeInstance)\nMATCH (this)-[:WITH_BACKEND]->(backendCtx)\nMATCH (ti)-[:STORED_IN]->(backendRef)\nWITH *\nCALL apoc.when(\n backendRef.abstract,\n '\n WITH {\n abstract: backendRef.abstract,\n builtinValue: apoc.convert.fromJsonMap(spec.value)\n } AS value\n RETURN value\n ',\n '\n WITH {\n abstract: backendRef.abstract,\n fetchInput: {\n typeInstance: { resourceVersion: rev.resourceVersion, id: ti.id },\n backend: { context: apoc.convert.fromJsonMap(backendCtx.context), id: backendRef.id}\n }\n } AS value\n RETURN value\n ',\n {spec: this, rev: rev, ti: ti, backendRef: backendRef, backendCtx: backendCtx}\n) YIELD value as out\n\nRETURN out.value") if err != nil { return nil, err } From e902da7e1c6b62a4576e9dfff0c75d64b9d35977 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Wed, 23 Mar 2022 22:25:57 +0100 Subject: [PATCH 8/9] Fix helm upgrade command --- pkg/runner/helm/types.go | 5 +++++ pkg/runner/helm/upgrade.go | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pkg/runner/helm/types.go b/pkg/runner/helm/types.go index 4936bbdef..e34fd7c41 100644 --- a/pkg/runner/helm/types.go +++ b/pkg/runner/helm/types.go @@ -96,6 +96,11 @@ type ChartRelease struct { Chart Chart `json:"chart"` } +// ChartReleaseInputData represents a Helm chart release input data. +type ChartReleaseInputData struct { + Value ChartRelease `json:"value"` +} + // Input stores the input configuration for the runner. type Input struct { Args Arguments diff --git a/pkg/runner/helm/upgrade.go b/pkg/runner/helm/upgrade.go index 8c4fc9631..20bc9a1a1 100644 --- a/pkg/runner/helm/upgrade.go +++ b/pkg/runner/helm/upgrade.go @@ -110,11 +110,11 @@ func (i *upgrader) loadHelmReleaseData(path string) (ChartRelease, error) { return ChartRelease{}, errors.Wrapf(err, "while reading values from file %q", path) } - var chartRelease ChartRelease - if err := yaml.Unmarshal(bytes, &chartRelease); err != nil { + var chartReleaseIn ChartReleaseInputData + if err := yaml.Unmarshal(bytes, &chartReleaseIn); err != nil { return ChartRelease{}, errors.Wrapf(err, "while parsing %q", path) } - return chartRelease, nil + return chartReleaseIn.Value, nil } func (i *upgrader) mergeHelmChartData(helmRelease ChartRelease, in Input) ChartRelease { From dca5f7cce708722b6a5504ebc75b4861ace3134a Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Thu, 24 Mar 2022 11:38:30 +0100 Subject: [PATCH 9/9] Run integration tests in main --- hack/lib/const.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hack/lib/const.sh b/hack/lib/const.sh index 1b8cc329a..4baeef414 100644 --- a/hack/lib/const.sh +++ b/hack/lib/const.sh @@ -50,7 +50,7 @@ readonly CAPACT_USE_TEST_SETUP="false" # readonly CAPACT_INCREASE_RESOURCE_LIMITS="true" -readonly CAPACT_HUB_MANIFESTS_SOURCE_REPO_URL="github.com/mkuziemko/hub-manifests" +readonly CAPACT_HUB_MANIFESTS_SOURCE_REPO_URL="github.com/capactio/hub-manifests" # The git ref to checkout. It can point to a commit SHA, a branch name, or a tag. # If you want to use your forked version, remember to update CAPACT_HUB_MANIFESTS_SOURCE_REPO_URL respectively. -readonly CAPACT_HUB_MANIFESTS_SOURCE_REPO_REF="context_handling" +readonly CAPACT_HUB_MANIFESTS_SOURCE_REPO_REF="main"