- adding the static and release stages to the makefile

- moving the render code into utils
- fixed up the dockerfile for testing
- adding the ignore on release
- adding the fix to makefile
This commit is contained in:
Rohith 2015-09-21 16:35:59 +01:00
parent 30b1e652b1
commit 12c5c37746
6 changed files with 157 additions and 136 deletions

3
.gitignore vendored
View file

@ -2,7 +2,8 @@
*.iml
*.swp
.idea/
build/
bin/
release/
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o

View file

@ -1,6 +1,6 @@
FROM gliderlabs/alpine:latest
MAINTAINER Rohith <gambol99@gmail.com>
ADD build/vault-sidekick /vault-sidekick
ADD bin/vault-sidekick /vault-sidekick
ENTRYPOINT [ "/vault-sidekick" ]

View file

@ -2,22 +2,30 @@
NAME=vault-sidekick
AUTHOR=gambol99
HARDWARE=$(shell uname -m)
VERSION=$(shell awk '/const Version/ { print $$4 }' version.go | sed 's/"//g')
VERSION=$(shell awk '/Version =/ { print $$3 }' version.go | sed 's/"//g')
.PHONY: test examples authors changelog build docker
.PHONY: test authors changelog build docker static release
default: build
build:
mkdir -p build
go build -o build/${NAME}
mkdir -p bin
go build -o bin/${NAME}
docker: build
sudo docker build -t ${AUTHOR}/${NAME} .
sudo docker build -t ${AUTHOR}/${NAME} .
static:
mkdir -p bin
CGO_ENABLED=0 GOOS=linux go build -a -tags netgo -ldflags '-w' -o bin/${NAME}
release: static
mkdir -p release
gzip -c bin/${NAME} > release/${NAME}_${VERSION}_linux_${HARDWARE}.gz
rm -f release/${NAME}
clean:
rm -rf ./build 2>/dev/null
rm -rf ./bin 2>/dev/null
authors:
git log --format='%aN <%aE>' | sort -u > AUTHORS

117
main.go
View file

