You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2020/12/03 14:04:38 UTC

[camel-k] branch master updated: Fix #1675: use kamelets in the operator namespace

This is an automated email from the ASF dual-hosted git repository.

nferraro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/master by this push:
     new 66660a3  Fix #1675: use kamelets in the operator namespace
66660a3 is described below

commit 66660a33669ac55d08d773b537a0d728467848ae
Author: nicolaferraro <ni...@gmail.com>
AuthorDate: Tue Dec 1 15:57:36 2020 +0100

    Fix #1675: use kamelets in the operator namespace
---
 deploy/resources.go                                | 16 ++++++
 .../user-global-kamelet-viewer-role-binding.yaml   | 31 +++++++++++
 deploy/user-global-kamelet-viewer-role.yaml        | 32 +++++++++++
 e2e/common/files/timer-kamelet-usage.groovy        | 19 +++++++
 e2e/common/global_kamelet_test.go                  | 63 ++++++++++++++++++++++
 pkg/cmd/operator/operator.go                       |  6 ++-
 pkg/controller/kameletbinding/initialize.go        |  3 +-
 pkg/install/kamelets.go                            |  8 +++
 pkg/install/optional.go                            | 23 ++++++--
 pkg/kamelet/repository/repository.go               | 15 ++++++
 pkg/trait/kamelets.go                              |  5 +-
 11 files changed, 213 insertions(+), 8 deletions(-)

diff --git a/deploy/resources.go b/deploy/resources.go
index 4767929..a67c4fb 100644
--- a/deploy/resources.go
+++ b/deploy/resources.go
@@ -351,6 +351,20 @@ var assets = func() http.FileSystem {
 
 			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x53\xc1\x8e\xdb\x36\x10\xbd\xf3\x2b\x1e\xa4\x4b\x52\xac\xe5\xb6\xa7\x42\x3d\xb9\x9b\xdd\x56\x68\x60\x03\x2b\xa7\x41\x50\xf4\x40\x8b\x63\x69\xb0\x14\xa9\x0e\xa9\x55\xb6\x5f\x5f\x90\xb6\x37\x5e\x14\x3d\x04\x08\x6f\x24\x87\x6f\xde\x9b\xf7\x58\x62\xf5\xed\x96\x2a\xf1\x9e\x3b\x72\x81\x0c\xa2\x47\x1c\x08\x9b\x49\x77\x03\xa1\xf5\xc7\xb8\x68\x21\xdc\xfb\xd9\x19\x1d\xd9\x3b\xbc\xd9\xb4\xf7\x6f\x31\x3b\x43\x02\xef\x08\x5e\x [...]
 		},
+		"/user-global-kamelet-viewer-role-binding.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "user-global-kamelet-viewer-role-binding.yaml",
+			modTime:          time.Time{},
+			uncompressedSize: 1250,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x53\xcd\x6e\xda\x4c\x14\xdd\xcf\x53\x1c\xe1\x4d\x22\x81\xf9\xbe\xae\x2a\x77\x45\x12\x68\xad\x46\x20\x61\xd2\x28\xcb\x8b\x7d\xb1\x6f\xb1\x67\xdc\x99\x71\x1c\xfa\xf4\xd5\x18\xc8\x8f\x2a\xb5\xaa\x94\xd9\x20\x34\x77\xce\xcf\x3d\xc7\x11\x26\xef\x77\x54\x84\x5b\xc9\x59\x3b\x2e\xe0\x0d\x7c\xc5\x98\xb5\x94\x57\x8c\xcc\xec\x7c\x4f\x96\xb1\x30\x9d\x2e\xc8\x8b\xd1\xb8\x98\x65\x8b\x4b\x74\xba\x60\x0b\xa3\x19\xc6\xa2\x31\x96\x [...]
+		},
+		"/user-global-kamelet-viewer-role.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "user-global-kamelet-viewer-role.yaml",
+			modTime:          time.Time{},
+			uncompressedSize: 1166,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x53\x4f\x6f\xfa\x46\x14\xbc\xef\xa7\x18\xe1\x4b\x22\x81\x69\x7b\xaa\xe8\x89\x26\xd0\x5a\x8d\x40\xc2\xa4\x51\x8e\x8b\xfd\xb0\x9f\x58\xef\xba\x6f\xd7\x38\xf4\xd3\x57\x6b\x4c\x43\xf4\xbb\x66\x2f\xeb\x3f\xe3\x37\x33\x3b\xe3\x04\xb3\xef\x5b\x2a\xc1\x0b\x17\x64\x3d\x95\x08\x0e\xa1\x26\x2c\x5b\x5d\xd4\x84\xdc\x1d\x43\xaf\x85\xb0\x76\x9d\x2d\x75\x60\x67\xf1\xb0\xcc\xd7\x8f\xe8\x6c\x49\x02\x67\x09\x4e\xd0\x38\x21\x95\xa0\x [...]
+		},
 	}
 	fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
 		fs["/addons"].(os.FileInfo),
