Merge pull request #7 from gambol99/fix/clean_up

- updated the readme
This commit is contained in:
Rohith 2015-09-23 22:41:58 +01:00
commit 232d8dc91f
15 changed files with 242 additions and 232 deletions

View file

@ -2,7 +2,7 @@
NAME=vault-sidekick
AUTHOR=gambol99
HARDWARE=$(shell uname -m)
VERSION=$(shell awk '/Version =/ { print $$3 }' version.go | sed 's/"//g')
VERSION=$(shell awk '/Version =/ { print $$3 }' main.go | sed 's/"//g')
.PHONY: test authors changelog build docker static release

View file

@ -39,11 +39,11 @@ spec:
image: gambol99/vault-sidekick:latest
args:
- -output=/etc/secrets
- -rn=pki:example.com:cn=commons.example.com,exec=/usr/bin/nginx_restart.sh,ctr=.*nginx_server.*
- -rn=secret:db/prod/username:fn=.credentials
- -rn=secret:db/prod/password
- -rn=aws:s3_backsup:fn=.s3_creds
- -rb=template:database_credentials:tpl=/etc/templates/db.tmpl,fn=/etc/credentials
- -cn=pki:example.com:cn=commons.example.com,rv=true,up=2h
- -cn=secret:db/prod/username:fn=.credentials
- -cn=secret:db/prod/password
- -cn=aws:s3_backsup:fn=.s3_creds
- -cn=template:database_credentials:tpl=/etc/templates/db.tmpl,fn=/etc/credentials
volumeMounts:
- name: secrets
mountPath: /etc/secrets
@ -59,7 +59,8 @@ The above say's
**Authentication**
A authentication file can be specified
A authentication file can be specified in either yaml of json format which contains a method field, indicating one of the authentication
methods provided by vault i.e. userpass, token, github etc and then followed by the required arguments for that plugin.
**Secret Renewals**
@ -107,9 +108,6 @@ In order to change the output format:
[jest@starfury vault-sidekick]$ build/vault-sidekick -cn=secret:password:fmt=yaml -logtostderr=true -dry-run
```
The default format is 'txt' which has the following behavour. If the number of keys in a resource is > 1, a file is created per key. Thus using the example
(build/vault-sidekick -cn=secret:password:fn=test) we would end up with files: test.this, test.nothing and test.demo
Format: 'cert' is less of a format of more file scheme i.e. is just extracts the 'certificate', 'issuing_ca' and 'private_key' and creates the three files FILE.{ca,key,crt}
**Resource Options**

63
auth_token.go Normal file
View file

@ -0,0 +1,63 @@
/*
Copyright 2015 Home Office All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"os"
"github.com/hashicorp/vault/api"
)
// token authentication plugin
type authTokenPlugin struct {
// the vault client
client *api.Client
}
// NewUserTokenPlugin ... creates a new User Token plugin
func NewUserTokenPlugin(client *api.Client) AuthInterface {
return &authTokenPlugin{
client: client,
}
}
// create ... retrieves the token from an environment variable or file
func (r authTokenPlugin) Create(cfg map[string]string) (string, error) {
filename, _ := cfg["filename"]
if filename != "" {
content, err := readConfigFile(filename)
if err != nil {
return "", err
}
// check: ensure we have a token in the file
token, found := content["token"]
if !found {
fmt.Errorf("the auth file: %s does not contain a token", filename)
}
return token, nil
}
// step: check the VAULT_TOKEN
if val := os.Getenv("VAULT_TOKEN"); val != "" {
return val, nil
}
return "", fmt.Errorf("no token provided")
}

View file

@ -20,42 +20,49 @@ import (
"fmt"
"github.com/hashicorp/vault/api"
"github.com/golang/glog"
)
// the userpass authentication plugin
type authUserPass struct {
// the vault client
type authUserPassPlugin struct {
client *api.Client
}
// auth token
type UserPassLogin struct {
type userPassLogin struct {
// the password for the account
Password string `json:"password,omitempty"`
}
func newUserPass(client *api.Client) *authUserPass {
return &authUserPass{
// NewUserPassPlugin ... creates a new User Pass plugin
func NewUserPassPlugin(client *api.Client) AuthInterface {
return &authUserPassPlugin{
client: client,
}
}
// create ... login with the username and password an
func (r authUserPass) create(username, password string) (*api.Secret, error) {
glog.V(10).Infof("using the userpass plugin, username: %s, password: %s", username, password)
// create ... login with the username and password provide in the file
func (r authUserPassPlugin) Create(cfg map[string]string) (string, error) {
// step: extract the options
username, _ := cfg["username"]
password, _ := cfg["password"]
req := r.client.NewRequest("POST", fmt.Sprintf("/v1/auth/userpass/login/%s", username))
// step: create the token request
if err := req.SetJSONBody(UserPassLogin{Password: password}); err != nil {
return nil, err
request := r.client.NewRequest("POST", fmt.Sprintf("/v1/auth/userpass/login/%s", username))
if err := request.SetJSONBody(userPassLogin{Password: password}); err != nil {
return "", err
}
// step: make the request
resp, err := r.client.RawRequest(req)
resp, err := r.client.RawRequest(request)
if err != nil {
return nil, err
return "", err
}
defer resp.Body.Close()
// step: parse and return auth
return api.ParseSecret(resp.Body)
secret, err := api.ParseSecret(resp.Body)
if err != nil {
return "", err
}
return secret.Auth.ClientToken, nil
}

View file

@ -27,22 +27,18 @@ import (
type config struct {
// the url for th vault server
vaultURL string
// the token to connect to vault with
vaultToken string
// a file containing the authenticate options
vaultAuthFile string
// the authentication options
vaultAuthOptions map[string]string
// the place to write the resources
outputDir string
// whether of not to remove the token post connection
deleteToken bool
// switch on dry run
dryRun bool
// skip tls verify
skipTLSVerify bool
tlsVerify bool
// the resource items to retrieve
resources *vaultResources
resources *VaultResources
// the interval for producing statistics
statsInterval time.Duration
}
@ -52,14 +48,15 @@ var (
)
func init() {
options.resources = new(vaultResources)
// step: setup some defaults
options.resources = new(VaultResources)
options.vaultAuthOptions = map[string]string{VaultAuth: "token"}
flag.StringVar(&options.vaultURL, "vault", getEnv("VAULT_ADDR", "https://127.0.0.1:8200"), "the url the vault service is running behind (VAULT_ADDR if available)")
flag.StringVar(&options.vaultToken, "token", "", "the token used to authenticate to teh vault service (VAULT_TOKEN if available)")
flag.StringVar(&options.vaultAuthFile, "auth", "", "a configuration file in a json or yaml containing authentication arguments")
flag.StringVar(&options.outputDir, "output", getEnv("VAULT_OUTPUT", "/etc/secrets"), "the full path to write the protected resources (VAULT_OUTPUT if available)")
flag.BoolVar(&options.deleteToken, "delete-token", false, "once the we have connected to vault, delete the token file from disk")
flag.BoolVar(&options.dryRun, "dryrun", false, "perform a dry run, printing the content to screen")
flag.BoolVar(&options.skipTLSVerify, "tls-skip-verify", false, "skip verifying the vault certificate")
flag.BoolVar(&options.tlsVerify, "tls-skip-verify", false, "whether to check and verify the vault service certificate")
flag.DurationVar(&options.statsInterval, "stats", time.Duration(5)*time.Minute, "the interval to produce statistics on the accessed resources")
flag.Var(options.resources, "cn", "a resource to retrieve and monitor from vault (e.g. pki:name:cert.name, secret:db_password, aws:s3_backup)")
}
@ -79,16 +76,6 @@ func validateOptions(cfg *config) error {
return fmt.Errorf("invalid vault url: '%s' specified", cfg.vaultURL)
}
// step: check if the token is in the VAULT_TOKEN var
if cfg.vaultToken == "" {
cfg.vaultToken = getEnv("VAULT_TOKEN", "")
}
// step: ensure we have a token
if cfg.vaultToken == "" && cfg.vaultAuthFile == "" {
return fmt.Errorf("you have not either a token or a authentication file to authenticate to vault with")
}
// step: read in the token if required
if cfg.vaultAuthFile != "" {
if exists, _ := fileExists(cfg.vaultAuthFile); !exists {

View file

@ -1,34 +0,0 @@
/*
Copyright 2015 Home Office All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestValidateOptions(t *testing.T) {
cfg := config{
vaultURL: "https://127.0.0.1:8200",
vaultToken: "d0ds09dis09ids09ifsd",
}
assert.Nil(t, validateOptions(&cfg))
cfg.vaultURL = "https://127.0.0.1"
assert.Nil(t, validateOptions(&cfg))
}

31
main.go
View file

@ -24,14 +24,25 @@ import (
"github.com/golang/glog"
)
const (
Prog = "vault-sidekick"
Version = "0.0.1"
GitSha = ""
)
func main() {
var err error
var vault *VaultService
// step: parse and validate the command line / environment options
if err := parseOptions(); err != nil {
if err = parseOptions(); err != nil {
showUsage("invalid options, %s", err)
}
glog.Infof("starting the %s, version: %s", Prog, Version)
// step: create a client to vault
vault, err := newVaultService(options.vaultURL)
if err != nil {
if vault, err = NewVaultService(options.vaultURL); err != nil {
showUsage("unable to create the vault client: %s", err)
}
@ -40,22 +51,22 @@ func main() {
signal.Notify(signalChannel, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
// step: create a channel to receive events upon and add our resources for renewal
ch := make(chan vaultResourceEvent, 10)
updates := make(chan VaultEvent, 10)
vault.AddListener(updates)
// 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 {
if err := rn.IsValid(); err != nil {
showUsage("%s", err)
}
vault.watch(rn, ch)
vault.Watch(rn)
}
// step: we simply wait for events i.e. secrets from vault and write them to the output directory
for {
select {
case evt := <-ch:
// step: write the secret to the output directory
go writeResource(evt.resource, evt.secret)
case evt := <-updates:
go writeResource(evt.Resource, evt.Secret)
case <-signalChannel:
glog.Infof("recieved a termination signal, shutting down the service")

View file

@ -76,14 +76,21 @@ func getKeys(data map[string]interface{}) []string {
// readConfigFile ... read in a configuration file
// filename : the path to the file
func readConfigFile(filename string) (map[string]string, error) {
// step: check the file exists
if exists, err := fileExists(filename); !exists {
return nil, fmt.Errorf("the file: %s does not exist", filename)
} else if err != nil {
return nil, err
}
// step: we only read in json or yaml formats
suffix := path.Ext(filename)
switch suffix {
case ".json":
return readJSONFile(filename)
case ".yaml":
return readYamlFile(filename)
return readYAMLFile(filename)
case ".yml":
return readYamlFile(filename)
return readYAMLFile(filename)
}
return nil, fmt.Errorf("unsupported config file format: %s", suffix)
}
@ -106,16 +113,14 @@ func readJSONFile(filename string) (map[string]string, error) {
return data, nil
}
// readYamlFile ... read in and unmarshall the data into a map
// readYAMLFile ... read in and unmarshall the data into a map
// filename : the path to the file container the yaml data
func readYamlFile(filename string) (map[string]string, error) {
func readYAMLFile(filename string) (map[string]string, error) {
data := make(map[string]string, 0)
content, err := ioutil.ReadFile(filename)
if err != nil {
return data, err
}
// unmarshall the data
err = yaml.Unmarshal(content, data)
if err != nil {
return data, err
@ -155,22 +160,19 @@ func fileExists(filename string) (bool, error) {
return true, nil
}
// writeResourceContent ... is resposinle for generate the specific content from the resource
// writeResourceContent ... is responsible for generating 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 {
func writeResource(rn *VaultResource, data map[string]interface{}) error {
var content []byte
var err error
// step: determine the resource path
resourcePath := rn.filename()
resourcePath := rn.GetFilename()
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 {
@ -267,5 +269,7 @@ func writeFile(filename string, content []byte) error {
return nil
}
glog.V(3).Infof("saving the file: %s", filename)
return ioutil.WriteFile(filename, content, 0660)
}

142
vault.go
View file

@ -17,49 +17,62 @@ limitations under the License.
package main
import (
"crypto/tls"
"fmt"
"net/http"
"time"
"github.com/golang/glog"
"github.com/hashicorp/vault/api"
"crypto/tls"
"net/http"
)
// a channel to send resource
type resourceChannel chan *vaultResource
const (
// VaultAuth ... the method to use when authenticating to vault
VaultAuth = "method"
)
// vaultService ... is the main interface into the vault API - placing into a structure
// AuthInterface .. the auth interface
type AuthInterface interface {
// Create and handle renewals of the token
Create(map[string]string) (string, error)
}
// VaultService ... is the main interface into the vault API - placing into a structure
// allows one to easily mock it and two to simplify the interface for us
type vaultService struct {
type VaultService struct {
// the vault client
client *api.Client
// the vault config
config *api.Config
// the token to authenticate with
token string
// the listener channel - technically we only have the one listener but there a long term reasons for adding this
listeners []chan VaultEvent
// a channel to inform of a new resource to processor
resourceChannel chan *watchedResource
}
type vaultResourceEvent struct {
// VaultEvent ... the definition which captures a change
type VaultEvent struct {
// the resource this relates to
resource *vaultResource
Resource *VaultResource
// the secret associated
secret map[string]interface{}
Secret map[string]interface{}
}
// newVaultService ... creates a new implementation to speak to vault and retrieve the resources
// NewVaultService ... creates a new implementation to speak to vault and retrieve the resources
// url : the url of the vault service
func newVaultService(url string) (*vaultService, error) {
func NewVaultService(url string) (*VaultService, error) {
var err error
glog.Infof("creating a new vault client: %s", url)
// step: create the config for client
service := new(vaultService)
service := new(VaultService)
service.config = api.DefaultConfig()
service.config.Address = url
service.listeners = make([]chan VaultEvent, 0)
// step: skip the cert verification if requested
if options.skipTLSVerify {
if options.tlsVerify {
service.config.HttpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
@ -69,21 +82,16 @@ func newVaultService(url string) (*vaultService, error) {
service.resourceChannel = make(chan *watchedResource, 20)
// step: create the actual client
service.client, err = api.NewClient(service.config)
if err != nil {
if service.client, err = api.NewClient(service.config); err != nil {
return nil, err
}
// step: are we using a token? or do we need to authenticate and grab a token
if options.vaultToken == "" {
options.vaultToken, err = service.authenticate(options.vaultAuthOptions)
if err != nil {
return nil, err
}
if service.token, err = service.authenticate(options.vaultAuthOptions); err != nil {
return nil, err
}
// step: set the token for the client
service.client.SetToken(options.vaultToken)
service.client.SetToken(service.token)
// step: start the service processor off
service.vaultServiceProcessor()
@ -91,9 +99,21 @@ func newVaultService(url string) (*vaultService, error) {
return service, nil
}
// AddListener ... add a listener to the events listeners
func (r *VaultService) AddListener(ch chan VaultEvent) {
glog.V(10).Infof("adding the listener: %v", ch)
r.listeners = append(r.listeners, ch)
}
// Watch ... add a watch on a resource and inform, renew which required and inform us when
// the resource is ready
func (r VaultService) Watch(rn *VaultResource) {
r.resourceChannel <- &watchedResource{resource: rn}
}
// vaultServiceProcessor ... is the background routine responsible for retrieving the resources, renewing when required and
// informing those who are watching the resource that something has changed
func (r vaultService) vaultServiceProcessor() {
func (r *VaultService) vaultServiceProcessor() {
go func() {
// a list of resource being watched
var items []*watchedResource
@ -111,7 +131,7 @@ func (r vaultService) vaultServiceProcessor() {
// - if we error attempting to retrieve the secret, we background and reschedule an attempt to add it
// - if ok, we grab the lease it and lease time, we setup a notification on renewal
case x := <-r.resourceChannel:
glog.Infof("adding a resource into the service processor, resource: %s", x.resource)
glog.V(4).Infof("adding a resource into the service processor, resource: %s", x.resource)
// step: add to the list of resources
items = append(items, x)
// step: push into the retrieval channel
@ -131,11 +151,10 @@ func (r vaultService) vaultServiceProcessor() {
glog.Errorf("failed to retrieve the resource: %s from vault, error: %s", x.resource, err)
// reschedule the attempt for later
r.reschedule(x, retrieveChannel, 3, 10)
break
}
glog.Infof("succesfully retrieved resournce: %s, leaseID: %s", x.resource, x.secret.LeaseID)
glog.V(4).Infof("successfully retrieved resournce: %s, leaseID: %s", x.resource, x.secret.LeaseID)
// step: if we had a previous lease and the option is to revoke, lets throw into the revoke channel
if leaseID != "" && x.resource.revoked {
@ -218,33 +237,22 @@ func (r vaultService) vaultServiceProcessor() {
// authenticate ... we need to authenticate to teh vault to grab a toke
// auth : a map containing the options required for authentication
func (r vaultService) authenticate(auth map[string]string) (string, error) {
var secret *api.Secret
func (r VaultService) authenticate(auth map[string]string) (string, error) {
var secret string
var err error
plugin, _ := auth["method"]
plugin, _ := auth[VaultAuth]
switch plugin {
case "userpass":
// step: get the options for this plugin
username, _ := auth["username"]
password, _ := auth["password"]
secret, err = newUserPass(r.client).create(username, password)
secret, err = NewUserPassPlugin(r.client).Create(auth)
case "token":
auth["filename"] = options.vaultAuthFile
secret, err = NewUserTokenPlugin(r.client).Create(auth)
default:
return "", fmt.Errorf("unsupported authentication plugin: %s", plugin)
}
// step: was there an error?
if err != nil {
return "", err
}
// step: do we have auth information
if secret.Auth == nil {
return "", fmt.Errorf("invalid authentication response, no auth response")
}
// step: return the client token
return secret.Auth.ClientToken, nil
return secret, err
}
// reschedule ... reschedules an event back into a channel after n seconds
@ -252,7 +260,7 @@ func (r vaultService) authenticate(auth map[string]string) (string, error) {
// ch : the channel the resource should be placed into
// min : the minimum amount of time i'm willing to wait
// max : the maximum amount of time i'm willing to wait
func (r vaultService) reschedule(rn *watchedResource, ch chan *watchedResource, min, max int) {
func (r VaultService) reschedule(rn *watchedResource, ch chan *watchedResource, min, max int) {
go func(x *watchedResource) {
glog.V(3).Infof("rescheduling the resource: %s, channel: %v", rn.resource, ch)
<-randomWait(min, max)
@ -262,21 +270,22 @@ func (r vaultService) reschedule(rn *watchedResource, ch chan *watchedResource,
// upstream ... the resource has changed thus we notify the upstream listener
// item : the item which has changed
func (r vaultService) upstream(item *watchedResource) {
func (r VaultService) upstream(item *watchedResource) {
// step: chunk this into a go-routine not to block us
go func() {
glog.V(6).Infof("sending the event for resource: %s upstream to listener: %v", item.resource, item.listener)
item.listener <- vaultResourceEvent{
resource: item.resource,
secret: item.secret.Data,
}
}()
for _, listener := range r.listeners {
go func() {
glog.V(6).Infof("sending the event for resource: %s upstream to listener: %v", item.resource, listener)
listener <- VaultEvent{
Resource: item.resource,
Secret: item.secret.Data,
}
}()
}
}
// renew ... attempts to renew the lease on a resource
// rn : the resource we wish to renew the lease on
func (r vaultService) renew(rn *watchedResource) error {
// step: extend the lease on a resource
func (r VaultService) renew(rn *watchedResource) error {
glog.V(4).Infof("attempting to renew the lease: %s on resource: %s", rn.secret.LeaseID, rn.resource)
// step: check the resource is renewable
if !rn.secret.Renewable {
@ -285,7 +294,6 @@ func (r vaultService) renew(rn *watchedResource) error {
secret, err := r.client.Sys().Renew(rn.secret.LeaseID, 0)
if err != nil {
glog.Errorf("unable to renew the lease on resource: %s", rn.resource)
return err
}
@ -300,8 +308,8 @@ func (r vaultService) renew(rn *watchedResource) error {
}
// revoke ... attempt to revoke the lease of a resource
// lease : the lease lease which was given when you got it
func (r vaultService) revoke(lease string) error {
// lease : the lease lease which was given when you got it
func (r VaultService) revoke(lease string) error {
glog.V(3).Infof("attemping to revoking the lease: %s", lease)
err := r.client.Sys().Revoke(lease)
@ -314,7 +322,8 @@ func (r vaultService) revoke(lease string) error {
}
// get ... retrieve a secret from the vault
func (r vaultService) get(rn *watchedResource) (err error) {
// rn : the watched resource
func (r VaultService) get(rn *watchedResource) (err error) {
var secret *api.Secret
glog.V(5).Infof("attempting to retrieve the resource: %s from vault", rn.resource)
@ -353,14 +362,3 @@ func (r vaultService) get(rn *watchedResource) (err error) {
return err
}
// watch ... add a watch on a resource and inform, renew which required and inform us when
// the resource is ready
func (r *vaultService) watch(rn *vaultResource, ch chan vaultResourceEvent) {
glog.V(6).Infof("adding the resource: %s, listener: %v to service processor", rn, ch)
r.resourceChannel <- &watchedResource{
resource: rn,
listener: ch,
}
}

View file

@ -41,7 +41,7 @@ const (
)
var (
resourceFormatRegex = regexp.MustCompile("^(yaml|json|ini|txt|cert)$")
resourceFormatRegex = regexp.MustCompile("^(yaml|json|ini|txt|cert|csv)$")
// a map of valid resource to retrieve from vault
validResources = map[string]bool{
@ -55,8 +55,8 @@ var (
}
)
func defaultVaultResource() *vaultResource {
return &vaultResource{
func defaultVaultResource() *VaultResource {
return &VaultResource{
format: "yaml",
renewable: false,
revoked: false,
@ -64,8 +64,8 @@ func defaultVaultResource() *vaultResource {
}
}
// resource ... the structure which defined a resource set from vault
type vaultResource struct {
// VaultResource ... the structure which defined a resource set from vault
type VaultResource struct {
// the namespace of the resource
resource string
// the name of the resource
@ -82,8 +82,18 @@ type vaultResource struct {
options map[string]string
}
// isValid ... checks to see if the resource is valid
func (r *vaultResource) isValid() error {
// GetFilename ... generates a resource filename by default the resource name and resource type, which
// can override by the OPTION_FILENAME option
func (r VaultResource) GetFilename() string {
if path, found := r.options[OptionFilename]; found {
return path
}
return fmt.Sprintf("%s.%s", r.name, r.resource)
}
// IsValid ... checks to see if the resource is valid
func (r *VaultResource) IsValid() error {
// step: check the resource type
if _, found := validResources[r.resource]; !found {
return fmt.Errorf("unsupported resource type: %s", r.resource)
@ -103,7 +113,7 @@ func (r *vaultResource) isValid() error {
}
// isValidResource ... validate the resource meets the requirements
func (r *vaultResource) isValidResource() error {
func (r *VaultResource) isValidResource() error {
switch r.resource {
case "pki":
if _, found := r.options[OptionCommonName]; !found {
@ -119,7 +129,7 @@ func (r *vaultResource) isValidResource() error {
}
// isValidOptions ... iterates through the options, converts the options and so forth
func (r *vaultResource) isValidOptions() error {
func (r *VaultResource) isValidOptions() error {
// check the filename directive
for opt, val := range r.options {
switch opt {
@ -160,17 +170,7 @@ func (r *vaultResource) isValidOptions() error {
return nil
}
// resourceFilename ... generates a resource filename by default the resource name and resource type, which
// can override by the OPTION_FILENAME option
func (r vaultResource) filename() string {
if path, found := r.options[OptionFilename]; found {
return path
}
return fmt.Sprintf("%s.%s", r.name, r.resource)
}
// String ... a string representation of the struct
func (r vaultResource) String() string {
func (r VaultResource) String() string {
return fmt.Sprintf("%s/%s", r.resource, r.name)
}

View file

@ -23,14 +23,14 @@ import (
)
func TestResourceFilename(t *testing.T) {
rn := vaultResource{
rn := VaultResource{
name: "test_secret",
resource: "secret",
options: map[string]string{},
}
assert.Equal(t, "test_secret.secret", rn.filename())
assert.Equal(t, "test_secret.secret", rn.GetFilename())
rn.options[OptionFilename] = "credentials"
assert.Equal(t, "credentials", rn.filename())
assert.Equal(t, "credentials", rn.GetFilename())
}
func TestIsValid(t *testing.T) {
@ -38,12 +38,12 @@ func TestIsValid(t *testing.T) {
resource.name = "/test/name"
resource.resource = "secret"
assert.Nil(t, resource.isValid())
assert.Nil(t, resource.IsValid())
resource.resource = "nothing"
assert.NotNil(t, resource.isValid())
assert.NotNil(t, resource.IsValid())
resource.resource = "pki"
assert.NotNil(t, resource.isValid())
assert.NotNil(t, resource.IsValid())
resource.options[OptionCommonName] = "common.example.com"
assert.Nil(t, resource.isValid())
assert.Nil(t, resource.IsValid())
}

View file

@ -27,14 +27,14 @@ var (
resourceOptionsRegex = regexp.MustCompile("([\\w\\d]{2,3})=([\\w\\d\\/\\.\\-_]+)[,]?")
)
// resources ... a collection of type resource
type vaultResources struct {
// VaultResources ... a collection of type resource
type VaultResources struct {
// an array of resource to retrieve
items []*vaultResource
items []*VaultResource
}
// Set ... implementation for the parser
func (r *vaultResources) Set(value string) error {
func (r *VaultResources) Set(value string) error {
rn := defaultVaultResource()
// step: extract the resource type and name
@ -68,6 +68,6 @@ func (r *vaultResources) Set(value string) error {
}
// String ... returns a string representation of the struct
func (r vaultResources) String() string {
func (r VaultResources) String() string {
return ""
}

View file

@ -23,7 +23,7 @@ import (
)
func TestSetResources(t *testing.T) {
var items vaultResources
var items VaultResources
assert.Nil(t, items.Set("secret:test:fn=filename.test,fmt=yaml"))
assert.Nil(t, items.Set("secret:test:fn=filename.test,"))
@ -41,7 +41,7 @@ func TestSetResources(t *testing.T) {
}
func TestResources(t *testing.T) {
var items vaultResources
var items VaultResources
items.Set("secret:test:fn=filename.test,fmt=yaml")
items.Set("secret:test:fn=fileame.test")

View file

@ -1,22 +0,0 @@
/*
Copyright 2015 Home Office All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
const (
Version = "0.0.1"
GitSha = ""
)

View file

@ -31,10 +31,8 @@ const (
// 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
type watchedResource struct {
// the upstream listener to the event
listener chan vaultResourceEvent
// the resource itself
resource *vaultResource
resource *VaultResource
// the last time the resource was retrieved
lastUpdated time.Time
// the time which the lease expires