Skip to content

Commit

Permalink
Adding initial workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
nextrevision committed Sep 20, 2016
1 parent 25eecf7 commit 28b591b
Show file tree
Hide file tree
Showing 398 changed files with 403,637 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Expand Up @@ -22,3 +22,7 @@ _testmain.go
*.exe
*.test
*.prof

*.swp

kenv
26 changes: 26 additions & 0 deletions .travis.yml
@@ -0,0 +1,26 @@
language: go
go:
- 1.7
cache:
directories:
- vendor
install:
- go get -u github.com/kardianos/govendor
- govendor sync
script:
- govendor vet +local
- govendor test +local
before_deploy:
- GOOS=linux GOARCH=amd64 govendor build -o kenv_linux_amd64
- GOOS=darwin GOARCH=amd64 govendor build -o kenv_darwin_amd64
deploy:
skip_cleanup: true
provider: releases
api_key:
secure: iaBN7XLYLyczNAC1gyby346iCOsNStj6l7Xu0dBrDdSIy82eYkEnxMe0AKJuWD6GyrP8ZWvbiEUnyPCyGLmKaQdEnKJtBb/5kp1AY8/SA9ZF5jEbwwYRPuUXBWM0jvi5Fc7SkHAUSKAXy9S5XTU3+Ag8iTSMDeL5Mm8BpBeA2/WVrHJDw4CcwoCxpODVOFFDKSePjiIWy0AE2zRpC4OONO9CYFKWtrhRpottON2u6QLVZhcvj5x3cRC2ukPN8rXJwCxUGrm1oyIRrZ/6LKBk1yRyA01YfkJqguyV1vLuyTg9/uhG/cfAzPlHPhW5B64+k4QgBgVSfY39eNIe5bGG59O9fQkYQNos1sFc46RrWBRkc8Xc+44uqAacEc+ebkjTL0HbGNnkYGerYLO94QOcnSbzqgB/tt8S5GD2+jeea1TMfxXEEv0aSjzVLmi0B12rd4RabEiOOh5QTzstU6Zglbs6XN07kgjMsU8/IT/bVlcRdEtBhvUl0eMRRHIxey0ipAypgxeL+8i0ab+wM3GincC/08d4W4XfXysHyFYKAs2nX8LBi3QTCVgmVEKE6VNjgAguRrT++9/moS33tU8KxvHnlAiEXSOS0DMGy82PgvlTngWUTenJiXzfbfZfB5XPAHygV8Dd/2q3rupPHzBwp+k/eMhzUrT813wvfqoWA94=
file:
- kenv_linux_amd64
- kenv_darwin_amd64
on:
tags: true
repo: thisendout/kenv
60 changes: 60 additions & 0 deletions README.md
@@ -1,2 +1,62 @@
# kenv

Environment file preprocessor for Kubernetes deployments

kenv injects variables into Kubernetes resource documents by loading a list of files containing variables, and modifying the resource document to include those values. This way, you can dynamically set environment variables without having to template your resource documents.

## Getting Started

