Files

196 lines
4.7 KiB
Go

package main
import (
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"time"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/pkg/api"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
"k8s.io/client-go/pkg/runtime"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
utilyaml "k8s.io/kubernetes/pkg/util/yaml"
)
type (
Repo struct {
Owner string
Name string
}
Build struct {
Tag string
Event string
Number int
Commit string
Ref string
Branch string
Author string
Status string
Link string
Started int64
Created int64
}
Job struct {
Started int64
}
Config struct {
Ca string
Server string
Token string
Namespace string
Template string
}
Plugin struct {
Repo Repo
Build Build
Config Config
Job Job
}
)
func (p Plugin) Exec() error {
if p.Config.Server == "" {
log.Fatal("KUBE_SERVER is not defined")
}
if p.Config.Token == "" {
log.Fatal("KUBE_TOKEN is not defined")
}
if p.Config.Ca == "" {
log.Fatal("KUBE_CA is not defined")
}
if p.Config.Namespace == "" {
p.Config.Namespace = "default"
}
if p.Config.Template == "" {
log.Fatal("KUBE_TEMPLATE, or template must be defined")
}
// connect to Kubernetes
clientset, err := p.createKubeClient()
if err != nil {
log.Fatal(err.Error())
}
// parse the template file and do substitutions
txt, err := openAndSub(p.Config.Template, p)
if err != nil {
return err
}
// convert txt back to []byte and convert to json
json, err := utilyaml.ToJSON([]byte(txt))
if err != nil {
return err
}
var dep v1beta1.Deployment
e := runtime.DecodeInto(api.Codecs.UniversalDecoder(), json, &dep)
if e != nil {
log.Fatal("Error decoding yaml file to json", e)
}
// check and see if there is a deployment already. If there is, update it.
oldDep, err := findDeployment(dep.ObjectMeta.Name, dep.ObjectMeta.Namespace, clientset)
if err != nil {
return err
}
if oldDep.ObjectMeta.Name == dep.ObjectMeta.Name {
// update the existing deployment, ignore the deployment that it comes back with
_, err = clientset.ExtensionsV1beta1().Deployments(p.Config.Namespace).Update(&dep)
return err
}
// create the new deployment since this never existed.
_, err = clientset.ExtensionsV1beta1().Deployments(p.Config.Namespace).Create(&dep)
return err
}
func findDeployment(depName string, namespace string, c *kubernetes.Clientset) (v1beta1.Deployment, error) {
if namespace == "" {
namespace = "default"
}
var d v1beta1.Deployment
deployments, err := listDeployments(c, namespace)
if err != nil {
return d, err
}
for _, thisDep := range deployments {
if thisDep.ObjectMeta.Name == depName {
return thisDep, err
}
}
return d, err
}
// List the deployments
func listDeployments(clientset *kubernetes.Clientset, namespace string) ([]v1beta1.Deployment, error) {
// docs on this:
// https://github.com/kubernetes/client-go/blob/master/pkg/apis/extensions/types.go
deployments, err := clientset.ExtensionsV1beta1().Deployments(namespace).List(v1.ListOptions{})
if err != nil {
log.Fatal(err.Error())
}
return deployments.Items, err
}
// open up the template and then sub variables in. Handlebar stuff.
func openAndSub(templateFile string, p Plugin) (string, error) {
t, err := ioutil.ReadFile(templateFile)
if err != nil {
return "", err
}
//potty humor! Render trim toilet paper! Ha ha, so funny.
return RenderTrim(string(t), p)
}
// create the connection to kubernetes based on parameters passed in.
// the kubernetes/client-go project is really hard to understand.
func (p Plugin) createKubeClient() (*kubernetes.Clientset, error) {
ca, err := base64.StdEncoding.DecodeString(p.Config.Ca)
config := clientcmdapi.NewConfig()
config.Clusters["drone"] = &clientcmdapi.Cluster{
Server: p.Config.Server,
CertificateAuthorityData: ca,
}
config.AuthInfos["drone"] = &clientcmdapi.AuthInfo{
Token: p.Config.Token,
}
config.Contexts["drone"] = &clientcmdapi.Context{
Cluster: "drone",
AuthInfo: "drone",
}
//config.Clusters["drone"].CertificateAuthorityData = ca
config.CurrentContext = "drone"
clientBuilder := clientcmd.NewNonInteractiveClientConfig(*config, "drone", &clientcmd.ConfigOverrides{}, nil)
actualCfg, err := clientBuilder.ClientConfig()
if err != nil {
log.Fatal(err)
}
return kubernetes.NewForConfig(actualCfg)
}
// Just an example from the client specification. Code not really used.
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)
}
}