@@ -390,6 +404,8 @@ var assets = func() http.FileSystem {
 		fs["/templates"].(os.FileInfo),
 		fs["/traits.yaml"].(os.FileInfo),
 		fs["/user-cluster-role.yaml"].(os.FileInfo),
+		fs["/user-global-kamelet-viewer-role-binding.yaml"].(os.FileInfo),
+		fs["/user-global-kamelet-viewer-role.yaml"].(os.FileInfo),
 	}
 	fs["/addons"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
 		fs["/addons/master"].(os.FileInfo),
diff --git a/deploy/user-global-kamelet-viewer-role-binding.yaml b/deploy/user-global-kamelet-viewer-role-binding.yaml
new file mode 100644
index 0000000..62fd38a
--- /dev/null
+++ b/deploy/user-global-kamelet-viewer-role-binding.yaml
@@ -0,0 +1,31 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: camel-k-kamelet-viewer
+  labels:
+    app: "camel-k"
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: camel-k-kamelet-viewer
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: Group
+  name: system:authenticated
diff --git a/deploy/user-global-kamelet-viewer-role.yaml b/deploy/user-global-kamelet-viewer-role.yaml
new file mode 100644
index 0000000..80bc061
--- /dev/null
+++ b/deploy/user-global-kamelet-viewer-role.yaml
@@ -0,0 +1,32 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: camel-k-kamelet-viewer
+  labels:
+    app: "camel-k"
+rules:
+- apiGroups:
+  - "camel.apache.org"
+  resources:
+  - kamelets
+  verbs:
+  - get
+  - list
+  - watch
diff --git a/e2e/common/files/timer-kamelet-usage.groovy b/e2e/common/files/timer-kamelet-usage.groovy
new file mode 100644
index 0000000..11ad5bb
--- /dev/null
+++ b/e2e/common/files/timer-kamelet-usage.groovy
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+from('kamelet:my-own-timer-source?message=Hello+world')
+    .to('log:info?showAll=false')
diff --git a/e2e/common/global_kamelet_test.go b/e2e/common/global_kamelet_test.go
new file mode 100644
index 0000000..4886820
--- /dev/null
+++ b/e2e/common/global_kamelet_test.go
@@ -0,0 +1,63 @@
+// +build integration
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "integration"
+
+/*
+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 common
+
+import (
+	"os"
+	"testing"
+
+	. "github.com/apache/camel-k/e2e/support"
+	"github.com/apache/camel-k/pkg/util/openshift"
+	. "github.com/onsi/gomega"
+	"github.com/stretchr/testify/assert"
+	v1 "k8s.io/api/core/v1"
+)
+
+func TestRunGlobalKamelet(t *testing.T) {
+	forceGlobalTest := os.Getenv("CAMEL_K_FORCE_GLOBAL_TEST") == "true"
+	if !forceGlobalTest {
+		ocp, err := openshift.IsOpenShift(TestClient)
+		assert.Nil(t, err)
+		if ocp {
+			t.Skip("Prefer not to run on OpenShift to avoid giving more permissions to the user running tests")
+			return
+		}
+	}
+
+	WithNewTestNamespace(t, func(ns string) {
+		Expect(Kamel("install", "-n", ns, "--global").Execute()).Should(BeNil())
+
+		Expect(CreateTimerKamelet(ns, "my-own-timer-source")()).Should(BeNil())
+
+		// NS2: namespace without operator
+		WithNewTestNamespace(t, func(ns2 string) {
+			Expect(Kamel("install", "-n", ns2, "--skip-operator-setup", "--olm=false").Execute()).Should(BeNil())
+
+			Expect(Kamel("run", "-n", ns2, "files/timer-kamelet-usage.groovy").Execute()).Should(BeNil())
+			Eventually(IntegrationPodPhase(ns2, "timer-kamelet-usage"), TestTimeoutMedium).Should(Equal(v1.PodRunning))
+			Eventually(IntegrationLogs(ns2, "timer-kamelet-usage"), TestTimeoutShort).Should(ContainSubstring("Hello world"))
+			Expect(Kamel("delete", "--all", "-n", ns2).Execute()).Should(BeNil())
+		})
+
+		Expect(Kamel("uninstall", "-n", ns, "--skip-cluster-roles=false", "--skip-cluster-role-bindings=false").Execute()).Should(BeNil())
+	})
+}
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index 2b1cc54..fb4a7c2 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -65,7 +65,8 @@ func printVersion() {
 }
 
 // Run starts the Camel K operator
-func Run(healthPort, monitoringPort int32) {rand.Seed(time.Now().UTC().UnixNano())
+func Run(healthPort, monitoringPort int32) {
+	rand.Seed(time.Now().UTC().UnixNano())
 
 	flag.Parse()
 
@@ -151,7 +152,8 @@ func Run(healthPort, monitoringPort int32) {rand.Seed(time.Now().UTC().UnixNano(
 	// Try to register the OpenShift CLI Download link if possible
 	installCtx, installCancel := context.WithTimeout(context.TODO(), 1*time.Minute)
 	defer installCancel()
-	install.OperatorStartupOptionalTools(installCtx, c, namespace, log)
+	operatorNamespace := os.Getenv("NAMESPACE")
+	install.OperatorStartupOptionalTools(installCtx, c, namespace, operatorNamespace, log)
 
 	// Setup all Controllers
 	if err := controller.AddToManager(mgr); err != nil {
diff --git a/pkg/controller/kameletbinding/initialize.go b/pkg/controller/kameletbinding/initialize.go
index 7235cd9..66fc329 100644
--- a/pkg/controller/kameletbinding/initialize.go
+++ b/pkg/controller/kameletbinding/initialize.go
@@ -23,6 +23,7 @@ import (
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/kamelet/repository"
+	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/util/patch"
 	"github.com/pkg/errors"
@@ -109,7 +110,7 @@ func (action *initializeAction) findIcon(ctx context.Context, binding *v1alpha1.
 		return "", nil
 	}
 
-	repo, err := repository.New(ctx, action.client, binding.Namespace)
+	repo, err := repository.New(ctx, action.client, binding.Namespace, platform.GetOperatorNamespace())
 	if err != nil {
 		return "", err
 	}
diff --git a/pkg/install/kamelets.go b/pkg/install/kamelets.go
index 07fb006..7298820 100644
--- a/pkg/install/kamelets.go
+++ b/pkg/install/kamelets.go
@@ -89,3 +89,11 @@ func KameletCatalog(ctx context.Context, c client.Client, namespace string) erro
 
 	return nil
 }
+
+// KameletViewerRole installs the role that allows any user ro access kamelets in the global namespace
+func KameletViewerRole(ctx context.Context, c client.Client, namespace string) error {
+	if err := Resource(ctx, c, namespace, true, IdentityResourceCustomizer, "user-global-kamelet-viewer-role.yaml"); err != nil {
+		return err
+	}
+	return Resource(ctx, c, namespace, true, IdentityResourceCustomizer, "user-global-kamelet-viewer-role-binding.yaml")
+}
diff --git a/pkg/install/optional.go b/pkg/install/optional.go
index 0571844..e7019f4 100644
--- a/pkg/install/optional.go
+++ b/pkg/install/optional.go
@@ -26,7 +26,7 @@ import (
 )
 
 // OperatorStartupOptionalTools tries to install optional tools at operator startup and warns if something goes wrong
-func OperatorStartupOptionalTools(ctx context.Context, c client.Client, namespace string, log logr.Logger) {
+func OperatorStartupOptionalTools(ctx context.Context, c client.Client, namespace string, operatorNamespace string, log logr.Logger) {
 
 	// Try to register the OpenShift CLI Download link if possible
 	if err := OpenShiftConsoleDownloadLink(ctx, c); err != nil {
@@ -45,12 +45,29 @@ func OperatorStartupOptionalTools(ctx context.Context, c client.Client, namespac
 		}
 	}
 
-	// Try to install Kamelet Catalog automatically if operator is namespace scoped
+	// Try to install Kamelet Catalog automatically
+	var kameletNamespace string
+	globalOperator := false
 	if namespace != "" && !strings.Contains(namespace, ",") {
-		if err := KameletCatalog(ctx, c, namespace); err != nil {
+		kameletNamespace = namespace
+	} else {
+		kameletNamespace = operatorNamespace
+		globalOperator = true
+	}
+
+	if kameletNamespace != "" {
+		if err := KameletCatalog(ctx, c, kameletNamespace); err != nil {
 			log.Info("Cannot install bundled Kamelet Catalog: skipping.")
 			log.V(8).Info("Error while installing bundled Kamelet Catalog", "error", err)
 		}
+
+		if globalOperator {
+			// Make sure that Kamelets installed in operator namespace can be used by others
+			if err := KameletViewerRole(ctx, c, kameletNamespace); err != nil {
+				log.Info("Cannot install global Kamelet viewer role: skipping.")
+				log.V(8).Info("Error while installing global Kamelet viewer role", "error", err)
+			}
+		}
 	}
 
 }
diff --git a/pkg/kamelet/repository/repository.go b/pkg/kamelet/repository/repository.go
index a0f8a1b..721b887 100644
--- a/pkg/kamelet/repository/repository.go
+++ b/pkg/kamelet/repository/repository.go
@@ -53,6 +53,7 @@ type KameletRepository interface {
 // If one namespace defines an IntegrationPlatform (only the first IntegrationPlatform in state "Ready" found),
 // then all kamelet repository URIs defined in the IntegrationPlatform are included.
 func New(ctx context.Context, client camel.Interface, namespaces ...string) (KameletRepository, error) {
+	namespaces = makeDistinctNonEmpty(namespaces)
 	platform, err := lookupPlatform(ctx, client, namespaces...)
 	if err != nil {
 		return nil, err
@@ -64,6 +65,7 @@ func New(ctx context.Context, client camel.Interface, namespaces ...string) (Kam
 // Kamelets are first looked up in all the given namespaces, in the order they appear,
 // then repositories defined in the platform are looked up.
 func NewForPlatform(ctx context.Context, client camel.Interface, platform *v1.IntegrationPlatform, namespaces ...string) (KameletRepository, error) {
+	namespaces = makeDistinctNonEmpty(namespaces)
 	repoImpls := make([]KameletRepository, 0)
 	for _, namespace := range namespaces {
 		// Add first a namespace local repository for each namespace
@@ -176,3 +178,16 @@ func newFromURI(uri string) (KameletRepository, error) {
 	}
 	return nil, fmt.Errorf("invalid uri: %s", uri)
 }
+
+func makeDistinctNonEmpty(names []string) []string {
+	res := make([]string, 0, len(names))
+	presence := make(map[string]bool, len(names))
+	for _, n := range names {
+		if n == "" || presence[n] {
+			continue
+		}
+		presence[n] = true
+		res = append(res, n)
+	}
+	return res
+}
diff --git a/pkg/trait/kamelets.go b/pkg/trait/kamelets.go
index 13c5bee..390c754 100644
--- a/pkg/trait/kamelets.go
+++ b/pkg/trait/kamelets.go
@@ -30,6 +30,7 @@ import (
 	kameletutils "github.com/apache/camel-k/pkg/kamelet"
 	"github.com/apache/camel-k/pkg/kamelet/repository"
 	"github.com/apache/camel-k/pkg/metadata"
+	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util"
 	"github.com/apache/camel-k/pkg/util/digest"
 	"github.com/apache/camel-k/pkg/util/flow"
@@ -132,7 +133,7 @@ func (t *kameletsTrait) Apply(e *Environment) error {
 func (t *kameletsTrait) addKamelets(e *Environment) error {
 	kameletKeys := t.getKameletKeys()
 	if len(kameletKeys) > 0 {
-		repo, err := repository.NewForPlatform(e.C, e.Client, e.Platform, e.Integration.Namespace)
+		repo, err := repository.NewForPlatform(e.C, e.Client, e.Platform, e.Integration.Namespace, platform.GetOperatorNamespace())
 		if err != nil {
 			return err
 		}
@@ -170,7 +171,7 @@ func (t *kameletsTrait) addKamelets(e *Environment) error {
 
 func (t *kameletsTrait) configureApplicationProperties(e *Environment) error {
 	if len(t.getKameletKeys()) > 0 {
-		repo, err := repository.NewForPlatform(e.C, e.Client, e.Platform, e.Integration.Namespace)
+		repo, err := repository.NewForPlatform(e.C, e.Client, e.Platform, e.Integration.Namespace, platform.GetOperatorNamespace())
 		if err != nil {
 			return err
 		}