22 Commits

Author SHA1 Message Date
Daniel 438081953b Merge pull request #4 from SvenC56/patch-1
Update drone.md
2020-04-03 23:03:50 +11:00
Sven f9f8d9aa49 Update drone.md
I tried to make the Readme more accessible. I hope it helps. 🙂
2020-04-03 13:48:13 +02:00
Daniel Gormly cd888d5ff8 Updated with service example, updated version number, build steps 2020-04-03 22:19:00 +11:00
Daniel ddac2b3969 Update README.md 2020-02-22 07:22:38 +11:00
danielgormly 5afd783cc1 Updated version number in plugin 2020-02-20 17:51:00 +11:00
danielgormly cdf38eea63 Readme 2020-02-20 12:51:20 +11:00
danielgormly 988c199078 Updated readme for 0.1 2020-02-20 12:43:57 +11:00
danielgormly 184aa03d35 Minor updates to documentation, testing 2020-02-20 12:40:19 +11:00
jbonzo b48fbdf379 Added some things back the way they were 2020-02-17 15:39:55 -05:00
jbonzo 18eb9355f6 Updated readme 2020-02-17 15:39:55 -05:00
jbonzo 905e7fb4dd add ingresses support 2020-02-17 15:39:55 -05:00
jbonzo 3b7fbfd838 remove old dep mgmt files 2020-02-17 15:39:55 -05:00
jbonzo 5ca8575d87 pinning to same kube client version as servers 2020-02-17 15:39:55 -05:00
jbonzo 3035df5f71 Lot of updates. Fixed some error handling. Changed up some logging. But best of all added service as a supported resource type 2020-02-17 15:39:55 -05:00
jbonzo fd8b5db452 Made some better error checking and pushed to our repo 2020-02-17 15:39:55 -05:00
Daniel 0984bb35d0 Update README.md 2020-02-13 11:54:57 +11:00
Daniel bf86fd6da1 Merge pull request #1 from Rand01ph/patch-1
fix development notes link
2019-11-28 23:34:43 +11:00
9r0k b734a70f81 fix Development notes link
Drone v1.0 has changed plugins tutorials address
2019-11-28 13:50:20 +08:00
danielgormly bffcd56139 Updated readme 2019-09-04 11:51:54 +10:00
Daniel Gormly 52c2cfd645 Merge branch 'master' of https://github.com/danielgormly/drone-plugin-kube 2019-09-04 08:22:44 +10:00
Daniel Gormly 96a6a70009 Updated drone.md to match drone-plugin-index PR post-review changes 2019-09-04 08:03:25 +10:00
danielgormly 5e5f7599d4 Fixed infinite watching of deployments 2019-08-27 15:22:15 +10:00
18 changed files with 494 additions and 426 deletions
+3
View File
@@ -0,0 +1,3 @@
**
!/built/*
+14 -34
View File
@@ -2,43 +2,23 @@
[![](https://images.microbadger.com/badges/version/danielgormly/drone-plugin-kube.svg)](https://microbadger.com/images/danielgormly/drone-plugin-kube "Get your own version badge on microbadger.com")
A simple Drone plugin for updating Kubernetes deployments from templates & configMaps from files. Follows from [vallard/drone-kube](https://github.com/vallard/drone-kube) but with dependency management, up-to-date client-go, docs updated to Drone 1.0.0 syntax, examples and a different structure. This plugin will create a deployment if it doesn't currently exist.
A simple Drone plugin for updating Kubernetes resources from yaml templates. Follows from [vallard/drone-kube](https://github.com/vallard/drone-kube). The plugin will create a resource if it doesn't already exist i.e. behaves similar to `kubectl apply`. This plugin supports deployments, configmaps, ingresses, and services.
## Usage
Usage: See [drone.md](./drone.md)
Add the following [build step](https://docs.drone.io/user-guide/pipeline/steps/) to your drone pipeline definition. Currently this plugin only updates deployments, it does not create them. I can add this behaviour or I will accept pull requests to introduce it.
#### drone.yaml deployment example
```yml
- name: Deploy app
image: danielgormly/drone-plugin-kube
settings:
template: path/to/deployment.yaml # within repo
ca: LS0tLS1... # BASE64 encoded string of the K8s CA cert
server: https://10.0.0.20:6443 # K8s master node address
token:
from_secret: kubernetes_token # Service account token to a service account that can manage deployments
namespace: custom # [Optional] Kubernetes namespace to use (defaults to `default`)
[example_custom_key]: string # [Optional, example] Any additional values you label here will be available for template interpolation (lower case key names only!)
```
## Deployment templates
Deployment config files are first interpreted by **aymerick/raymond** ([handlebarsjs](http://handlebarsjs.com/) equivalent). You can use all available raymond expressions and anything you put in settings will be made available in your deployment template e.g. `{{namespace}}`. See [example/deployment.template.yaml](/example/deployment.template.yaml) for a complete example.
## Config maps from files
In this case, you can create a template just like deployment.yaml but you can provide a file path (relative to the repo's root) in the plugin setting `configmap_file`. (Currently only accepts utf-8 encoded data). Like deployments, this will both create new or update existing configmaps (based on the configmap name).
#### Adding a service account to Kubernetes that can manage deployments
#### Creating a Kubernetes service account for Drone
See [example/Role.yaml](example/Role.yaml), [example/ServiceAccount.yaml](example/ServiceAccount.yaml), [example/RoleBinding.yaml](example/RoleBinding.yaml).
## Usage notes:
- The watching process is not currently reliable i.e. it doesn't properly wait for new deployments to become live. Not entirely sure how this should behave but I think behind a flag would make sense. PRs welcome.
## Development notes
- No tagged releases or support for older go libraries yet, happy to take feedback in Github issues or PRs.
- 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`
- [Installing kubernetes/client-go](https://github.com/kubernetes/client-go/blob/master/INSTALL.md)
- [Creating a Drone plugin in Go](https://docs.drone.io/plugins/examples/golang/)
- [kubernetes/client-go installation notes](https://github.com/kubernetes/client-go/blob/master/INSTALL.md)
- [Creating a Drone plugin in Go](https://docs.drone.io/plugins/golang/)
- [Client-go API Docs @ godoc.org](https://pkg.go.dev/k8s.io/client-go/kubernetes?tab=doc)
- Testing with minikube (OSX: `brew cask install minikube`)
- [Client-go API Docs @ godoc.org](https://godoc.org/k8s.io/client-go/kubernetes)
## Acknowledgements
- [@vallard](https://github.com/vallard) for the original plugin
- [@jbonzo](https://github.com/jbonzo) for ingress, service support, improved error handling etc
+6 -3
View File
@@ -1,7 +1,10 @@
#!/bin/bash
#!/usr/bin/env bash
set -eou pipefail
rm -rf build/kubano
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o build/kubano
docker build -t danielgormly/drone-plugin-kube:0.0.1 build
docker push danielgormly/drone-plugin-kube
docker build -t danielgormly/drone-plugin-kube:0.2.0 -t danielgormly/drone-plugin-kube:latest build
docker push danielgormly/drone-plugin-kube:0.2.0
docker push danielgormly/drone-plugin-kube:latest
-48
View File
@@ -1,48 +0,0 @@
package main
import (
"io/ioutil"
"log"
"path/filepath"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
// ApplyConfigMapFromFile -- Updates given deployment in Kubernetes
func ApplyConfigMapFromFile(clientset *kubernetes.Clientset, namespace string, configmap *corev1.ConfigMap, path string) error {
log.Printf("📦 Reading contents of %s", path)
_, filename := filepath.Split(path)
fileContents, err := ioutil.ReadFile(path)
if err != nil {
return err
}
configMapData := make(map[string]string)
configMapData[filename] = string(fileContents)
configmap.Data = configMapData
// Check if deployment exists
deploymentExists, err := configMapExists(clientset, namespace, configmap.Name)
if deploymentExists {
log.Printf("📦 Found existing deployment. Updating %s.", configmap.Name)
_, err = clientset.CoreV1().ConfigMaps(namespace).Update(configmap)
return err
}
_, err = clientset.CoreV1().ConfigMaps(namespace).Create(configmap)
return err
}
// configMapExists -- Updates given deployment in Kubernetes
func configMapExists(clientset *kubernetes.Clientset, namespace string, name string) (bool, error) {
_, err := clientset.CoreV1().ConfigMaps(namespace).Get(name, meta.GetOptions{})
if err != nil {
// TODO: Only conver to StatusError if the error is in fact a status error
statusError, ok := err.(*errors.StatusError)
if ok == true && statusError.Status().Code == 404 {
return false, nil
}
return false, err
}
return true, nil
}
-65
View File
@@ -1,65 +0,0 @@
package main
import (
"log"
"strings"
appv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/api/errors"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
// CreateOrUpdateDeployment -- Checks if deployment already exists, updates if it does, creates if it doesn't
func CreateOrUpdateDeployment(clientset *kubernetes.Clientset, namespace string, deployment *appv1.Deployment) error {
deploymentExists, err := deploymentExists(clientset, namespace, deployment.Name)
if deploymentExists {
log.Printf("📦 Found existing deployment '%s'. Updating.", deployment.Name)
_, err = clientset.AppsV1().Deployments(namespace).Update(deployment)
return err
}
log.Printf("📦 Creating new deployment '%s'. Updating.", deployment.Name)
_, err = clientset.AppsV1().Deployments(namespace).Create(deployment)
return err
}
// deploymentExists -- Updates given deployment in Kubernetes
func deploymentExists(clientset *kubernetes.Clientset, namespace string, name string) (bool, error) {
_, err := clientset.AppsV1().Deployments(namespace).Get(name, meta.GetOptions{})
if err != nil {
// TODO: Only conver to StatusError if the error is in fact a status error
statusError, ok := err.(*errors.StatusError)
if ok == true && statusError.Status().Code == 404 {
return false, nil
}
return false, err
}
return true, nil
}
// waitUntilDeploymentSettled -- Waits until ready, failure or timeout
func waitUntilDeploymentSettled(clientset *kubernetes.Clientset, namespace string, name string, timeout int64) (state string, err error) {
fieldSelector := strings.Join([]string{"metadata.name", name}, "=")
watchOptions := meta.ListOptions{
FieldSelector: fieldSelector,
Watch: true,
}
watcher, error := clientset.AppsV1().Deployments(namespace).Watch(watchOptions)
liveDeployment, error := clientset.AppsV1().Deployments(namespace).Get(name, meta.GetOptions{})
log.Printf("📦 Unavailable replicas: %d", liveDeployment.Status.UnavailableReplicas)
log.Printf("📦 %s", liveDeployment)
if liveDeployment.Status.UnavailableReplicas == 0 {
return "📦 Updated", error
}
i := 0
for {
event := <-watcher.ResultChan()
deployment := event.Object.(*appv1.Deployment)
if liveDeployment.Status.UnavailableReplicas == 0 {
return "📦 Updated", error
}
log.Printf("📦 Unavailable replicas: %d", deployment.Status.UnavailableReplicas)
i++
}
return "failed", error
}
+75 -13
View File
@@ -1,5 +1,5 @@
---
date: 2019-09-26T00:00:00+00:00
date: 2019-09-27T00:00:00+00:00
title: Kubernetes
author: danielgormly
tags: [ kubernetes, deployment, configmap ]
@@ -8,14 +8,14 @@ logo: kubernetes.svg
image: danielgormly/drone-plugin-kube
---
Updates Kubernetes deployments from templates & configMaps from files. This plugin will either create or update existing resources dependent on their presence. It will wait for deployments before it progresses.
Updates Kubernetes resources (deployments, ingresses, services, configmap - with binary data). This plugin will either create or update existing resources dependent on their presence. It will wait for deployments before it progresses.
Create or update deployment
```yaml
pipeline:
- name: Deploy app
image: danielgormly/drone-plugin-kube:0.0.1
image: danielgormly/drone-plugin-kube:0.2.0
settings:
template: path/to/deployment.yaml # relative to repo root
ca: LS0tLS1... # BASE64 encoded string of the K8s CA cert
@@ -29,7 +29,7 @@ Create or update config-map from a single file
```diff
pipeline:
- name: Deploy app
image: danielgormly/drone-plugin-kube:0.0.1
image: danielgormly/drone-plugin-kube:0.2.0
settings:
- template: path/to/deployment.yaml
+ template: path/to/config-map.yaml
@@ -40,15 +40,77 @@ pipeline:
from_secret: kubernetes_token
```
# Secret Reference
kubernetes_token
: Kubernetes service account token (Not base64 encoded)
# Parameter Reference
ca
: Base-64 encoded string of the K8s CA cert
| Value | Key |
|------------------|------------------------------------------------------------------------------------------------------------------------------|
| ca | Base-64 encoded string of the K8s CA cert |
| server | https://10.0.0.20:6443 |
| namespace | Namespace to deploy to |
| kubernetes_token | Kubernetes service account token (Not base64 encoded) |
| template | Path to Kubernetes yaml based definition file (Configmap or Deployment). Relative to Git basedir. |
| configmap_file | Path to file containing data to inject in configmap (They configmap key that contains the data will be the filename) |
| * | Other parameters will be made available for interpolation within yaml templates (upper-case will be converted to lower-case) |
server
: https://10.0.0.20:6443
## Template Substitution
You can substitute values in the deployment template files. But first you have to define the variables.
```yaml {4-5}
- name: Deploy K8s - Workload
image: danielgormly/drone-plugin-kube:0.0.2
settings:
build_number: ${DRONE_BUILD_NUMBER}
template: deployment.yaml
ca:
from_secret: k8s_cert
server:
from_secret: k8s_server
token:
from_secret: k8s_token
```
You can substitute the following values between ```{{ }}``` in your deployment template.
Example `deployment.yaml`:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: wiki
labels:
app: wiki
name: wiki
spec:
replicas: 1
selector:
matchLabels:
app: wiki
template:
metadata:
labels:
app: wiki
spec:
containers:
- image: registry.example.com/wiki:{{ build_number }}
imagePullPolicy: Always
name: wiki
```
### Template Reference
| Key | Value |
|---------------|----------------------------------------------------------------------------------|
| repo.owner | repository owner |
| repo.name | repository name |
| build.status | build status type enumeration, either `success` or `failure` |
| build.event | build event type enumeration, one of `push`, `pull_request`, `tag`, `deployment` |
| build.number | build number |
| build.commit | git sha for current commit |
| build.branch | git branch for current commit |
| build.tag | git tag for current commit |
| build.ref | git ref for current commit |
| build.author | git author for current commit |
| build.link | link the the build results in drone |
| build.created | unix timestamp for build creation |
| build.started | unix timestamp for build started |
Generated
-243
View File
@@ -1,243 +0,0 @@
hash: 0a6476e347f6e80ed358c4dadb03f9a46c1acac36f211864199ff09a4325ec94
updated: 2019-08-27T11:44:06.143731+10:00
imports:
- name: github.com/aymerick/raymond
version: b565731e1464263de0bda75f2e45d97b54b60110
subpackages:
- ast
- lexer
- parser
- name: github.com/davecgh/go-spew
version: 8991bc29aa16c548c550c7ff78260e27b9ab7c73
subpackages:
- spew
- name: github.com/gogo/protobuf
version: 4c00d2f19fb91be5fecd8681fa83450a2a979e69
subpackages:
- proto
- sortkeys
- name: github.com/golang/protobuf
version: aa810b61a9c79d51363740d207bb46cf8e620ed5
subpackages:
- proto
- ptypes
- ptypes/any
- ptypes/duration
- ptypes/timestamp
- name: github.com/google/gofuzz
version: f140a6486e521aad38f5917de355cbf147cc0496
- name: github.com/googleapis/gnostic
version: 0c5108395e2debce0d731cf0287ddf7242066aba
subpackages:
- OpenAPIv2
- compiler
- extensions
- name: github.com/imdario/mergo
version: 9316a62528ac99aaecb4e47eadd6dc8aa6533d58
- name: github.com/json-iterator/go
version: 27518f6661eba504be5a7a9a9f6d9460d892ade3
- name: github.com/modern-go/concurrent
version: bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94
- name: github.com/modern-go/reflect2
version: 94122c33edd36123c84d5368cfb2b69df93a0ec8
- name: github.com/spf13/pflag
version: 298182f68c66c05229eb03ac171abe6e309ee79a
- name: golang.org/x/crypto
version: de0752318171da717af4ce24d0a2e8626afaeb11
subpackages:
- ssh/terminal
- name: golang.org/x/net
version: cdfb69ac37fc6fa907650654115ebebb3aae2087
subpackages:
- context
- http/httpguts
- http2
- http2/hpack
- idna
- name: golang.org/x/oauth2
version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4
subpackages:
- google
- internal
- jws
- jwt
- name: golang.org/x/sys
version: 3b5209105503162ded1863c307ac66fec31120dd
subpackages:
- unix
- windows
- name: golang.org/x/text
version: e6919f6577db79269a6443b9dc46d18f2238fb5d
subpackages:
- secure/bidirule
- transform
- unicode/bidi
- unicode/norm
- name: golang.org/x/time
version: f51c12702a4d776e4c1fa9b0fabab841babae631
subpackages:
- rate
- name: google.golang.org/appengine
version: fb139bde60fa77cede04f226b4d5a3cf68dcce27
subpackages:
- internal
- internal/base
- internal/datastore
- internal/log
- internal/remote_api
- internal/urlfetch
- urlfetch
- name: gopkg.in/inf.v0
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
- name: gopkg.in/yaml.v2
version: 51d6538a90f86fe93ac480b35f37b2be17fef232
- name: k8s.io/api
version: 40a48860b5abbba9aa891b02b32da429b08d96a0
subpackages:
- admissionregistration/v1beta1
- apps/v1
- apps/v1beta1
- apps/v1beta2
- auditregistration/v1alpha1
- authentication/v1
- authentication/v1beta1
- authorization/v1
- authorization/v1beta1
- autoscaling/v1
- autoscaling/v2beta1
- autoscaling/v2beta2
- batch/v1
- batch/v1beta1
- batch/v2alpha1
- certificates/v1beta1
- coordination/v1
- coordination/v1beta1
- core/v1
- events/v1beta1
- extensions/v1beta1
- networking/v1
- networking/v1beta1
- node/v1alpha1
- node/v1beta1
- policy/v1beta1
- rbac/v1
- rbac/v1alpha1
- rbac/v1beta1
- scheduling/v1
- scheduling/v1alpha1
- scheduling/v1beta1
- settings/v1alpha1
- storage/v1
- storage/v1alpha1
- storage/v1beta1
- name: k8s.io/apimachinery
version: d7deff9243b165ee192f5551710ea4285dcfd615
subpackages:
- pkg/api/errors
- pkg/api/meta
- pkg/api/resource
- pkg/apis/meta/v1
- pkg/apis/meta/v1/unstructured
- pkg/apis/meta/v1beta1
- pkg/conversion
- pkg/conversion/queryparams
- pkg/fields
- pkg/labels
- pkg/runtime
- pkg/runtime/schema
- pkg/runtime/serializer
- pkg/runtime/serializer/json
- pkg/runtime/serializer/protobuf
- pkg/runtime/serializer/recognizer
- pkg/runtime/serializer/streaming
- pkg/runtime/serializer/versioning
- pkg/selection
- pkg/types
- pkg/util/clock
- pkg/util/errors
- pkg/util/framer
- pkg/util/intstr
- pkg/util/json
- pkg/util/naming
- pkg/util/net
- pkg/util/runtime
- pkg/util/sets
- pkg/util/validation
- pkg/util/validation/field
- pkg/util/yaml
- pkg/version
- pkg/watch
- third_party/forked/golang/reflect
- name: k8s.io/client-go
version: 6ee68ca5fd8355d024d02f9db0b3b667e8357a0f
subpackages:
- discovery
- kubernetes
- kubernetes/scheme
- kubernetes/typed/admissionregistration/v1beta1
- kubernetes/typed/apps/v1
- kubernetes/typed/apps/v1beta1
- kubernetes/typed/apps/v1beta2
- kubernetes/typed/auditregistration/v1alpha1
- kubernetes/typed/authentication/v1
- kubernetes/typed/authentication/v1beta1
- kubernetes/typed/authorization/v1
- kubernetes/typed/authorization/v1beta1
- kubernetes/typed/autoscaling/v1
- kubernetes/typed/autoscaling/v2beta1
- kubernetes/typed/autoscaling/v2beta2
- kubernetes/typed/batch/v1
- kubernetes/typed/batch/v1beta1
- kubernetes/typed/batch/v2alpha1
- kubernetes/typed/certificates/v1beta1
- kubernetes/typed/coordination/v1
- kubernetes/typed/coordination/v1beta1
- kubernetes/typed/core/v1
- kubernetes/typed/events/v1beta1
- kubernetes/typed/extensions/v1beta1
- kubernetes/typed/networking/v1
- kubernetes/typed/networking/v1beta1
- kubernetes/typed/node/v1alpha1
- kubernetes/typed/node/v1beta1
- kubernetes/typed/policy/v1beta1
- kubernetes/typed/rbac/v1
- kubernetes/typed/rbac/v1alpha1
- kubernetes/typed/rbac/v1beta1
- kubernetes/typed/scheduling/v1
- kubernetes/typed/scheduling/v1alpha1
- kubernetes/typed/scheduling/v1beta1
- kubernetes/typed/settings/v1alpha1
- kubernetes/typed/storage/v1
- kubernetes/typed/storage/v1alpha1
- kubernetes/typed/storage/v1beta1
- pkg/apis/clientauthentication
- pkg/apis/clientauthentication/v1alpha1
- pkg/apis/clientauthentication/v1beta1
- pkg/version
- plugin/pkg/client/auth/exec
- rest
- rest/watch
- tools/auth
- tools/clientcmd
- tools/clientcmd/api
- tools/clientcmd/api/latest
- tools/clientcmd/api/v1
- tools/metrics
- tools/reference
- transport
- util/cert
- util/connrotation
- util/flowcontrol
- util/homedir
- util/keyutil
- name: k8s.io/klog
version: 3ca30a56d8a775276f9cdae009ba326fdc05af7f
- name: k8s.io/utils
version: c2654d5206da6b7b6ace12841e8f359bb89b443c
subpackages:
- buffer
- integer
- trace
- name: sigs.k8s.io/yaml
version: fd68e9863619f6ec2fdd8625fe1f02e7c877e480
testImports: []
-5
View File
@@ -1,5 +0,0 @@
package: github.com/danielgormly/kubano
import:
- package: k8s.io/client-go
version: v11.0.0
- package: github.com/aymerick/raymond
+16
View File
@@ -0,0 +1,16 @@
module github.com/polygon-io/drone-plugin-kube
go 1.13
require (
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible
github.com/gogo/protobuf v1.2.2-0.20190818160850-4c00d2f19fb9 // indirect
github.com/json-iterator/go v1.1.7 // indirect
github.com/spf13/pflag v1.0.3 // indirect
google.golang.org/appengine v1.6.2-0.20190801181406-fb139bde60fa // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
k8s.io/api v0.0.0-20190819141258-3544db3b9e44
k8s.io/apimachinery v0.0.0-20190817020851-f2f3a405f61d
k8s.io/client-go v0.0.0-20190819141724-e14f31a72a77
k8s.io/klog v0.4.0 // indirect
)
+118
View File
@@ -0,0 +1,118 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.2-0.20190818160850-4c00d2f19fb9 h1:ACDM6sadFsshhlBc2RXIEFJqVY3uChLkeWBZhlcc1pA=
github.com/gogo/protobuf v1.2.2-0.20190818160850-4c00d2f19fb9/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68=
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c h1:+EXw7AwNOKzPFXMZ1yNjO40aWCh3PIquJB2fYlv9wcs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g=
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.2-0.20190801181406-fb139bde60fa h1:MnE5UP0nRrHc2tdEcOM2SIf8s1GWYfVetU/nVFDQf/U=
google.golang.org/appengine v1.6.2-0.20190801181406-fb139bde60fa/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.0.0-20190819141258-3544db3b9e44 h1:7Gz7/nQ7X2qmPXMyN0bNq7Zm9Uip+UnFuMZTd2l3vms=
k8s.io/api v0.0.0-20190819141258-3544db3b9e44/go.mod h1:AOxZTnaXR/xiarlQL0JUfwQPxjmKDvVYoRp58cA7lUo=
k8s.io/apimachinery v0.0.0-20190817020851-f2f3a405f61d h1:7Kns6qqhMAQWvGkxYOLSLRZ5hJO0/5pcE5lPGP2fxUw=
k8s.io/apimachinery v0.0.0-20190817020851-f2f3a405f61d/go.mod h1:3jediapYqJ2w1BFw7lAZPCx7scubsTfosqHkhXCWJKw=
k8s.io/client-go v0.0.0-20190819141724-e14f31a72a77 h1:w1BoabVnPpPqQCY3sHK4qVwa12Lk8ip1pKMR1C+qbdo=
k8s.io/client-go v0.0.0-20190819141724-e14f31a72a77/go.mod h1:DmkJD5UDP87MVqUQ5VJ6Tj9Oen8WzXPhk3la4qpyG4g=
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ=
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4=
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+2 -5
View File
@@ -22,7 +22,7 @@ func (p Plugin) CreateKubeClient() (*kubernetes.Clientset, error) {
} else {
ca, err := base64.StdEncoding.DecodeString(p.KubeConfig.Ca)
if err != nil {
log.Fatal(err)
log.Printf("possible corrupted CA, or not base64 encoded: %s\n", err)
}
clusterConfig.CertificateAuthorityData = ca
}
@@ -39,10 +39,7 @@ func (p Plugin) CreateKubeClient() (*kubernetes.Clientset, error) {
clientBuilder := clientcmd.NewNonInteractiveClientConfig(*config, "default", &clientcmd.ConfigOverrides{}, nil)
actualCfg, err := clientBuilder.ClientConfig()
if err != nil {
log.Fatal(err)
}
if err != nil {
fmt.Printf("Unexpected error: %v", err)
return nil, fmt.Errorf("client builder client config; %w", err)
}
return kubernetes.NewForConfig(actualCfg)
}
+1 -1
View File
@@ -19,7 +19,7 @@ func main() {
},
}
fmt.Printf(os.Getenv("PLUGIN_SKIP_TLS"))
fmt.Println("kubano v0.0.1 https://github.com/danielgormly/drone-plugin-kube")
fmt.Println("danielgormly/drone-plugin-kube@0.2.0 https://github.com/danielgormly/drone-plugin-kube")
err := plugin.Exec()
if err != nil {
log.Fatalf("⛔️ Fatal error: \n%s", err)
+41 -9
View File
@@ -9,8 +9,9 @@ import (
"strings"
"github.com/aymerick/raymond"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
appV1 "k8s.io/api/apps/v1"
coreV1 "k8s.io/api/core/v1"
v1BetaV1 "k8s.io/api/extensions/v1beta1"
"k8s.io/client-go/kubernetes/scheme"
)
@@ -42,9 +43,6 @@ func (p Plugin) Exec() error {
if p.KubeConfig.Ca == "" {
return errors.New("PLUGIN_CA is not defined")
}
if p.KubeConfig.Namespace == "" {
p.KubeConfig.Namespace = "default"
}
if p.Template == "" {
return errors.New("PLUGIN_TEMPLATE, or template must be defined")
}
@@ -58,47 +56,81 @@ func (p Plugin) Exec() error {
key := strings.ToLower(matches[1])
ctx[key] = matches[2]
}
re = regexp.MustCompile(`^DRONE_(.*)=(.*)`)
if re.MatchString(value) {
matches := re.FindStringSubmatch(value)
key := strings.ToLower(matches[1])
ctx[key] = matches[2]
}
}
// Grab template from filesystem
raw, err := ioutil.ReadFile(p.Template)
if err != nil {
log.Print("⛔️ Error reading template file:")
return err
}
// Parse template
templateYaml, err := raymond.Render(string(raw), ctx)
if err != nil {
return err
}
// Connect to Kubernetes
clientset, err := p.CreateKubeClient()
if err != nil {
return err
}
// Decode
kubernetesObject, _, err := scheme.Codecs.UniversalDeserializer().Decode([]byte(templateYaml), nil, nil)
if err != nil {
log.Print("⛔️ Error decoding template into valid Kubernetes object:")
return err
}
switch o := kubernetesObject.(type) {
case *appv1.Deployment:
case *appV1.Deployment:
log.Print("📦 Resource type: Deployment")
if p.KubeConfig.Namespace == "" {
p.KubeConfig.Namespace = o.Namespace
}
err = CreateOrUpdateDeployment(clientset, p.KubeConfig.Namespace, o)
if err != nil {
return err
}
// Watch for successful update
log.Print("📦 Watching deployment until no unavailable replicas.")
state, watchErr := waitUntilDeploymentSettled(clientset, p.KubeConfig.Namespace, o.ObjectMeta.Name, 120)
log.Printf("%s", state)
return watchErr
case *corev1.ConfigMap:
case *coreV1.ConfigMap:
if p.KubeConfig.Namespace == "" {
p.KubeConfig.Namespace = o.Namespace
}
log.Print("📦 Resource type: ConfigMap")
err = ApplyConfigMapFromFile(clientset, p.KubeConfig.Namespace, o, p.ConfigMapFile)
case *coreV1.Service:
if p.KubeConfig.Namespace == "" {
p.KubeConfig.Namespace = o.Namespace
}
log.Print("Resource type: Service")
err = ApplyService(clientset, p.KubeConfig.Namespace, o)
case *v1BetaV1.Ingress:
if p.KubeConfig.Namespace == "" {
p.KubeConfig.Namespace = o.Namespace
}
log.Print("Resource type: Ingress")
err = ApplyIngress(clientset, p.KubeConfig.Namespace, o)
default:
err = errors.New("⛔️ This plugin doesn't support that resource type")
return err
return errors.New("⛔️ This plugin doesn't support that resource type")
}
return err
}
+192
View File
@@ -0,0 +1,192 @@
package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"path/filepath"
"strings"
"time"
appV1 "k8s.io/api/apps/v1"
coreV1 "k8s.io/api/core/v1"
v1BetaV1 "k8s.io/api/extensions/v1beta1"
kubeErrors "k8s.io/apimachinery/pkg/api/errors"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
// CreateOrUpdateDeployment -- Checks if deployment already exists, updates if it does, creates if it doesn't
func CreateOrUpdateDeployment(clientset *kubernetes.Clientset, namespace string, deployment *appV1.Deployment) error {
deploymentExists, err := deploymentExists(clientset, namespace, deployment.Name)
if err != nil {
return err
}
if deploymentExists {
log.Printf("📦 Found existing deployment '%s'. Updating.", deployment.Name)
_, err = clientset.AppsV1().Deployments(namespace).Update(deployment)
return err
}
log.Printf("📦 Creating new deployment '%s'. Updating.", deployment.Name)
_, err = clientset.AppsV1().Deployments(namespace).Create(deployment)
return err
}
// deploymentExists -- Updates given deployment in Kubernetes
func deploymentExists(clientset *kubernetes.Clientset, namespace string, name string) (bool, error) {
_, err := clientset.AppsV1().Deployments(namespace).Get(name, metaV1.GetOptions{})
if err != nil {
// TODO: Only conver to StatusError if the error is in fact a status error
statusError, ok := err.(*kubeErrors.StatusError)
if ok && statusError.Status().Code == http.StatusNotFound {
return false, nil
}
return false, err
}
return true, nil
}
const (
stateUpdated = "📦 Updated"
stateFailed = "⛔️ Failed"
)
// waitUntilDeploymentSettled -- Waits until ready, failure or timeout
func waitUntilDeploymentSettled(clientset *kubernetes.Clientset, namespace string, name string, timeoutInSeconds int64) (string, error) {
fieldSelector := strings.Join([]string{"metadata.name", name}, "=")
watchOptions := metaV1.ListOptions{
FieldSelector: fieldSelector,
Watch: true,
}
watcher, err := clientset.AppsV1().Deployments(namespace).Watch(watchOptions)
if err != nil {
return stateFailed, fmt.Errorf("watch deployment; %w", err)
}
liveDeployment, err := clientset.AppsV1().Deployments(namespace).Get(name, metaV1.GetOptions{})
if err != nil {
return stateFailed, fmt.Errorf("get deployment; %w", err)
}
log.Printf("📦 Unavailable replicas: %d", liveDeployment.Status.UnavailableReplicas)
if liveDeployment.Status.UnavailableReplicas == 0 {
return "📦 Updated", nil
}
timer := time.NewTimer(time.Duration(timeoutInSeconds) * time.Second)
for {
select {
case <-timer.C:
return stateFailed, errors.New("deployment watcher timed out. Something is wrong")
case event := <-watcher.ResultChan():
deployment := event.Object.(*appV1.Deployment)
if deployment.Status.UnavailableReplicas == 0 {
return stateUpdated, nil
}
log.Printf("📦 Unavailable replicas: %d", deployment.Status.UnavailableReplicas)
}
}
}
// ApplyService creates a service if it doesn't exists, updates it if it does
func ApplyService(clientset *kubernetes.Clientset, namespace string, service *coreV1.Service) error {
existingService, exists, err := getService(clientset, namespace, service.Name)
if err != nil {
return err
}
if exists {
_, err = clientset.CoreV1().Services(namespace).Update(existingService)
return err
}
_, err = clientset.CoreV1().Services(namespace).Create(service)
return err
}
// getService returns a service object, or false if it doesn't exist
func getService(clientset *kubernetes.Clientset, namespace string, name string) (*coreV1.Service, bool, error) {
service, err := clientset.CoreV1().Services(namespace).Get(name, metaV1.GetOptions{})
if err != nil {
statusError, ok := err.(*kubeErrors.StatusError)
if ok && statusError.Status().Code == http.StatusNotFound {
return nil, false, nil
}
return nil, false, err
}
return service, true, nil
}
// ApplyConfigMapFromFile -- Updates given deployment in Kubernetes
func ApplyConfigMapFromFile(clientset *kubernetes.Clientset, namespace string, configMap *coreV1.ConfigMap, path string) error {
log.Printf("📦 Reading contents of %s", path)
_, filename := filepath.Split(path)
fileContents, err := ioutil.ReadFile(path)
if err != nil {
return err
}
configMapData := make(map[string]string)
configMapData[filename] = string(fileContents)
configMap.Data = configMapData
// Check if deployment exists
exists, err := configMapExists(clientset, namespace, configMap.Name)
if err != nil {
return err
}
if exists {
log.Printf("📦 Found existing deployment. Updating %s.", configMap.Name)
_, err = clientset.CoreV1().ConfigMaps(namespace).Update(configMap)
return err
}
_, err = clientset.CoreV1().ConfigMaps(namespace).Create(configMap)
return err
}
// configMapExists -- Updates given deployment in Kubernetes
func configMapExists(clientset *kubernetes.Clientset, namespace string, name string) (bool, error) {
_, err := clientset.CoreV1().ConfigMaps(namespace).Get(name, metaV1.GetOptions{})
if err != nil {
statusError, ok := err.(*kubeErrors.StatusError)
if ok && statusError.Status().Code == http.StatusNotFound {
return false, nil
}
return false, err
}
return true, nil
}
func ApplyIngress(clientset *kubernetes.Clientset, namespace string, ingress *v1BetaV1.Ingress) error {
_, exists, err := getIngress(clientset, namespace, ingress.Name)
if err != nil {
return err
}
if !exists {
_, err = clientset.ExtensionsV1beta1().Ingresses(namespace).Create(ingress)
return err
}
_, err = clientset.ExtensionsV1beta1().Ingresses(namespace).Update(ingress)
return err
}
func getIngress(clientset *kubernetes.Clientset, namespace string, name string) (*v1BetaV1.Ingress, bool, error) {
ingress, err := clientset.ExtensionsV1beta1().Ingresses(namespace).Get(name, metaV1.GetOptions{})
if err != nil {
statusError, ok := err.(*kubeErrors.StatusError)
if ok && statusError.Status().Code == http.StatusNotFound {
return nil, false, nil
}
return nil, false, err
}
return ingress, true, nil
}
+1
View File
@@ -4,6 +4,7 @@ export PLUGIN_TEMPLATE=test/configmap.template.yaml
export PLUGIN_CONFIGMAP_FILE=test/sample-config-data
export PLUGIN_NAME=drone-kube-test
export PLUGIN_COMMIT=a5b81d0f
export PLUGIN_NAMESPACE=default
go build -o build/kubano
export $(cat .env | xargs) && ./build/kubano
+1
View File
@@ -2,6 +2,7 @@
export PLUGIN_TEMPLATE=test/deployment.template.yaml
export PLUGIN_NAME=drone-kube-test
export PLUGIN_NAMESPACE=default
go build -o build/kubano
export $(cat .env | xargs) && ./build/kubano
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
export PLUGIN_TEMPLATE=test/service.template.yaml
export PLUGIN_NAME=drone-kube-test
export PLUGIN_NAMESPACE=default
go build -o build/kubano
export $(cat .env | xargs) && ./build/kubano
# docker run --env-file=.env drone-kubano
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: wiki-http
namespace: default
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: wiki
type: ClusterIP