From 870529d6065e694c422edb57d4fff7fa448fed9d Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 2 Apr 2019 16:25:11 +1100 Subject: [PATCH] Updated readme, added examples, deployment updates added --- .gitignore | 7 ++- README.md | 28 ++++------ Dockerfile => build/Dockerfile | 4 +- example/Role.yaml | 12 ++++ example/RoleBinding.yaml | 12 ++++ example/ServiceAccount.yaml | 5 ++ {test => example}/deployment.template.yaml | 2 +- glide.lock | 14 +---- kube.go | 65 ++++++++++++++-------- main.go | 9 +-- plugin.go | 19 +++---- 11 files changed, 106 insertions(+), 71 deletions(-) rename Dockerfile => build/Dockerfile (52%) create mode 100644 example/Role.yaml create mode 100644 example/RoleBinding.yaml create mode 100644 example/ServiceAccount.yaml rename {test => example}/deployment.template.yaml (83%) diff --git a/.gitignore b/.gitignore index 4ae9d9d..8a9e2b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -vendor -kubano # Development -test* +vendor +build/kubano +test +test.sh diff --git a/README.md b/README.md index 4890e4b..3dfc698 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # drone-kubano -A simple Drone plugin for managing Kubernetes deployments. Follows from `vallard/drone-kube` but with dependency management system, improved docs, examples and restructured code. +A simple Drone plugin for managing Kubernetes deployments. Follows from `vallard/drone-kube` but with dependency management, up-to-date , improved docs updated to Drone 1.0.0, examples, restructured code and I will . ## Usage @@ -12,28 +12,24 @@ Add the following [build step](https://docs.drone.io/user-guide/pipeline/steps/) image: danielgormly/kubano settings: template: path/to/deployment.yaml # within repo - ca: # BASE64 encoded string of the K8s CA cert - Server: 10.0.0.24:6443 # K8s master node address - Token: # Service account token to a service account that can manage deployments - Namespace: custom # Custom namespace. (Optional, defaults to `default`) - custom: string # Available to be referenced in template rendering as PLUGIN_CUSTOM - master_alias: production # Custom setting example. Available as PLUGIN_MASTER_ALIAS + ca: LS0tLS1... # BASE64 encoded string of the K8s CA cert + server: https://10.0.0.20:6443 # K8s master node address + token: ey... # Service account token to a service account that can manage deployments + namespace: custom # [Optional] Custom namespace. (Defaults to `default`) + custom: string # [Optional] Available to be referenced in template rendering as PLUGIN_CUSTOM + master_alias: production # [Optional] Custom setting example. Available as PLUGIN_MASTER_ALIAS ``` ## deployment templates -Deployment config files are first interpreted by **aymerick/raymond** ([handlebarsjs](http://handlebarsjs.com/) equivalent). You can use all available raymond expressions, [DRONE_*](https://docs.drone.io/reference/environ/), PLUGIN_* environment variables. Use `{{VARIABLE}}` to add interpolated expressions e.g. +Deployment config files are first interpreted by **aymerick/raymond** ([handlebarsjs](http://handlebarsjs.com/) equivalent). You can use all available raymond expressions, [DRONE_*](https://docs.drone.io/reference/environ/), PLUGIN_* environment variables. Use `{{VARIABLE}}` to add interpolated expressions. See `/example/deployment.template.yaml` for a complete example. -#### deployment.yaml partial example -```yaml -spec: - containers: - - name: nginx - image: 10.0.0.24:443/danielgormly:{{DRONE_BRANCH}} -``` +#### Adding a service account to Kubernetes that can manage deployments +See `/example/Role.yaml`, `/example/ServiceAccount.yaml`, `/example/RoleBinding.yaml`. -#### Development +### Development - Kubernetes client not yet supported by dep, so we are using [`brew install glide`](https://github.com/Masterminds/glide). - Update dependencies with brew `glide update --strip-vendor` - [Creating a Drone plugin in Go](https://docs.drone.io/plugins/examples/golang/) +- Testing with minikube (OSX: `brew cask install minikube`) diff --git a/Dockerfile b/build/Dockerfile similarity index 52% rename from Dockerfile rename to build/Dockerfile index 93c8b66..5902cd4 100644 --- a/Dockerfile +++ b/build/Dockerfile @@ -1,4 +1,4 @@ FROM alpine -ADD slack /bin/ +ADD kubano /bin/ RUN apk -Uuv add ca-certificates -ENTRYPOINT /bin/slack \ No newline at end of file +ENTRYPOINT /bin/kubano diff --git a/example/Role.yaml b/example/Role.yaml new file mode 100644 index 0000000..5669239 --- /dev/null +++ b/example/Role.yaml @@ -0,0 +1,12 @@ +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + namespace: default + name: update-deployments +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch", "create", "update", "patch"] +- apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["get", "list", "watch", "create", "update", "patch"] diff --git a/example/RoleBinding.yaml b/example/RoleBinding.yaml new file mode 100644 index 0000000..316e7f5 --- /dev/null +++ b/example/RoleBinding.yaml @@ -0,0 +1,12 @@ +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: drone-ci +subjects: +- kind: ServiceAccount + name: drone-ci + namespace: default +roleRef: + kind: Role + name: update-deployments + apiGroup: rbac.authorization.k8s.io diff --git a/example/ServiceAccount.yaml b/example/ServiceAccount.yaml new file mode 100644 index 0000000..20fce74 --- /dev/null +++ b/example/ServiceAccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: drone-ci +automountServiceAccountToken: true diff --git a/test/deployment.template.yaml b/example/deployment.template.yaml similarity index 83% rename from test/deployment.template.yaml rename to example/deployment.template.yaml index c19095a..7c1ea1d 100644 --- a/test/deployment.template.yaml +++ b/example/deployment.template.yaml @@ -14,7 +14,7 @@ spec: spec: containers: - name: nginx - image: 10.0.0.24:443/test:{{DRONE_BRANCH}}.{{DRONE_COMMIT_SHA}} + image: 10.0.0.24:443/image:{{DRONE_COMMIT_SHA}} ports: - containerPort: 80 imagePullSecrets: diff --git a/glide.lock b/glide.lock index 5803c77..586aa90 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 3db3288a056b65e1736f52faf757d1a393b0af3d0ebbe5b63bdeab60b37e1da0 -updated: 2019-04-01T15:48:47.671231+11:00 +updated: 2019-04-02T13:06:49.449913+11:00 imports: - name: github.com/aymerick/raymond version: b565731e1464263de0bda75f2e45d97b54b60110 @@ -54,7 +54,6 @@ imports: version: 0ed95abb35c445290478a5348a7b38bb154135fd subpackages: - context - - context/ctxhttp - http2 - http2/hpack - idna @@ -120,7 +119,6 @@ imports: - core/v1 - events/v1beta1 - extensions/v1beta1 - - imagepolicy/v1alpha1 - networking/v1 - policy/v1beta1 - rbac/v1 @@ -143,7 +141,6 @@ imports: - pkg/api/meta - pkg/api/resource - pkg/apis/meta/fuzzer - - pkg/apis/meta/internalversion - pkg/apis/meta/v1 - pkg/apis/meta/v1/unstructured - pkg/apis/meta/v1beta1 @@ -161,30 +158,21 @@ imports: - pkg/runtime/serializer/versioning - pkg/selection - pkg/types - - pkg/util/cache - pkg/util/clock - pkg/util/diff - pkg/util/errors - pkg/util/framer - - pkg/util/httpstream - - pkg/util/httpstream/spdy - pkg/util/intstr - pkg/util/json - - pkg/util/mergepatch - pkg/util/naming - pkg/util/net - - pkg/util/remotecommand - pkg/util/runtime - pkg/util/sets - - pkg/util/strategicpatch - pkg/util/validation - pkg/util/validation/field - - pkg/util/wait - pkg/util/yaml - pkg/version - pkg/watch - - third_party/forked/golang/json - - third_party/forked/golang/netutil - third_party/forked/golang/reflect - name: k8s.io/client-go version: e64494209f554a6723674bd494d69445fb76a1d4 diff --git a/kube.go b/kube.go index b4248b0..8ff807a 100644 --- a/kube.go +++ b/kube.go @@ -1,31 +1,41 @@ package main import ( + "encoding/base64" "fmt" "log" - "time" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + appv1 "k8s.io/api/apps/v1" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/clientcmd/api" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" ) // CreateKubeClient -- Creates KubeClient func (p Plugin) CreateKubeClient() (*kubernetes.Clientset, error) { - // ca, err := base64.StdEncoding.DecodeString(p.KubeConfig.Ca) - config := api.NewConfig() - config.Clusters["default"] = &api.Cluster{ + config := clientcmdapi.NewConfig() + clusterConfig := clientcmdapi.Cluster{ Server: p.KubeConfig.Server, - // CertificateAuthorityData: ca, - InsecureSkipTLSVerify: true, } - config.AuthInfos["default"] = &api.AuthInfo{ + if p.KubeConfig.InsecureSkipTLSVerify == true { + clusterConfig.InsecureSkipTLSVerify = true + log.Println("InsecureSkipTLSVerify flag set") + } else { + ca, err := base64.StdEncoding.DecodeString(p.KubeConfig.Ca) + if err != nil { + log.Fatal(err) + } + clusterConfig.CertificateAuthorityData = ca + } + config.Clusters["default"] = &clusterConfig + config.AuthInfos["default"] = &clientcmdapi.AuthInfo{ Token: p.KubeConfig.Token, } - config.Contexts["default"] = &api.Context{ - Cluster: "default", - AuthInfo: "default", + config.Contexts["default"] = &clientcmdapi.Context{ + Cluster: "default", + AuthInfo: "default", + Namespace: p.KubeConfig.Namespace, } config.CurrentContext = "default" clientBuilder := clientcmd.NewNonInteractiveClientConfig(*config, "default", &clientcmd.ConfigOverrides{}, nil) @@ -39,14 +49,25 @@ func (p Plugin) CreateKubeClient() (*kubernetes.Clientset, error) { return kubernetes.NewForConfig(actualCfg) } -// WatchPodCounts -- Example function -func WatchPodCounts(clientset *kubernetes.Clientset) { - for { - pods, err := clientset.Core().Pods("").List(v1.ListOptions{}) - if err != nil { - log.Fatal(err.Error()) - } - fmt.Printf("There are %d pods in the cluster\n", len(pods.Items)) - time.Sleep(10 * time.Second) - } +// CreateDeploymentObj -- Construct KubeClient ready json from YAML definition file +func CreateDeploymentObj(yaml string) *appv1.Deployment { + deployment := appv1.Deployment{} + scheme.Codecs.UniversalDeserializer().Decode([]byte(yaml), nil, &deployment) + return &deployment } + +// UpdateDeployment -- Updates given deployment in Kubernetes +func UpdateDeployment(clientset *kubernetes.Clientset, namespace string, deployment *appv1.Deployment) error { + _, err := clientset.AppsV1().Deployments(namespace).Update(deployment) + return err +} + +// ListDeployments -- List deployments in Kubernetes +// func ListDeployments(clientset *kubernetes.Clientset, namespace string) { +// deployments, err := clientset.AppsV1().Deployments(namespace).List(v1.ListOptions{}) +// if err != nil { +// log.Fatal(err.Error()) +// } +// fmt.Println(deployments.Items) +// // return deployments.Items +// } diff --git a/main.go b/main.go index d8a0964..e612ffb 100644 --- a/main.go +++ b/main.go @@ -9,10 +9,11 @@ func main() { plugin := Plugin{ Template: os.Getenv("PLUGIN_TEMPLATE"), KubeConfig: KubeConfig{ - Token: os.Getenv("PLUGIN_TOKEN"), - Server: os.Getenv("PLUGIN_SERVER"), - Ca: os.Getenv("PLUGIN_CA"), - Namespace: os.Getenv("PLUGIN_NAMESPACE"), + Token: os.Getenv("PLUGIN_TOKEN"), + Server: os.Getenv("PLUGIN_SERVER"), + Ca: os.Getenv("PLUGIN_CA"), + Namespace: os.Getenv("PLUGIN_NAMESPACE"), + InsecureSkipTLSVerify: os.Getenv("PLUGIN_SKIP_TLS") == "false", }, } err := plugin.Exec() diff --git a/plugin.go b/plugin.go index be8d96b..bba8894 100644 --- a/plugin.go +++ b/plugin.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "io/ioutil" "log" "os" @@ -13,10 +12,11 @@ import ( type ( // KubeConfig -- Contains connection settings for Kube client KubeConfig struct { - Ca string - Server string - Token string - Namespace string + Ca string + Server string + Token string + Namespace string + InsecureSkipTLSVerify bool } // Plugin -- Contains config for plugin Plugin struct { @@ -59,17 +59,16 @@ func (p Plugin) Exec() error { return err } // Parse template - result, err := raymond.Render(string(raw), ctx) + depYaml, err := raymond.Render(string(raw), ctx) if err != nil { panic(err) } - // connect to Kubernetes + // Connect to Kubernetes clientset, err := p.CreateKubeClient() - WatchPodCounts(clientset) if err != nil { log.Fatal(err.Error()) } - fmt.Print(result) - + deployment := CreateDeploymentObj(depYaml) + UpdateDeployment(clientset, p.KubeConfig.Namespace, deployment) return err }