This is an automated email from the ASF dual-hosted git repository. nfilotto pushed a commit to branch 2850/add-custom-kamelet-catalog in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit f4facf6e6e444a70ef2795ba24a0aadae47b4e44 Author: Nicolas Filotto <nfilo...@talend.com> AuthorDate: Thu Sep 8 19:18:31 2022 +0200 feat(cli): Add add-repo command to add a repo for custom Kamelet catalog --- .../files/TimerCustomKameletIntegration.java | 28 ++++++ e2e/global/common/kamelet_test.go | 46 ++++++--- pkg/cmd/kamelet.go | 1 + pkg/cmd/kamelet_add_repo.go | 111 +++++++++++++++++++++ pkg/cmd/kamelet_get.go | 5 +- pkg/cmd/kit_get.go | 5 +- 6 files changed, 176 insertions(+), 20 deletions(-) diff --git a/e2e/global/common/files/TimerCustomKameletIntegration.java b/e2e/global/common/files/TimerCustomKameletIntegration.java new file mode 100644 index 000000000..9f749d834 --- /dev/null +++ b/e2e/global/common/files/TimerCustomKameletIntegration.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.Exception; +import java.lang.Override; +import org.apache.camel.builder.RouteBuilder; + +public class TimerCustomKameletIntegration extends RouteBuilder { + @Override + public void configure() throws Exception { + from("kamelet:timer-custom-source?message=great%20message") + .to("log:info"); + } +} diff --git a/e2e/global/common/kamelet_test.go b/e2e/global/common/kamelet_test.go index d255f7319..c9e08e251 100644 --- a/e2e/global/common/kamelet_test.go +++ b/e2e/global/common/kamelet_test.go @@ -41,18 +41,40 @@ func TestKameletClasspathLoading(t *testing.T) { Eventually(Kamelet(kameletName, ns)).Should(BeNil()) - Expect(KamelRunWithID(operatorID, ns, "files/TimerKameletIntegration.java", "-t", "kamelets.enabled=false", - "--resource", "file:files/timer-source.kamelet.yaml@/kamelets/timer-source.kamelet.yaml", - "-p camel.component.kamelet.location=file:/kamelets", - "-d", "camel:yaml-dsl", - // kamelet dependencies - "-d", "camel:timer").Execute()).To(Succeed()) - Eventually(IntegrationPodPhase(ns, "timer-kamelet-integration"), TestTimeoutLong).Should(Equal(corev1.PodRunning)) - - Eventually(IntegrationLogs(ns, "timer-kamelet-integration")).Should(ContainSubstring("important message")) - - // Cleanup - Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil()) + // Basic + t.Run("test basic case", func(t *testing.T) { + + Expect(KamelRunWithID(operatorID, ns, "files/TimerKameletIntegration.java", "-t", "kamelets.enabled=false", + "--resource", "file:files/timer-source.kamelet.yaml@/kamelets/timer-source.kamelet.yaml", + "-p camel.component.kamelet.location=file:/kamelets", + "-d", "camel:yaml-dsl", + // kamelet dependencies + "-d", "camel:timer").Execute()).To(Succeed()) + Eventually(IntegrationPodPhase(ns, "timer-kamelet-integration"), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + + Eventually(IntegrationLogs(ns, "timer-kamelet-integration")).Should(ContainSubstring("important message")) + + // Cleanup + Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil()) + }) + + // Custom repo + t.Run("test custom Kamelet repository", func(t *testing.T) { + + // Add the custom repository + Expect(Kamel("kamelet", "add-repo", "github:essobedo/camel-k-test/kamelets", "-n", ns, "-x", operatorID).Execute()).To(Succeed()) + + Expect(KamelRunWithID(operatorID, ns, "files/TimerCustomKameletIntegration.java", + "-d", "camel:yaml-dsl", + // kamelet dependencies + "-d", "camel:timer").Execute()).To(Succeed()) + Eventually(IntegrationPodPhase(ns, "timer-custom-kamelet-integration"), TestTimeoutLong).Should(Equal(corev1.PodRunning)) + + Eventually(IntegrationLogs(ns, "timer-custom-kamelet-integration")).Should(ContainSubstring("great message")) + + // Cleanup + Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil()) + }) }) } diff --git a/pkg/cmd/kamelet.go b/pkg/cmd/kamelet.go index 89a09062c..7645fd0c2 100644 --- a/pkg/cmd/kamelet.go +++ b/pkg/cmd/kamelet.go @@ -30,6 +30,7 @@ func newCmdKamelet(rootCmdOptions *RootCmdOptions) *cobra.Command { cmd.AddCommand(cmdOnly(newKameletGetCmd(rootCmdOptions))) cmd.AddCommand(cmdOnly(newKameletDeleteCmd(rootCmdOptions))) + cmd.AddCommand(cmdOnly(newKameletAddRepoCmd(rootCmdOptions))) return &cmd } diff --git a/pkg/cmd/kamelet_add_repo.go b/pkg/cmd/kamelet_add_repo.go new file mode 100644 index 000000000..c288ff2b9 --- /dev/null +++ b/pkg/cmd/kamelet_add_repo.go @@ -0,0 +1,111 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "errors" + "fmt" + "regexp" + + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + "github.com/spf13/cobra" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// kameletRepositoryURIRegexp is the regular expression used to validate the URI of a Kamelet repository. +var kameletRepositoryURIRegexp = regexp.MustCompile(`^github:[^/]+/[^/]+((/[^/]+)*)?$`) + +func newKameletAddRepoCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kameletAddRepoCommandOptions) { + options := kameletAddRepoCommandOptions{ + RootCmdOptions: rootCmdOptions, + } + + cmd := cobra.Command{ + Use: "add-repo github:owner/repo[/path_to_kamelets_folder][@version] ...", + Short: "Add a Kamelet repository", + Long: `Add a Kamelet repository.`, + PreRunE: decode(&options), + RunE: func(cmd *cobra.Command, args []string) error { + if err := options.validate(args); err != nil { + return err + } + return options.run(cmd, args) + }, + } + + cmd.Flags().StringP("operator-id", "x", "camel-k", "Id of the Operator to update.") + + return &cmd, &options +} + +type kameletAddRepoCommandOptions struct { + *RootCmdOptions + OperatorID string `mapstructure:"operator-id" yaml:",omitempty"` +} + +func (o *kameletAddRepoCommandOptions) validate(args []string) error { + if len(args) < 1 { + return errors.New("at least one Kamelet repository is expected") + } + if o.OperatorID == "" { + return fmt.Errorf("cannot use empty operator id") + } + return nil +} + +func (o *kameletAddRepoCommandOptions) run(cmd *cobra.Command, args []string) error { + c, err := o.GetCmdClient() + if err != nil { + return err + } + key := client.ObjectKey{ + Namespace: o.Namespace, + Name: o.OperatorID, + } + platform := v1.IntegrationPlatform{} + if err := c.Get(o.Context, key, &platform); err != nil { + if k8serrors.IsNotFound(err) { + // IntegrationPlatform may be in the operator namespace, but we currently don't have a way to determine it: we just warn + fmt.Fprintf(cmd.ErrOrStderr(), "Warning: IntegrationPlatform %q not found in namespace %q\n", key.Name, key.Namespace) + return nil + } + return err + } + for _, uri := range args { + if err := checkURI(uri, platform.Spec.Kamelet.Repositories); err != nil { + return err + } + platform.Spec.Kamelet.Repositories = append(platform.Spec.Kamelet.Repositories, v1.IntegrationPlatformKameletRepositorySpec{ + URI: uri, + }) + } + return c.Update(o.Context, &platform) +} + +func checkURI(uri string, repositories []v1.IntegrationPlatformKameletRepositorySpec) error { + if !kameletRepositoryURIRegexp.MatchString(uri) { + return fmt.Errorf("malformed Kamelet repository uri %s, the expected format is github:owner/repo[/path_to_kamelets_folder][@version]", uri) + } + for _, repo := range repositories { + if repo.URI == uri { + return fmt.Errorf("duplicate Kamelet repository uri %s", uri) + } + } + return nil +} diff --git a/pkg/cmd/kamelet_get.go b/pkg/cmd/kamelet_get.go index 013e92c0f..ced23216a 100644 --- a/pkg/cmd/kamelet_get.go +++ b/pkg/cmd/kamelet_get.go @@ -44,11 +44,8 @@ func newKameletGetCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kameletG if err := options.validate(); err != nil { return err } - if err := options.run(cmd); err != nil { - fmt.Fprintln(cmd.ErrOrStderr(), err.Error()) - } - return nil + return options.run(cmd) }, } diff --git a/pkg/cmd/kit_get.go b/pkg/cmd/kit_get.go index e74864897..47f78268a 100644 --- a/pkg/cmd/kit_get.go +++ b/pkg/cmd/kit_get.go @@ -42,11 +42,8 @@ func newKitGetCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kitGetComman if err := options.validate(cmd, args); err != nil { return err } - if err := options.run(cmd); err != nil { - fmt.Fprintln(cmd.ErrOrStderr(), err.Error()) - } - return nil + return options.run(cmd) }, }