This is an automated email from the ASF dual-hosted git repository. pcongiusti pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 3988ba34fb7686a37ae941b99d1e733d9c4c74e3 Author: Pasquale Congiusti <[email protected]> AuthorDate: Fri Jul 28 16:19:48 2023 +0200 feat(cmd): promote multi tenancy Closes #3890 --- pkg/cmd/promote.go | 32 ++++++++++++++++++-------- pkg/cmd/promote_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/pkg/cmd/promote.go b/pkg/cmd/promote.go index b12ebc948..b15cc0838 100644 --- a/pkg/cmd/promote.go +++ b/pkg/cmd/promote.go @@ -49,14 +49,15 @@ func newCmdPromote(rootCmdOptions *RootCmdOptions) (*cobra.Command, *promoteCmdO RootCmdOptions: rootCmdOptions, } cmd := cobra.Command{ - Use: "promote my-it --to [namespace]", + Use: "promote my-it [--to <namespace>] [-x <promoted-operator-id>]", Short: "Promote an Integration/Pipe from an environment to another", Long: "Promote an Integration/Pipe from an environment to another, for example from a Development environment to a Production environment", PreRunE: decode(&options), RunE: options.run, } - cmd.Flags().String("to", "", "The namespace where to promote the Integration") + cmd.Flags().String("to", "", "The namespace where to promote the Integration/Pipe") + cmd.Flags().StringP("to-operator", "x", "", "The operator id which will reconcile the promoted Integration/Pipe") cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml") cmd.Flags().BoolP("image", "i", false, "Output the container image only") @@ -66,16 +67,20 @@ func newCmdPromote(rootCmdOptions *RootCmdOptions) (*cobra.Command, *promoteCmdO type promoteCmdOptions struct { *RootCmdOptions To string `mapstructure:"to" yaml:",omitempty"` + ToOperator string `mapstructure:"to-operator" yaml:",omitempty"` OutputFormat string `mapstructure:"output" yaml:",omitempty"` Image bool `mapstructure:"image" yaml:",omitempty"` } func (o *promoteCmdOptions) validate(_ *cobra.Command, args []string) error { if len(args) != 1 { - return errors.New("promote expects an Integration/Pipe name argument") + return errors.New("promote requires an Integration/Pipe name argument") } if o.To == "" { - return errors.New("promote expects a destination namespace as --to argument") + return errors.New("promote requires a destination namespace as --to argument") + } + if o.To == o.Namespace { + return errors.New("source and destination namespaces must be different in order to avoid promoted Integration/Pipe clashes with the source Integration/Pipe") } return nil } @@ -457,7 +462,7 @@ func (o *promoteCmdOptions) editIntegration(it *v1.Integration) *v1.Integration dst := v1.NewIntegration(o.To, it.Name) contImage := it.Status.Image dst.Spec = *it.Spec.DeepCopy() - dst.Annotations = cloneAnnotations(it.Annotations) + dst.Annotations = cloneAnnotations(it.Annotations, o.ToOperator) dst.Labels = cloneLabels(it.Labels) if dst.Spec.Traits.Container == nil { dst.Spec.Traits.Container = &traitv1.ContainerTrait{} @@ -466,14 +471,23 @@ func (o *promoteCmdOptions) editIntegration(it *v1.Integration) *v1.Integration return &dst } -// Return all annotations but the ones specific to source (ie, the operator). -func cloneAnnotations(ann map[string]string) map[string]string { +// Return all annotations overriding the operator Id if provided. +func cloneAnnotations(ann map[string]string, operatorID string) map[string]string { + operatorIDAnnotationSet := false newMap := make(map[string]string) for k, v := range ann { - if k != v1.OperatorIDAnnotation { + if k == v1.OperatorIDAnnotation { + if operatorID != "" { + newMap[v1.OperatorIDAnnotation] = operatorID + operatorIDAnnotationSet = true + } + } else { newMap[k] = v } } + if !operatorIDAnnotationSet && operatorID != "" { + newMap[v1.OperatorIDAnnotation] = operatorID + } return newMap } @@ -489,7 +503,7 @@ func cloneLabels(lbs map[string]string) map[string]string { func (o *promoteCmdOptions) editPipe(kb *v1.Pipe, it *v1.Integration) *v1.Pipe { dst := v1.NewPipe(o.To, kb.Name) dst.Spec = *kb.Spec.DeepCopy() - dst.Annotations = cloneAnnotations(kb.Annotations) + dst.Annotations = cloneAnnotations(kb.Annotations, o.ToOperator) dst.Labels = cloneLabels(kb.Labels) contImage := it.Status.Image if dst.Spec.Integration == nil { diff --git a/pkg/cmd/promote_test.go b/pkg/cmd/promote_test.go index f6817ecbe..bc39d63de 100644 --- a/pkg/cmd/promote_test.go +++ b/pkg/cmd/promote_test.go @@ -263,7 +263,7 @@ func TestItImageOnly(t *testing.T) { _, promoteCmd, _ := initializePromoteCmdOptions(t, &srcPlatform, &dstPlatform, &defaultIntegration, &srcCatalog, &dstCatalog) output, err := test.ExecuteCommand(promoteCmd, cmdPromote, "my-it-test", "--to", "prod-namespace", "-i", "-n", "default") assert.Nil(t, err) - assert.Equal(t, fmt.Sprintf("my-special-image\n"), output) + assert.Equal(t, "my-special-image\n", output) } func TestPipeImageOnly(t *testing.T) { @@ -283,5 +283,61 @@ func TestPipeImageOnly(t *testing.T) { _, promoteCmd, _ := initializePromoteCmdOptions(t, &srcPlatform, &dstPlatform, &defaultKB, &defaultIntegration, &srcCatalog, &dstCatalog) output, err := test.ExecuteCommand(promoteCmd, cmdPromote, "my-kb-test", "--to", "prod-namespace", "-i", "-n", "default") assert.Nil(t, err) - assert.Equal(t, fmt.Sprintf("my-special-image\n"), output) + assert.Equal(t, "my-special-image\n", output) +} + +func TestIntegrationToOperatorId(t *testing.T) { + srcPlatform := v1.NewIntegrationPlatform("default", platform.DefaultPlatformName) + srcPlatform.Status.Version = defaults.Version + srcPlatform.Status.Build.RuntimeVersion = defaults.DefaultRuntimeVersion + srcPlatform.Status.Phase = v1.IntegrationPlatformPhaseReady + dstPlatform := v1.NewIntegrationPlatform("prod-namespace", platform.DefaultPlatformName) + dstPlatform.Status.Version = defaults.Version + dstPlatform.Status.Build.RuntimeVersion = defaults.DefaultRuntimeVersion + dstPlatform.Status.Phase = v1.IntegrationPlatformPhaseReady + defaultIntegration := nominalIntegration("my-it-test") + srcCatalog := createTestCamelCatalog(srcPlatform) + dstCatalog := createTestCamelCatalog(dstPlatform) + + // Verify default (missing) operator Id + promoteCmdOptions, promoteCmd, _ := initializePromoteCmdOptions(t, &srcPlatform, &dstPlatform, &defaultIntegration, &srcCatalog, &dstCatalog) + output, err := test.ExecuteCommand(promoteCmd, cmdPromote, "my-it-test", "-x", "my-prod-operator", "-o", "yaml", "--to", "prod") + assert.Equal(t, "yaml", promoteCmdOptions.OutputFormat) + assert.Nil(t, err) + assert.Equal(t, `apiVersion: camel.apache.org/v1 +kind: Integration +metadata: + annotations: + camel.apache.org/operator.id: my-prod-operator + creationTimestamp: null + name: my-it-test + namespace: prod +spec: + traits: + container: + image: my-special-image +status: {} +`, output) + // Verify also when the operator Id is set in the integration + defaultIntegration.Annotations = map[string]string{ + "camel.apache.org/operator.id": "camel-k", + } + promoteCmdOptions, promoteCmd, _ = initializePromoteCmdOptions(t, &srcPlatform, &dstPlatform, &defaultIntegration, &srcCatalog, &dstCatalog) + output, err = test.ExecuteCommand(promoteCmd, cmdPromote, "my-it-test", "-x", "my-prod-operator", "-o", "yaml", "--to", "prod") + assert.Equal(t, "yaml", promoteCmdOptions.OutputFormat) + assert.Nil(t, err) + assert.Equal(t, `apiVersion: camel.apache.org/v1 +kind: Integration +metadata: + annotations: + camel.apache.org/operator.id: my-prod-operator + creationTimestamp: null + name: my-it-test + namespace: prod +spec: + traits: + container: + image: my-special-image +status: {} +`, output) }
