This is an automated email from the ASF dual-hosted git repository. lburgazzoli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit d827ed238bd6a072ce5f145c62a3afc8e999a872 Author: nferraro <ni.ferr...@gmail.com> AuthorDate: Mon Sep 10 11:19:55 2018 +0200 Add kamel install (docker image still incomplete) --- build/Makefile | 5 +- build/embed_resources.sh | 47 ++++++ deploy/operator-role-binding.yaml | 11 ++ deploy/{rbac.yaml => operator-role.yaml} | 18 +-- deploy/operator-service-account.yaml | 4 + deploy/operator.yaml | 5 +- deploy/resources.go | 180 +++++++++++++++++++++ deploy/user-cluster-role.yaml | 12 ++ pkg/build/local/local_builder.go | 31 +--- pkg/client/cmd/{get => }/get.go | 2 +- pkg/client/cmd/install.go | 58 +++++++ pkg/client/cmd/root.go | 11 +- pkg/install/cluster.go | 124 ++++++++++++++ pkg/install/cluster_integration_test.go | 36 +++++ pkg/{client/cmd/get/get.go => install/operator.go} | 59 ++++--- pkg/util/kubernetes/customclient/customclient.go | 44 +++++ pkg/util/kubernetes/customclient/scheme.go | 98 +++++++++++ pkg/util/kubernetes/loader.go | 40 +++++ 18 files changed, 706 insertions(+), 79 deletions(-) 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/get.go b/pkg/client/cmd/get.go similarity index 99% copy from pkg/client/cmd/get/get.go copy to pkg/client/cmd/get.go index 6702145..39a45d9 100644 --- a/pkg/client/cmd/get/get.go +++ b/pkg/client/cmd/get.go @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package get +package cmd import ( "github.com/spf13/cobra" 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 74a8f07..4014a20 100644 --- a/pkg/client/cmd/root.go +++ b/pkg/client/cmd/root.go @@ -19,20 +19,22 @@ package cmd import ( "os" + + "github.com/apache/camel-k/pkg/client/cmd/run" + "github.com/apache/camel-k/pkg/client/cmd/version" "github.com/spf13/cobra" "github.com/apache/camel-k/pkg/client/cmd/get" "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/pkg/errors" ) -type KamelCmdOptions struct { +type rootCmdOptions struct { KubeConfig string Namespace string } - func NewKamelCommand() (*cobra.Command, error) { - options := KamelCmdOptions{} + options := rootCmdOptions{} var cmd = cobra.Command{ Use: "kamel", Short: "Kamel is a awesome client tool for running Apache Camel integrations natively on Kubernetes", @@ -62,7 +64,8 @@ func NewKamelCommand() (*cobra.Command, error) { cmd.AddCommand(NewCmdCompletion()) cmd.AddCommand(NewCmdVersion()) cmd.AddCommand(NewCmdRun()) - cmd.AddCommand(get.NewCmdGet()) + cmd.AddCommand(NewCmdGet()) + cmd.AddCommand(NewCmdInstall()) return &cmd, nil } 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/client/cmd/get/get.go b/pkg/install/operator.go similarity index 51% rename from pkg/client/cmd/get/get.go rename to pkg/install/operator.go index 6702145..ad6d5d6 100644 --- a/pkg/client/cmd/get/get.go +++ b/pkg/install/operator.go @@ -15,50 +15,47 @@ See the License for the specific language governing permissions and limitations under the License. */ -package get +package install 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/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 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 InstallOperator(namespace string) error { + return installResources(namespace, + "operator-service-account.yaml", + "operator-role.yaml", + "operator-role-binding.yaml", + "operator.yaml", + ) } -func run(cmd *cobra.Command, args []string) error { - integrationList := v1alpha1.IntegrationList{ - TypeMeta: metav1.TypeMeta{ - APIVersion: v1alpha1.SchemeGroupVersion.String(), - Kind: "Integration", - }, +func installResources(namespace string, names ...string) error { + for _, name := range names { + if err := installResource(namespace, name); err != nil { + return err + } } + return nil +} - namespace := cmd.Flag("namespace").Value.String() - - err := sdk.List(namespace, &integrationList) +func installResource(namespace string, name string) error { + obj, err := kubernetes.LoadResourceFromYaml(deploy.Resources[name]) 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)) + if kObj, ok := obj.(metav1.Object); ok { + kObj.SetNamespace(namespace) } - w.Flush() - return nil + err = sdk.Create(obj) + if err != nil && errors.IsAlreadyExists(err) { + return sdk.Update(obj) + } + return err } 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) +}