[Download](https://github.com/thisendout/kenv/releases/tag/v0.1.0) kenv and run by passing a resource doc (YAML or JSON) to:

STDIN with a pipe:

```
cat fixtures/deployment.yaml | ./kenv -v fixtures/vars.env
```

or as a CLI Arg:

```
./kenv -v fixtures/vars.env fixtures/deployment.yaml
```

## Variables

Variables are stored in files either as simple `key=value` pairs, or YAML pairs (`key: value`). Multiple variable files can be specified by repeating the `-v` flag:

```
./kenv -v fixtures/vars.env -v fixtures/complex.env fixtures/deployment.yaml
```

### YAML

YAML files must be in the following format (nested data types are not currently supported):

```
key1: value1
key2: value2
```

### KV Format

A `key=value` format is also supported, for example:

```
key1=value1
key2=value2
```

## Building

```
govendor sync
go test -v .
```

## TODO

* Support [ConfigMaps](http://kubernetes.io/docs/user-guide/configmap/)
* Support [Secrets](http://kubernetes.io/docs/user-guide/secrets/walkthrough/)
* Support [DaemonSets](http://kubernetes.io/docs/admin/daemons/)
* Support [ReplicationControllers](http://kubernetes.io/docs/user-guide/replication-controller/)
* Support [ReplicaSets](http://kubernetes.io/docs/user-guide/replicasets/)
36 changes: 36 additions & 0 deletions deployment.go
@@ -0,0 +1,36 @@
package main

import (
"encoding/json"

"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)

// InjectVarsDeployment inserts EnvVars into a deployment doc
func InjectVarsDeployment(data []byte, envVars []v1.EnvVar) (string, error) {
deployment := v1beta1.Deployment{}

if err := json.Unmarshal(data, &deployment); err != nil {
return "", err
}

updateContainersDeployment(&deployment, envVars)

data, err := json.MarshalIndent(&deployment, "", " ")

return string(data), err
}

func updateContainersDeployment(deployment *v1beta1.Deployment, envVars []v1.EnvVar) {
podSpec := deployment.Spec.Template.Spec
containers := []v1.Container{}

for _, c := range podSpec.Containers {
c.Env = envVars
containers = append(containers, c)
}

podSpec.Containers = containers
deployment.Spec.Template.Spec = podSpec
}
69 changes: 69 additions & 0 deletions deployment_test.go
@@ -0,0 +1,69 @@
package main

import (
"encoding/json"
"io/ioutil"
"reflect"
"testing"

"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)

func TestInjectVarsDeployment(t *testing.T) {
data, err := ioutil.ReadFile("fixtures/deployment.json")
if err != nil {
t.Fatal(err)
}

envVars := []v1.EnvVar{
v1.EnvVar{
Name: "key1",
Value: "value1",
},
v1.EnvVar{
Name: "key2",
Value: "value2",
},
}

s, err := InjectVarsDeployment(data, envVars)
if err != nil {
t.Fatal(err)
}

if s == "" {
t.Fatalf("got empty string")
}
}

func TestUpdateContainersDeployment(t *testing.T) {
data, err := ioutil.ReadFile("fixtures/deployment.json")
if err != nil {
t.Fatal(err)
}

envVars := []v1.EnvVar{
v1.EnvVar{
Name: "key1",
Value: "value1",
},
v1.EnvVar{
Name: "key2",
Value: "value2",
},
}

deployment := v1beta1.Deployment{}
if err := json.Unmarshal(data, &deployment); err != nil {
t.Fatal(err)
}

updateContainersDeployment(&deployment, envVars)

for _, c := range deployment.Spec.Template.Spec.Containers {
if !reflect.DeepEqual(c.Env, envVars) {
t.Fatalf("container env vars not equal")
}
}
}
1 change: 1 addition & 0 deletions fixtures/complex.env
@@ -0,0 +1 @@
complexKey1=value1=test
46 changes: 46 additions & 0 deletions fixtures/deployment.json
@@ -0,0 +1,46 @@
{
"kind": "Deployment",
"apiVersion": "extensions/v1beta1",
"metadata": {
"name": "nginx",
"creationTimestamp": null,
"labels": {
"app": "nginx"
}
},
"spec": {
"replicas": 3,
"template": {
"metadata": {
"creationTimestamp": null,
"labels": {
"app": "nginx"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest",
"ports": [
{
"containerPort": 80
}
],
"resources": {},
"readinessProbe": {
"httpGet": {
"path": "/",
"port": 80
},
"initialDelaySeconds": 10,
"timeoutSeconds": 1
}
}
]
}
},
"strategy": {}
},
"status": {}
}
24 changes: 24 additions & 0 deletions fixtures/deployment.yaml
@@ -0,0 +1,24 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
timeoutSeconds: 1
2 changes: 2 additions & 0 deletions fixtures/vars.env
@@ -0,0 +1,2 @@
KEY1=VALUE1
key2=value2
1 change: 1 addition & 0 deletions fixtures/vars.yaml
@@ -0,0 +1 @@
KEY1: VALUE1
112 changes: 112 additions & 0 deletions main.go
@@ -0,0 +1,112 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"

"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util/yaml"
)

var (
varsFiles FlagSlice
flagSet *flag.FlagSet
)

func init() {
// workaround to avoid inheriting vendor flags
flagSet = flag.NewFlagSet("kenv", flag.ExitOnError)
flagSet.Var(&varsFiles, "v", "File containing environment variables (repeatable)")
}

func main() {
if err := flagSet.Parse(os.Args[1:]); err != nil {
log.Fatal(err)
}

// take either a doc as a cli arg or stdin
var in *os.File
var err error

switch name := flagSet.Arg(0); {
case name == "":
in = os.Stdin
default:
if in, err = os.Open(name); err != nil {
log.Fatal(err)
}
defer in.Close()
}

// read in doc
data, err := ioutil.ReadAll(in)
if err != nil {
log.Fatal(err)
}

// ensure doc is JSON
data, err = yaml.ToJSON(data)
if err != nil {
log.Fatal(err)
}

vars := Vars{}

// read in vars files
for _, filename := range varsFiles {
v, err := ReadVarsFile(filename)
if err != nil {
log.Fatal(err)
}

vars = append(vars, v...)
}

envVars := vars.toEnvVar()
var doc string

kind, err := getDocKind(data)
if err != nil {
log.Fatal(err)
}

switch kind {
case "Deployment":
doc, err = InjectVarsDeployment(data, envVars)
default:
err = fmt.Errorf("Kind %s not supported\n", kind)
}

if err != nil {
log.Fatal(err)
}

println(doc)
}

// FlagSlice represents a repeatable string flag
type FlagSlice []string

// String returns a string representation of FlagSlice
func (f *FlagSlice) String() string {
return strings.Join(*f, ",")
}

// Set appends a string value to FlagSlice
func (f *FlagSlice) Set(value string) error {
*f = append(*f, value)
return nil
}

func getDocKind(data []byte) (string, error) {
typeMeta := unversioned.TypeMeta{}
if err := json.Unmarshal(data, &typeMeta); err != nil {
return "", err
}
return typeMeta.Kind, nil
}

0 comments on commit 28b591b

Please sign in to comment.