lburgazzoli closed pull request #6: Adding install and get commands
URL: https://github.com/apache/camel-k/pull/6
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/build/Makefile b/build/Makefile
index d8d2b7a..eb1a16c 100644
--- a/build/Makefile
+++ b/build/Makefile
@@ -1,6 +1,6 @@
 VERSION := $(shell ./build/get_version.sh)
 
-build: build-runtime build-operator build-kamel
+build: build-runtime build-embed-resources build-operator build-kamel
 
 build-operator:
        go build -o camel-k-operator ./cmd/camel-k-operator/*.go
@@ -8,6 +8,9 @@ build-operator:
 build-kamel:
        go build -o kamel ./cmd/kamel/*.go
 
+build-embed-resources:
+       ./build/embed_resources.sh deploy
+
 build-runtime:
        mvn clean install -f ./runtime/pom.xml
 
diff --git a/build/embed_resources.sh b/build/embed_resources.sh
new file mode 100755
index 0000000..3ca6385
--- /dev/null
+++ b/build/embed_resources.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+if [[ $# -ne 1 ]] ; then
+    echo "Error invoking embed_resources.sh: directory argument required"
+    exit 1
+fi
+
+location=$(dirname $0)
+destdir=$location/../$1
+destfile=$location/../$1/resources.go
+
+cat > $destfile << EOM
+/*
+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.
+*/
+
+// Code generated by build/embed_resources.sh. DO NOT EDIT.
+
+package deploy
+
+var Resources map[string]string
+
+func init() {
+       Resources = make(map[string]string)
+
+EOM
+
+for f in $(ls $destdir | grep ".yaml"); do
+       printf "Resources[\"$f\"] =\n\`\n" >> $destfile
+       cat $destdir/$f >> $destfile
+       printf "\n\`\n" >> $destfile
+done
+
+printf "\n}\n" >> $destfile
\ No newline at end of file
diff --git a/deploy/operator-role-binding.yaml 
b/deploy/operator-role-binding.yaml
new file mode 100644
index 0000000..eb4c712
--- /dev/null
+++ b/deploy/operator-role-binding.yaml
@@ -0,0 +1,11 @@
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: camel-k-operator
+subjects:
+- kind: ServiceAccount
+  name: camel-k-operator
+roleRef:
+  kind: Role
+  name: camel-k-operator
+  apiGroup: rbac.authorization.k8s.io
\ No newline at end of file
diff --git a/deploy/rbac.yaml b/deploy/operator-role.yaml
similarity index 60%
rename from deploy/rbac.yaml
rename to deploy/operator-role.yaml
index e1cb15f..7dea8d3 100644
--- a/deploy/rbac.yaml
+++ b/deploy/operator-role.yaml
@@ -1,7 +1,7 @@
 kind: Role
 apiVersion: rbac.authorization.k8s.io/v1beta1
 metadata:
-  name: camel-k
+  name: camel-k-operator
 rules:
 - apiGroups:
   - camel.apache.org
@@ -29,18 +29,4 @@ rules:
   - replicasets
   - statefulsets
   verbs:
-  - "*"
-
----
-
-kind: RoleBinding
-apiVersion: rbac.authorization.k8s.io/v1beta1
-metadata:
-  name: default-account-camel-k
-subjects:
-- kind: ServiceAccount
-  name: default
-roleRef:
-  kind: Role
-  name: camel-k
-  apiGroup: rbac.authorization.k8s.io
+  - "*"
\ No newline at end of file
diff --git a/deploy/operator-service-account.yaml 
b/deploy/operator-service-account.yaml
new file mode 100644
index 0000000..eb771aa
--- /dev/null
+++ b/deploy/operator-service-account.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: camel-k-operator
\ No newline at end of file
diff --git a/deploy/operator.yaml b/deploy/operator.yaml
index 863b21c..265b76d 100644
--- a/deploy/operator.yaml
+++ b/deploy/operator.yaml
@@ -2,6 +2,8 @@ apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: camel-k-operator
+  labels:
+    app: "camel-k"
 spec:
   replicas: 1
   selector:
@@ -12,6 +14,7 @@ spec:
       labels:
         name: camel-k-operator
     spec:
+      serviceAccountName: camel-k-operator
       containers:
         - name: camel-k-operator
           image: docker.io/apache/camel-k:0.0.1-SNAPSHOT
@@ -20,7 +23,7 @@ spec:
             name: metrics
           command:
           - camel-k-operator
-          imagePullPolicy: Always
+          imagePullPolicy: IfNotPresent
           env:
             - name: WATCH_NAMESPACE
               valueFrom:
diff --git a/deploy/resources.go b/deploy/resources.go
new file mode 100644
index 0000000..1048333
--- /dev/null
+++ b/deploy/resources.go
@@ -0,0 +1,180 @@
+/*
+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.
+*/
+
+// Code generated by build/embed_resources.sh. DO NOT EDIT.
+
+package deploy
+
+var Resources map[string]string
+
+func init() {
+       Resources = make(map[string]string)
+
+Resources["crd.yaml"] =
+`
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: integrations.camel.apache.org
+spec:
+  group: camel.apache.org
+  names:
+    kind: Integration
+    listKind: IntegrationList
+    plural: integrations
+    singular: integration
+  scope: Namespaced
+  version: v1alpha1
+
+`
+Resources["cr.yaml"] =
+`
+apiVersion: "camel.apache.org/v1alpha1"
+kind: "Integration"
+metadata:
+  name: "example"
+spec:
+  replicas: 1
+  source:
+    code: |-
+      package kamel;
+
+      import org.apache.camel.builder.RouteBuilder;
+
+      public class Routes extends RouteBuilder {
+
+          @Override
+          public void configure() throws Exception {
+              from("timer:tick")
+                .setBody(constant("Hello World!!!"))
+                .to("log:info");
+          }
+
+      }
+
+`
+Resources["operator-role-binding.yaml"] =
+`
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: camel-k-operator
+subjects:
+- kind: ServiceAccount
+  name: camel-k-operator
+roleRef:
+  kind: Role
+  name: camel-k-operator
+  apiGroup: rbac.authorization.k8s.io
+`
+Resources["operator-role.yaml"] =
+`
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: camel-k-operator
+rules:
+- apiGroups:
+  - camel.apache.org
+  resources:
+  - "*"
+  verbs:
+  - "*"
+- apiGroups:
+  - ""
+  resources:
+  - pods
+  - services
+  - endpoints
+  - persistentvolumeclaims
+  - events
+  - configmaps
+  - secrets
+  verbs:
+  - "*"
+- apiGroups:
+  - apps
+  resources:
+  - deployments
+  - daemonsets
+  - replicasets
+  - statefulsets
+  verbs:
+  - "*"
+`
+Resources["operator-service-account.yaml"] =
+`
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: camel-k-operator
+`
+Resources["operator.yaml"] =
+`
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: camel-k-operator
+  labels:
+    app: "camel-k"
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      name: camel-k-operator
+  template:
+    metadata:
+      labels:
+        name: camel-k-operator
+    spec:
+      serviceAccountName: camel-k-operator
+      containers:
+        - name: camel-k-operator
+          image: docker.io/apache/camel-k:0.0.1-SNAPSHOT
+          ports:
+          - containerPort: 60000
+            name: metrics
+          command:
+          - camel-k-operator
+          imagePullPolicy: IfNotPresent
+          env:
+            - name: WATCH_NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
+            - name: OPERATOR_NAME
+              value: "camel-k-operator"
+
+`
+Resources["user-cluster-role.yaml"] =
+`
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: camel-k:edit
+  labels:
+    # Add these permissions to the "admin" and "edit" default roles.
+    rbac.authorization.k8s.io/aggregate-to-admin: "true"
+    rbac.authorization.k8s.io/aggregate-to-edit: "true"
+rules:
+- apiGroups: ["camel.apache.org"]
+  resources: ["*"]
+  verbs: ["*"]
+
+`
+
+}
diff --git a/deploy/user-cluster-role.yaml b/deploy/user-cluster-role.yaml
new file mode 100644
index 0000000..2fde6d1
--- /dev/null
+++ b/deploy/user-cluster-role.yaml
@@ -0,0 +1,12 @@
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: camel-k:edit
+  labels:
+    # Add these permissions to the "admin" and "edit" default roles.
+    rbac.authorization.k8s.io/aggregate-to-admin: "true"
+    rbac.authorization.k8s.io/aggregate-to-edit: "true"
+rules:
+- apiGroups: ["camel.apache.org"]
+  resources: ["*"]
+  verbs: ["*"]
diff --git a/pkg/build/local/local_builder.go b/pkg/build/local/local_builder.go
index 98830d6..d37a56e 100644
--- a/pkg/build/local/local_builder.go
+++ b/pkg/build/local/local_builder.go
@@ -27,9 +27,6 @@ import (
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/api/core/v1"
        "github.com/operator-framework/operator-sdk/pkg/sdk"
-       "k8s.io/client-go/rest"
-       "github.com/operator-framework/operator-sdk/pkg/k8sclient"
-       "k8s.io/apimachinery/pkg/runtime/schema"
        imagev1 "github.com/openshift/api/image/v1"
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
        "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
@@ -39,6 +36,7 @@ import (
        "github.com/apache/camel-k/pkg/util/kubernetes"
        "github.com/apache/camel-k/version"
        "github.com/apache/camel-k/pkg/util/maven"
+       "github.com/apache/camel-k/pkg/util/kubernetes/customclient"
 )
 
 type localBuilder struct {
@@ -203,34 +201,17 @@ func (b *localBuilder) publish(tarFile string, source 
build.BuildSource) (string
                return "", errors.Wrap(err, "cannot create image stream")
        }
 
-       inConfig := k8sclient.GetKubeConfig()
-       config := rest.CopyConfig(inConfig)
-       config.GroupVersion = &schema.GroupVersion{
-               Group:   "build.openshift.io",
-               Version: "v1",
-       }
-       config.APIPath = "/apis"
-       config.AcceptContentTypes = "application/json"
-       config.ContentType = "application/json"
-
-       // this gets used for discovery and error handling types
-       config.NegotiatedSerializer = basicNegotiatedSerializer{}
-       if config.UserAgent == "" {
-               config.UserAgent = rest.DefaultKubernetesUserAgent()
-       }
-
-       restClient, err := rest.RESTClientFor(config)
+       resource, err := ioutil.ReadFile(tarFile)
        if err != nil {
-               return "", err
+               return "", errors.Wrap(err, "cannot fully read tar file 
"+tarFile)
        }
 
-       resource, err := ioutil.ReadFile(tarFile)
+       restClient, err := customclient.GetClientFor("build.openshift.io", "v1")
        if err != nil {
-               return "", errors.Wrap(err, "cannot fully read tar file 
"+tarFile)
+               return "", err
        }
 
-       result := restClient.
-               Post().
+       result := restClient.Post().
                Namespace(b.namespace).
                Body(resource).
                Resource("buildconfigs").
diff --git a/pkg/client/cmd/get.go b/pkg/client/cmd/get.go
new file mode 100644
index 0000000..39a45d9
--- /dev/null
+++ b/pkg/client/cmd/get.go
@@ -0,0 +1,64 @@
+/*
+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 (
+       "github.com/spf13/cobra"
+       "text/tabwriter"
+       "os"
+       "fmt"
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "github.com/operator-framework/operator-sdk/pkg/sdk"
+)
+
+func NewCmdGet() *cobra.Command {
+       cmd := cobra.Command{
+               Use:   "get",
+               Short: "Get all integrations deployed on Kubernetes",
+               Long:  `Get the status of all integrations deployed on on 
Kubernetes.`,
+               RunE:  run,
+       }
+
+       return &cmd
+}
+
+func run(cmd *cobra.Command, args []string) error {
+       integrationList := v1alpha1.IntegrationList{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: v1alpha1.SchemeGroupVersion.String(),
+                       Kind:       "Integration",
+               },
+       }
+
+       namespace := cmd.Flag("namespace").Value.String()
+
+       err := sdk.List(namespace, &integrationList)
+       if err != nil {
+               return err
+       }
+
+       w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0)
+       fmt.Fprintln(w, "NAME\tSTATUS")
+       for _, integration := range integrationList.Items {
+               fmt.Fprintln(w, 
integration.Name+"\t"+string(integration.Status.Phase))
+       }
+       w.Flush()
+
+       return nil
+}
diff --git a/pkg/client/cmd/install.go b/pkg/client/cmd/install.go
new file mode 100644
index 0000000..edeece4
--- /dev/null
+++ b/pkg/client/cmd/install.go
@@ -0,0 +1,58 @@
+/*
+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 (
+       "github.com/spf13/cobra"
+       installutils "github.com/apache/camel-k/pkg/install"
+       "fmt"
+       "k8s.io/apimachinery/pkg/api/errors"
+)
+
+type InstallCmdOptions struct {
+}
+
+func NewCmdInstall() *cobra.Command {
+       options := InstallCmdOptions{}
+       cmd := cobra.Command{
+               Use:   "install",
+               Short: "Install Camel K on a Kubernetes cluster",
+               Long:  `Installs Camel K on a Kubernetes or Openshift cluster.`,
+               RunE:  options.install,
+       }
+       return &cmd
+}
+
+func (o *InstallCmdOptions) install(cmd *cobra.Command, args []string) error {
+       err := installutils.SetupClusterwideResources()
+       if err != nil && errors.IsForbidden(err) {
+               // TODO explain that this is a one time operation and add a 
flag to do cluster-level operations only when logged as admin
+               fmt.Println("Current user is not authorized to create 
cluster-wide objects like custom resource definitions or cluster roles: ", err)
+               fmt.Println("Please login as cluster-admin to continue the 
installation.")
+               return nil // TODO better error handling: if here we return err 
the help page is shown
+       }
+
+       namespace := cmd.Flag("namespace").Value.String()
+
+       err = installutils.InstallOperator(namespace)
+       if err != nil {
+               return err
+       }
+       fmt.Println("Camel K installed in namespace", namespace)
+       return nil
+}
diff --git a/pkg/client/cmd/root.go b/pkg/client/cmd/root.go
index e49da28..f310f10 100644
--- a/pkg/client/cmd/root.go
+++ b/pkg/client/cmd/root.go
@@ -18,21 +18,42 @@ limitations under the License.
 package cmd
 
 import (
+       "os"
+
        "github.com/spf13/cobra"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       "github.com/pkg/errors"
 )
 
+type rootCmdOptions struct {
+       KubeConfig string
+       Namespace  string
+}
+
 func NewKamelCommand() (*cobra.Command, error) {
+       options := rootCmdOptions{}
        var cmd = cobra.Command{
                Use:   "kamel",
                Short: "Kamel is a awesome client tool for running Apache Camel 
integrations natively on Kubernetes",
                Long:  "Apache Camel K (a.k.a. Kamel) is a lightweight 
integration framework\nbuilt from Apache Camel that runs natively on Kubernetes 
and is\nspecifically designed for serverless and microservice architectures.",
        }
 
-       var kubeconfig string
-       cmd.PersistentFlags().StringVar(&kubeconfig, "config", "", "Path to the 
config file to use for CLI requests")
+       cmd.PersistentFlags().StringVar(&options.KubeConfig, "config", "", 
"Path to the config file to use for CLI requests")
+       cmd.PersistentFlags().StringVarP(&options.Namespace, "namespace", "n", 
"", "Namespace to use for all operations")
+
+       // Parse the flags before setting the defaults
+       cmd.ParseFlags(os.Args)
+
+       if options.Namespace == "" {
+               current, err := 
kubernetes.GetClientCurrentNamespace(options.KubeConfig)
+               if err != nil {
+                       return nil, errors.Wrap(err, "cannot get current 
namespace")
+               }
+               cmd.Flag("namespace").Value.Set(current)
+       }
 
        // Initialize the Kubernetes client to allow using the operator-sdk
-       err := initKubeClient(&cmd)
+       err := kubernetes.InitKubeClient(options.KubeConfig)
        if err != nil {
                return nil, err
        }
@@ -40,6 +61,8 @@ func NewKamelCommand() (*cobra.Command, error) {
        cmd.AddCommand(NewCmdCompletion())
        cmd.AddCommand(NewCmdVersion())
        cmd.AddCommand(NewCmdRun())
+       cmd.AddCommand(NewCmdGet())
+       cmd.AddCommand(NewCmdInstall())
 
        return &cmd, nil
 }
diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index ca0872a..1fb90d4 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -32,27 +32,27 @@ import (
        "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-func NewCmdRun() *cobra.Command {
-       impl := runCmd{}
+type RunCmdOptions struct {
+       Language string
+}
 
+func NewCmdRun() *cobra.Command {
+       options := RunCmdOptions{}
        cmd := cobra.Command{
                Use:   "run [file to run]",
                Short: "Run a integration on Kubernetes",
                Long:  `Deploys and execute a integration pod on Kubernetes.`,
-               Args:  impl.validateArgs,
-               RunE:  impl.execute,
+               Args:  options.validateArgs,
+               RunE:  options.run,
        }
 
-       cmd.Flags().StringVarP(&impl.language, "language", "l", "", 
"Programming Language used to write the file")
+       cmd.Flags().StringVarP(&options.Language, "language", "l", "", 
"Programming Language used to write the file")
+       cmd.ParseFlags(os.Args)
 
        return &cmd
 }
 
-type runCmd struct {
-       language string
-}
-
-func (target runCmd) validateArgs(cmd *cobra.Command, args []string) error {
+func (*RunCmdOptions) validateArgs(cmd *cobra.Command, args []string) error {
        if len(args) != 1 {
                return errors.New("accepts 1 arg, received " + 
strconv.Itoa(len(args)))
        }
@@ -65,12 +65,14 @@ func (target runCmd) validateArgs(cmd *cobra.Command, args 
[]string) error {
        return nil
 }
 
-func (target runCmd) execute(cmd *cobra.Command, args []string) error {
-       code, err := target.loadCode(args[0])
+func (o *RunCmdOptions) run(cmd *cobra.Command, args []string) error {
+       code, err := o.loadCode(args[0])
        if err != nil {
                return err
        }
 
+       namespace := cmd.Flag("namespace").Value.String()
+
        name := kubernetes.SanitizeName(args[0])
        if name == "" {
                name = "integration"
@@ -82,7 +84,7 @@ func (target runCmd) execute(cmd *cobra.Command, args 
[]string) error {
                        APIVersion: v1alpha1.SchemeGroupVersion.String(),
                },
                ObjectMeta: v1.ObjectMeta{
-                       Namespace: "test", // TODO discover current namespace 
dynamically (and with command option)
+                       Namespace: namespace,
                        Name:      name,
                },
                Spec: v1alpha1.IntegrationSpec{
@@ -117,7 +119,7 @@ func (target runCmd) execute(cmd *cobra.Command, args 
[]string) error {
        return nil
 }
 
-func (target runCmd) loadCode(fileName string) (string, error) {
+func (*RunCmdOptions) loadCode(fileName string) (string, error) {
        content, err := ioutil.ReadFile(fileName)
        if err != nil {
                return "", err
diff --git a/pkg/install/cluster.go b/pkg/install/cluster.go
new file mode 100644
index 0000000..a6b60d5
--- /dev/null
+++ b/pkg/install/cluster.go
@@ -0,0 +1,124 @@
+/*
+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 install
+
+import (
+       "github.com/operator-framework/operator-sdk/pkg/k8sclient"
+       "github.com/apache/camel-k/deploy"
+       "k8s.io/apimachinery/pkg/util/yaml"
+       "github.com/apache/camel-k/pkg/util/kubernetes/customclient"
+       "k8s.io/apimachinery/pkg/api/errors"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       "github.com/operator-framework/operator-sdk/pkg/sdk"
+       "k8s.io/api/rbac/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func SetupClusterwideResources() error {
+       // Installing CRD
+       crdInstalled, err := isCRDInstalled()
+       if err != nil {
+               return err
+       }
+       if !crdInstalled {
+               if err := installCRD(); err != nil {
+                       return err
+               }
+       }
+
+       // Installing ClusterRole
+       clusterRoleInstalled, err := isClusterRoleInstalled()
+       if err != nil {
+               return err
+       }
+       if !clusterRoleInstalled {
+               err := installClusterRole()
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func isCRDInstalled() (bool, error) {
+       lst, err := 
k8sclient.GetKubeClient().Discovery().ServerResourcesForGroupVersion("camel.apache.org/v1alpha1")
+       if err != nil && errors.IsNotFound(err) {
+               return false, nil
+       } else if err != nil {
+               return false, err
+       }
+       for _, res := range lst.APIResources {
+               if res.Kind == "Integration" {
+                       return true, nil
+               }
+       }
+       return false, nil
+}
+
+func installCRD() error {
+       crd := []byte(deploy.Resources["crd.yaml"])
+       crdJson, err := yaml.ToJSON(crd)
+       if err != nil {
+               return err
+       }
+       restClient, err := customclient.GetClientFor("apiextensions.k8s.io", 
"v1beta1")
+       if err != nil {
+               return err
+       }
+       // Post using dynamic client
+       result := restClient.
+               Post().
+               Body(crdJson).
+               Resource("customresourcedefinitions").
+               Do()
+       // Check result
+       if result.Error() != nil && !errors.IsAlreadyExists(result.Error()) {
+               return result.Error()
+       }
+
+       return nil
+}
+
+func isClusterRoleInstalled() (bool, error) {
+       clusterRole := v1.ClusterRole{
+               TypeMeta: metav1.TypeMeta{
+                       Kind:       "ClusterRole",
+                       APIVersion: "rbac.authorization.k8s.io/v1",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Name: "camel-k:edit",
+               },
+       }
+       err := sdk.Get(&clusterRole)
+       if err != nil && errors.IsNotFound(err) {
+               return false, nil
+       } else if err != nil {
+               return false, err
+       }
+       return true, nil
+}
+
+func installClusterRole() error {
+       obj, err := 
kubernetes.LoadResourceFromYaml(deploy.Resources["user-cluster-role.yaml"])
+       if err != nil {
+               return err
+       }
+
+       return sdk.Create(obj)
+}
diff --git a/pkg/install/cluster_integration_test.go 
b/pkg/install/cluster_integration_test.go
new file mode 100644
index 0000000..df9f93c
--- /dev/null
+++ b/pkg/install/cluster_integration_test.go
@@ -0,0 +1,36 @@
+/*
+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 install
+
+import (
+       "testing"
+       "github.com/stretchr/testify/assert"
+)
+
+func TestInstallation(t *testing.T) {
+       err := SetupClusterwideResources()
+       assert.Nil(t, err)
+
+       installedCRD, err := isCRDInstalled()
+       assert.Nil(t, err)
+       assert.True(t, installedCRD)
+
+       installedClusterRole, err := isClusterRoleInstalled()
+       assert.Nil(t, err)
+       assert.True(t, installedClusterRole)
+}
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
new file mode 100644
index 0000000..ad6d5d6
--- /dev/null
+++ b/pkg/install/operator.go
@@ -0,0 +1,61 @@
+/*
+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 install
+
+import (
+       "github.com/apache/camel-k/deploy"
+       "github.com/operator-framework/operator-sdk/pkg/sdk"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       "k8s.io/apimachinery/pkg/api/errors"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func InstallOperator(namespace string) error {
+       return installResources(namespace,
+               "operator-service-account.yaml",
+               "operator-role.yaml",
+               "operator-role-binding.yaml",
+               "operator.yaml",
+       )
+}
+
+func installResources(namespace string, names ...string) error {
+       for _, name := range names {
+               if err := installResource(namespace, name); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+func installResource(namespace string, name string) error {
+       obj, err := kubernetes.LoadResourceFromYaml(deploy.Resources[name])
+       if err != nil {
+               return err
+       }
+
+       if kObj, ok := obj.(metav1.Object); ok {
+               kObj.SetNamespace(namespace)
+       }
+
+       err = sdk.Create(obj)
+       if err != nil && errors.IsAlreadyExists(err) {
+               return sdk.Update(obj)
+       }
+       return err
+}
diff --git a/pkg/client/cmd/config.go b/pkg/util/kubernetes/config.go
similarity index 79%
rename from pkg/client/cmd/config.go
rename to pkg/util/kubernetes/config.go
index f7b347b..f01a8de 100644
--- a/pkg/client/cmd/config.go
+++ b/pkg/util/kubernetes/config.go
@@ -15,26 +15,18 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package cmd
+package kubernetes
 
 import (
        "os/user"
        "path/filepath"
-
-       "github.com/operator-framework/operator-sdk/pkg/k8sclient"
-       "github.com/spf13/cobra"
        "k8s.io/client-go/tools/clientcmd"
+       "github.com/operator-framework/operator-sdk/pkg/k8sclient"
 )
 
-func initKubeClient(cmd *cobra.Command) error {
-       kubeconfig := cmd.Flag("config").Value.String()
+func InitKubeClient(kubeconfig string) error {
        if kubeconfig == "" {
-               usr, err := user.Current()
-               if err != nil {
-                       return err
-               }
-
-               kubeconfig = filepath.Join(usr.HomeDir, ".kube", "config")
+               kubeconfig = GetDefaultKubeConfigFile()
        }
 
        // use the current context in kubeconfig
@@ -46,3 +38,12 @@ func initKubeClient(cmd *cobra.Command) error {
        k8sclient.CustomConfig = config
        return nil
 }
+
+func GetDefaultKubeConfigFile() string {
+       usr, err := user.Current()
+       if err != nil {
+               panic(err) // TODO handle error
+       }
+
+       return filepath.Join(usr.HomeDir, ".kube", "config")
+}
diff --git a/pkg/util/kubernetes/customclient/customclient.go 
b/pkg/util/kubernetes/customclient/customclient.go
new file mode 100644
index 0000000..9dc0735
--- /dev/null
+++ b/pkg/util/kubernetes/customclient/customclient.go
@@ -0,0 +1,44 @@
+/*
+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 customclient
+
+import (
+       "k8s.io/client-go/rest"
+       "k8s.io/apimachinery/pkg/runtime/schema"
+       "github.com/operator-framework/operator-sdk/pkg/k8sclient"
+)
+
+func GetClientFor(group string, version string) (*rest.RESTClient, error) {
+       inConfig := k8sclient.GetKubeConfig()
+       config := rest.CopyConfig(inConfig)
+       config.GroupVersion = &schema.GroupVersion{
+               Group:   group,
+               Version: version,
+       }
+       config.APIPath = "/apis"
+       config.AcceptContentTypes = "application/json"
+       config.ContentType = "application/json"
+
+       // this gets used for discovery and error handling types
+       config.NegotiatedSerializer = basicNegotiatedSerializer{}
+       if config.UserAgent == "" {
+               config.UserAgent = rest.DefaultKubernetesUserAgent()
+       }
+
+       return rest.RESTClientFor(config)
+}
diff --git a/pkg/util/kubernetes/customclient/scheme.go 
b/pkg/util/kubernetes/customclient/scheme.go
new file mode 100644
index 0000000..f20496d
--- /dev/null
+++ b/pkg/util/kubernetes/customclient/scheme.go
@@ -0,0 +1,98 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed 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 customclient
+
+import (
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       "k8s.io/apimachinery/pkg/runtime/schema"
+       "k8s.io/apimachinery/pkg/runtime/serializer"
+       "k8s.io/apimachinery/pkg/runtime/serializer/json"
+       "k8s.io/apimachinery/pkg/runtime/serializer/versioning"
+)
+
+var watchScheme = runtime.NewScheme()
+var basicScheme = runtime.NewScheme()
+var deleteScheme = runtime.NewScheme()
+var parameterScheme = runtime.NewScheme()
+var deleteOptionsCodec = serializer.NewCodecFactory(deleteScheme)
+var dynamicParameterCodec = runtime.NewParameterCodec(parameterScheme)
+
+var versionV1 = schema.GroupVersion{Version: "v1"}
+
+func init() {
+       metav1.AddToGroupVersion(watchScheme, versionV1)
+       metav1.AddToGroupVersion(basicScheme, versionV1)
+       metav1.AddToGroupVersion(parameterScheme, versionV1)
+       metav1.AddToGroupVersion(deleteScheme, versionV1)
+}
+
+var watchJsonSerializerInfo = runtime.SerializerInfo{
+       MediaType:        "application/json",
+       EncodesAsText:    true,
+       Serializer:       json.NewSerializer(json.DefaultMetaFactory, 
watchScheme, watchScheme, false),
+       PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, 
watchScheme, watchScheme, true),
+       StreamSerializer: &runtime.StreamSerializerInfo{
+               EncodesAsText: true,
+               Serializer:    json.NewSerializer(json.DefaultMetaFactory, 
watchScheme, watchScheme, false),
+               Framer:        json.Framer,
+       },
+}
+
+// watchNegotiatedSerializer is used to read the wrapper of the watch stream
+type watchNegotiatedSerializer struct{}
+
+var watchNegotiatedSerializerInstance = watchNegotiatedSerializer{}
+
+func (s watchNegotiatedSerializer) SupportedMediaTypes() 
[]runtime.SerializerInfo {
+       return []runtime.SerializerInfo{watchJsonSerializerInfo}
+}
+
+func (s watchNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, 
gv runtime.GroupVersioner) runtime.Encoder {
+       return versioning.NewDefaultingCodecForScheme(watchScheme, encoder, 
nil, gv, nil)
+}
+
+func (s watchNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, 
gv runtime.GroupVersioner) runtime.Decoder {
+       return versioning.NewDefaultingCodecForScheme(watchScheme, nil, 
decoder, nil, gv)
+}
+
+// basicNegotiatedSerializer is used to handle discovery and error handling 
serialization
+type basicNegotiatedSerializer struct{}
+
+func (s basicNegotiatedSerializer) SupportedMediaTypes() 
[]runtime.SerializerInfo {
+       return []runtime.SerializerInfo{
+               {
+                       MediaType:        "application/json",
+                       EncodesAsText:    true,
+                       Serializer:       
json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, false),
+                       PrettySerializer: 
json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, true),
+                       StreamSerializer: &runtime.StreamSerializerInfo{
+                               EncodesAsText: true,
+                               Serializer:    
json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, false),
+                               Framer:        json.Framer,
+                       },
+               },
+       }
+}
+
+func (s basicNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, 
gv runtime.GroupVersioner) runtime.Encoder {
+       return versioning.NewDefaultingCodecForScheme(watchScheme, encoder, 
nil, gv, nil)
+}
+
+func (s basicNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, 
gv runtime.GroupVersioner) runtime.Decoder {
+       return versioning.NewDefaultingCodecForScheme(watchScheme, nil, 
decoder, nil, gv)
+}
\ No newline at end of file
diff --git a/pkg/util/kubernetes/loader.go b/pkg/util/kubernetes/loader.go
new file mode 100644
index 0000000..c1cfc5a
--- /dev/null
+++ b/pkg/util/kubernetes/loader.go
@@ -0,0 +1,40 @@
+/*
+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 kubernetes
+
+import (
+       "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+       "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
+       "k8s.io/apimachinery/pkg/util/yaml"
+       "k8s.io/apimachinery/pkg/runtime"
+)
+
+func LoadResourceFromYaml(data string) (runtime.Object, error) {
+       role := []byte(data)
+       roleJson, err := yaml.ToJSON(role)
+       if err != nil {
+               return nil, err
+       }
+       u := unstructured.Unstructured{}
+       err = u.UnmarshalJSON(roleJson)
+       if err != nil {
+               return nil, err
+       }
+
+       return k8sutil.RuntimeObjectFromUnstructured(&u)
+}
diff --git a/pkg/util/kubernetes/namespace.go b/pkg/util/kubernetes/namespace.go
new file mode 100644
index 0000000..116865d
--- /dev/null
+++ b/pkg/util/kubernetes/namespace.go
@@ -0,0 +1,56 @@
+/*
+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 kubernetes
+
+import (
+       "k8s.io/client-go/tools/clientcmd"
+       clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+       clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest"
+       "io/ioutil"
+       "github.com/pkg/errors"
+       "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+func GetClientCurrentNamespace(kubeconfig string) (string, error) {
+       if kubeconfig == "" {
+               kubeconfig = GetDefaultKubeConfigFile()
+       }
+       if kubeconfig == "" {
+               return "default", nil
+       }
+
+       data, err := ioutil.ReadFile(kubeconfig)
+       if err != nil {
+               return "", err
+       }
+       config := clientcmdapi.NewConfig()
+       if len(data) == 0 {
+               return "", errors.New("kubernetes config file is empty")
+       }
+
+       decoded, _, err := clientcmdlatest.Codec.Decode(data, 
&schema.GroupVersionKind{Version: clientcmdlatest.Version, Kind: "Config"}, 
config)
+       if err != nil {
+               return "", err
+       }
+
+       clientcmdconfig := decoded.(*clientcmdapi.Config)
+
+       cc := clientcmd.NewDefaultClientConfig(*clientcmdconfig, 
&clientcmd.ConfigOverrides{})
+       ns, _, err := cc.Namespace()
+       return ns, err
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to