This is an automated email from the ASF dual-hosted git repository. astefanutti pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit c33e0cc189c5b516f5da524b9a5f88032775fbd1 Author: Christoph Deppisch <cdeppi...@redhat.com> AuthorDate: Wed Jun 8 20:11:44 2022 +0200 Fix #2177: Use operator id in KameletBindings - Introduce operator-id option to kamel CLI bind command - Make sure to use proper operator id when creating the binding - Rebuild Kamelet binding when operator id annotation changes --- .../v1alpha1/kamelet_binding_types_support.go | 9 ++++ pkg/cmd/bind.go | 48 ++++++++++++++++++++++ pkg/cmd/bind_test.go | 19 ++++++++- pkg/controller/kameletbinding/monitor.go | 7 +++- 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go b/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go index d47024609..90c36c821 100644 --- a/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go +++ b/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go @@ -66,6 +66,15 @@ func (c KameletBindingCondition) GetMessage() string { return c.Message } +// SetOperatorID sets the given operator id as an annotation +func (in *KameletBinding) SetOperatorID(operatorID string) { + if in.Annotations == nil { + in.Annotations = make(map[string]string) + } + + in.Annotations[v1.OperatorIDAnnotation] = operatorID +} + // GetCondition returns the condition with the provided type. func (in *KameletBindingStatus) GetCondition(condType KameletBindingConditionType) *KameletBindingCondition { for i := range in.Conditions { diff --git a/pkg/cmd/bind.go b/pkg/cmd/bind.go index 2a1c4ad26..b86b08e88 100644 --- a/pkg/cmd/bind.go +++ b/pkg/cmd/bind.go @@ -25,6 +25,7 @@ import ( v1 "github.com/apache/camel-k/pkg/apis/camel/v1" "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + platformutil "github.com/apache/camel-k/pkg/platform" "github.com/apache/camel-k/pkg/trait" "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/apache/camel-k/pkg/util/reference" @@ -60,6 +61,9 @@ func newCmdBind(rootCmdOptions *RootCmdOptions) (*cobra.Command, *bindCmdOptions cmd.Flags().Bool("skip-checks", false, "Do not verify the binding for compliance with Kamelets and other Kubernetes resources") cmd.Flags().StringArray("step", nil, `Add binding steps as Kubernetes resources. Endpoints are expected in the format "[[apigroup/]version:]kind:[namespace/]name", plain Camel URIs or Kamelet name.`) cmd.Flags().StringArrayP("trait", "t", nil, `Add a trait to the corresponding Integration.`) + cmd.Flags().String("operator-id", "camel-k", "Operator id selected to manage this Kamelet binding.") + cmd.Flags().StringArray("annotation", nil, "Add an annotation to the Kamelet binding. E.g. \"--annotation my.company=hello\"") + cmd.Flags().Bool("force", false, "Force creation of Kamelet binding regardless of potential misconfiguration.") return &cmd, &options } @@ -81,6 +85,9 @@ type bindCmdOptions struct { SkipChecks bool `mapstructure:"skip-checks" yaml:",omitempty"` Steps []string `mapstructure:"steps" yaml:",omitempty"` Traits []string `mapstructure:"traits" yaml:",omitempty"` + OperatorId string `mapstructure:"operator-id" yaml:",omitempty"` + Annotations []string `mapstructure:"annotations" yaml:",omitempty"` + Force bool `mapstructure:"force" yaml:",omitempty"` } func (o *bindCmdOptions) preRunE(cmd *cobra.Command, args []string) error { @@ -109,6 +116,17 @@ func (o *bindCmdOptions) validate(cmd *cobra.Command, args []string) error { return errors.New("source or sink arguments are missing") } + if o.OperatorId == "" { + return fmt.Errorf("cannot use empty operator id") + } + + for _, annotation := range o.Annotations { + parts := strings.SplitN(annotation, "=", 2) + if len(parts) != 2 { + return fmt.Errorf(`invalid annotation specification %s. Expected "<annotationkey>=<annotationvalue>"`, annotation) + } + } + for _, p := range o.Properties { if _, _, _, err := o.parseProperty(p); err != nil { return err @@ -215,6 +233,36 @@ func (o *bindCmdOptions) run(cmd *cobra.Command, args []string) error { } } + if binding.Annotations == nil { + binding.Annotations = make(map[string]string) + } + + if o.OperatorId != "" { + if pl, err := platformutil.LookupForPlatformName(o.Context, client, o.OperatorId); err != nil { + if k8serrors.IsForbidden(err) { + o.PrintfVerboseOutf(cmd, "Unable to verify existence of operator id [%s] due to lack of user privileges\n", o.OperatorId) + } else { + return err + } + } else if pl == nil { + if o.Force { + o.PrintfVerboseOutf(cmd, "Unable to find operator with given id [%s] - Kamelet binding may not be reconciled and get stuck in waiting state\n", o.OperatorId) + } else { + return errors.New(fmt.Sprintf("unable to find integration platform for given operator id '%s', use --force option or make sure to use a proper operator id", o.OperatorId)) + } + } + } + + // --operator-id={id} is a syntax sugar for '--annotation camel.apache.org/operator.id={id}' + binding.SetOperatorID(strings.TrimSpace(o.OperatorId)) + + for _, annotation := range o.Annotations { + parts := strings.SplitN(annotation, "=", 2) + if len(parts) == 2 { + binding.Annotations[parts[0]] = parts[1] + } + } + if o.OutputFormat != "" { return showOutput(cmd, &binding, o.OutputFormat, client.GetScheme()) } diff --git a/pkg/cmd/bind_test.go b/pkg/cmd/bind_test.go index aa5778a5c..e4583b7bd 100644 --- a/pkg/cmd/bind_test.go +++ b/pkg/cmd/bind_test.go @@ -20,6 +20,8 @@ package cmd import ( "testing" + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + "github.com/apache/camel-k/pkg/platform" "github.com/apache/camel-k/pkg/util/test" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" @@ -31,7 +33,10 @@ const cmdBind = "bind" func initializeBindCmdOptions(t *testing.T) (*bindCmdOptions, *cobra.Command, RootCmdOptions) { t.Helper() - options, rootCmd := kamelTestPreAddCommandInit() + defaultIntegrationPlatform := v1.NewIntegrationPlatform("default", platform.DefaultPlatformName) + fakeClient, _ := test.NewFakeClient(&defaultIntegrationPlatform) + + options, rootCmd := kamelTestPreAddCommandInitWithClient(fakeClient) bindCmdOptions := addTestBindCmd(*options, rootCmd) kamelTestPostAddCommandInit(t, rootCmd) @@ -52,7 +57,7 @@ func TestBindOutputJSON(t *testing.T) { assert.Equal(t, "json", buildCmdOptions.OutputFormat) assert.Nil(t, err) - assert.Equal(t, `{"kind":"KameletBinding","apiVersion":"camel.apache.org/v1alpha1","metadata":{"name":"my-to-my","creationTimestamp":null},"spec":{"source":{"uri":"my:src"},"sink":{"uri":"my:dst"}},"status":{}}`, output) + assert.Equal(t, `{"kind":"KameletBinding","apiVersion":"camel.apache.org/v1alpha1","metadata":{"name":"my-to-my","creationTimestamp":null,"annotations":{"camel.apache.org/operator.id":"camel-k"}},"spec":{"source":{"uri":"my:src"},"sink":{"uri":"my:dst"}},"status":{}}`, output) } func TestBindOutputYAML(t *testing.T) { @@ -64,6 +69,8 @@ func TestBindOutputYAML(t *testing.T) { assert.Equal(t, `apiVersion: camel.apache.org/v1alpha1 kind: KameletBinding metadata: + annotations: + camel.apache.org/operator.id: camel-k creationTimestamp: null name: my-to-my spec: @@ -93,6 +100,8 @@ func TestBindErrorHandlerDLCKamelet(t *testing.T) { assert.Equal(t, `apiVersion: camel.apache.org/v1alpha1 kind: KameletBinding metadata: + annotations: + camel.apache.org/operator.id: camel-k creationTimestamp: null name: my-to-my spec: @@ -123,6 +132,8 @@ func TestBindErrorHandlerNone(t *testing.T) { assert.Equal(t, `apiVersion: camel.apache.org/v1alpha1 kind: KameletBinding metadata: + annotations: + camel.apache.org/operator.id: camel-k creationTimestamp: null name: my-to-my spec: @@ -146,6 +157,8 @@ func TestBindErrorHandlerLog(t *testing.T) { assert.Equal(t, `apiVersion: camel.apache.org/v1alpha1 kind: KameletBinding metadata: + annotations: + camel.apache.org/operator.id: camel-k creationTimestamp: null name: my-to-my spec: @@ -168,6 +181,8 @@ func TestBindTraits(t *testing.T) { assert.Equal(t, `apiVersion: camel.apache.org/v1alpha1 kind: KameletBinding metadata: + annotations: + camel.apache.org/operator.id: camel-k creationTimestamp: null name: my-to-my spec: diff --git a/pkg/controller/kameletbinding/monitor.go b/pkg/controller/kameletbinding/monitor.go index 5c9b599aa..d6465573c 100644 --- a/pkg/controller/kameletbinding/monitor.go +++ b/pkg/controller/kameletbinding/monitor.go @@ -73,13 +73,18 @@ func (action *monitorAction) Handle(ctx context.Context, kameletbinding *v1alpha return nil, errors.Wrapf(err, "could not load integration for KameletBinding %q", kameletbinding.Name) } + operatorIdChanged := v1.GetOperatorIDAnnotation(kameletbinding) != "" && + (v1.GetOperatorIDAnnotation(kameletbinding) != v1.GetOperatorIDAnnotation(&it)) + // Check if the integration needs to be changed expected, err := CreateIntegrationFor(ctx, action.client, kameletbinding) if err != nil { return nil, err } - if !equality.Semantic.DeepDerivative(expected.Spec, it.Spec) { + if !equality.Semantic.DeepDerivative(expected.Spec, it.Spec) || operatorIdChanged { + action.L.Info("Monitor: KameletBinding needs a rebuild") + // KameletBinding has changed and needs rebuild target := kameletbinding.DeepCopy() // Rebuild the integration