@ -17,17 +17,11 @@ limitations under the License.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/signal"
"strings"
"syscall"
"github.com/golang/glog"
"gopkg.in/yaml.v2"
)
func main() {
@ -35,7 +29,6 @@ func main() {
if err := parseOptions(); err != nil {
showUsage("invalid options, %s", err)
}
// step: create a client to vault
vault, err := newVaultService(options.vaultURL)
if err != nil {
@ -48,7 +41,7 @@ func main() {
// step: create a channel to receive events upon and add our resources for renewal
ch := make(chan vaultResourceEvent, 10)
// step: add each of the resources to the service processor
for _, rn := range options.resources.items {
// step: valid the resource
if err := rn.isValid(); err != nil {
@ -62,117 +55,11 @@ func main() {
select {
case evt := <-ch:
// step: write the secret to the output directory
go processResource(evt.resource, evt.secret)
go writeResource(evt.resource, evt.secret)
case <-signalChannel:
glog.Infof("recieved a termination signal, shutting down the service")
os.Exit(0)
}
}
}
// processResource ... write the resource to file, converting into the selected format
func processResource(rn *vaultResource, data map[string]interface{}) error {
var content []byte
var err error
// step: determine the resource path
resourcePath := rn.filename()
if !strings.HasPrefix(resourcePath, "/") {
resourcePath = fmt.Sprintf("%s/%s", options.outputDir, resourcePath)
}
// step: get the output format
glog.V(3).Infof("saving resource: %s, format: %s", rn, rn.format)
switch rn.format {
case "yaml":
// marshall the content to yaml
if content, err = yaml.Marshal(data); err != nil {
return err
}
case "ini":
var buf bytes.Buffer
for key, val := range data {
buf.WriteString(fmt.Sprintf("%s = %s\n", key, val))
}
content = buf.Bytes()
// Less of a format and more of a standard naming scheme
case "cert":
files := map[string]string{
"certificate": "crt",
"issuing_ca": "ca",
"private_key": "key",
}
for key, suffix := range files {
filename := fmt.Sprintf("%s.%s", resourcePath, suffix)
content, found := data[key]
if !found {
continue
}
// step: write the file
if err := writeFile(filename, []byte(fmt.Sprintf("%s", content))); err != nil {
glog.Errorf("failed to write resource: %s, elemment: %s, filename: %s, error: %s", rn, suffix, filename, err)
continue
}
}
return nil
case "csv":
var buf bytes.Buffer
for key, val := range data {
buf.WriteString(fmt.Sprintf("%s,%s\n", key, val))
}
content = buf.Bytes()
case "txt":
keys := getKeys(data)
if len(keys) > 1 {
// step: for plain formats we need to iterate the keys and produce a file per key
for suffix, content := range data {
filename := fmt.Sprintf("%s.%s", resourcePath, suffix)
if err := writeFile(filename, []byte(fmt.Sprintf("%s", content))); err != nil {
glog.Errorf("failed to write resource: %s, elemment: %s, filename: %s, error: %s",
rn, suffix, filename, err)
continue
}
}
return nil
}
// step: we only have the one key, so will write plain
value, _ := data[keys[0]]
content = []byte(fmt.Sprintf("%s", value))
case "json":
if content, err = json.MarshalIndent(data, "", " "); err != nil {
return err
}
}
// step: write the content to file
if err := writeFile(resourcePath, content); err != nil {
glog.Errorf("failed to write the resource: %s to file: %s, error: %s", rn, resourcePath, err)
return err
}
return nil
}
// writeFile ... writes the content of a file
func writeFile(filename string, content []byte) error {
// step: are we dry running?
if options.dryRun {
glog.Infof("dry-run: filename: %s, content:", filename)
fmt.Printf("%s\n", string(content))
return nil
}
err := ioutil.WriteFile(filename, content, 0660)
if err != nil {
return err
}
return nil
}

133
utils.go
View file

@ -17,15 +17,19 @@ limitations under the License.
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"math/rand"
"os"
"time"
"io/ioutil"
"encoding/json"
"gopkg.in/yaml.v2"
"path"
"strings"
"time"
"github.com/golang/glog"
"gopkg.in/yaml.v2"
)
func init() {
@ -54,7 +58,7 @@ func randomWait(min, max int) <-chan time.Time {
// hasKey ... checks to see if a key is present
// key : the key we are looking for
// data : a map of strings to something we are looking at
func hasKey(key string, data map [string]interface{}) bool {
func hasKey(key string, data map[string]interface{}) bool {
_, found := data[key]
return found
}
@ -75,7 +79,7 @@ func readConfigFile(filename string) (map[string]string, error) {
suffix := path.Ext(filename)
switch suffix {
case ".json":
return readJsonFile(filename)
return readJSONFile(filename)
case ".yaml":
return readYamlFile(filename)
case ".yml":
@ -86,7 +90,7 @@ func readConfigFile(filename string) (map[string]string, error) {
// readJsonFile ... read in and unmarshall the data into a map
// filename : the path to the file container the json data
func readJsonFile(filename string) (map[string]string, error) {
func readJSONFile(filename string) (map[string]string, error) {
data := make(map[string]string, 0)
content, err := ioutil.ReadFile(filename)
@ -150,3 +154,118 @@ func fileExists(filename string) (bool, error) {
return true, nil
}
// writeResourceContent ... is resposinle for generate the specific content from the resource
// rn : a point to the vault resource
// data : a map of the related secret associated to the resource
func writeResource(rn *vaultResource, data map[string]interface{}) error {
var content []byte
var err error
// step: determine the resource path
resourcePath := rn.filename()
if !strings.HasPrefix(resourcePath, "/") {
resourcePath = fmt.Sprintf("%s/%s", options.outputDir, resourcePath)
}
// step: get the output format
glog.V(3).Infof("saving resource: %s, format: %s", rn, rn.format)
if rn.format == "yaml" {
// marshall the content to yaml
if content, err = yaml.Marshal(data); err != nil {
return err
}
return writeFile(resourcePath, content)
}
if rn.format == "ini" {
var buf bytes.Buffer
for key, val := range data {
buf.WriteString(fmt.Sprintf("%s = %s\n", key, val))
}
content = buf.Bytes()
return writeFile(resourcePath, content)
}
if rn.format == "cert" {
files := map[string]string{
"certificate": "crt",
"issuing_ca": "ca",
"private_key": "key",
}
for key, suffix := range files {
filename := fmt.Sprintf("%s.%s", resourcePath, suffix)
content, found := data[key]
if !found {
continue
}
// step: write the file
if err := writeFile(filename, []byte(fmt.Sprintf("%s", content))); err != nil {
glog.Errorf("failed to write resource: %s, elemment: %s, filename: %s, error: %s", rn, suffix, filename, err)
continue
}
}
return nil
}
if rn.format == "csv" {
var buf bytes.Buffer
for key, val := range data {
buf.WriteString(fmt.Sprintf("%s,%s\n", key, val))
}
content = buf.Bytes()
return writeFile(resourcePath, content)
}
if rn.format == "txt" {
keys := getKeys(data)
if len(keys) > 1 {
// step: for plain formats we need to iterate the keys and produce a file per key
for suffix, content := range data {
filename := fmt.Sprintf("%s.%s", resourcePath, suffix)
if err := writeFile(filename, []byte(fmt.Sprintf("%s", content))); err != nil {
glog.Errorf("failed to write resource: %s, elemment: %s, filename: %s, error: %s",
rn, suffix, filename, err)
continue
}
}
return nil
}
// step: we only have the one key, so will write plain
value, _ := data[keys[0]]
content = []byte(fmt.Sprintf("%s", value))
return writeFile(resourcePath, content)
}
if rn.format == "json" {
if content, err = json.MarshalIndent(data, "", " "); err != nil {
return err
}
return writeFile(resourcePath, content)
}
return fmt.Errorf("unknown output format: %s", rn.format)
}
// writeFile ... writes the content to a file .. dah
// filename : the path to the file
// content : the content to be written
func writeFile(filename string, content []byte) error {
if options.dryRun {
glog.Infof("dry-run: filename: %s, content:", filename)
fmt.Printf("%s\n", string(content))
return nil
}
return ioutil.WriteFile(filename, content, 0660)
}

View file

@ -23,6 +23,10 @@ import (
"github.com/hashicorp/vault/api"
)
const (
renewalMinimum = 0.8
renewalMaximum = 0.95
)
// watchedResource ... is a resource which is being watched - i.e. when the item is coming up for renewal
// lets grab it and renew the lease
@ -46,15 +50,10 @@ func (r *watchedResource) notifyOnRenewal(ch chan *watchedResource) {
go func() {
// step: check if the resource has a pre-configured renewal time
r.renewalTime = r.resource.update
// step: if the answer is no, we set the notification between 80-95% of the lease time of the secret
if r.renewalTime <= 0 {
glog.V(10).Infof("calculating the renewal between 80-95 pcent of lease time: %d seconds", r.secret.LeaseDuration)
r.renewalTime = time.Duration(getRandomWithin(
int(float64(r.secret.LeaseDuration)*0.8),
int(float64(r.secret.LeaseDuration)*0.95))) * time.Second
r.renewalTime = r.calculateRenewal()
}
glog.V(3).Infof("setting a renewal notification on resource: %s, time: %s", r.resource, r.renewalTime)
// step: wait for the duration
<-time.After(r.renewalTime)
@ -62,3 +61,10 @@ func (r *watchedResource) notifyOnRenewal(ch chan *watchedResource) {
ch <- r
}()
}
// calculateRenewal ... calculate the renewal between
func (r watchedResource) calculateRenewal() time.Duration {
return time.Duration(getRandomWithin(
int(float64(r.secret.LeaseDuration)*renewalMinimum),
int(float64(r.secret.LeaseDuration)*renewalMaximum))) * time.Second
}