- extracting the options completelt

- passing all the options into the write, effectivly allowing you to pass all options
- fixed the format of the content
- added a changelog
- shifting to version v0.0.6
This commit is contained in:
Rohith 2016-03-16 12:55:16 +00:00
parent cc74402b92
commit 9d02d7e843
8 changed files with 88 additions and 88 deletions

13
CHANGELOG.md Normal file
View file

@ -0,0 +1,13 @@
#### **Version v0.0.6**
##### FEATURES:
* Fixed up a number of niggling issues
* Added the bundle format to pki paths can write a bundle private and certificate file and a separate ca file
* Added the env format which will create a environment variables file
* Adding comma separated list as resource arguments comes in the form <ARG>|<ARG> i.e.
-cn=pki:platform/pki/issue/example-dot-com:common_name=blah.example.com,alt_names='me.example.com|ted.example.com'
##### BUGS:
* Fixed the formatting of values in various formats, i.e. %!s(bool=true)

View file

@ -26,7 +26,7 @@ import (
const (
Prog = "vault-sidekick"
Version = "v0.0.5"
Version = "v0.0.6"
)
func main() {
@ -63,8 +63,11 @@ func main() {
select {
case evt := <-updates:
glog.V(10).Infof("recieved an update from the resource: %s", evt.Resource)
go writeResource(evt.Resource, evt.Secret)
go func(r VaultEvent) {
if err := writeResource(evt.Resource, evt.Secret); err != nil {
glog.Errorf("failed to write out the update, error: %s", err)
}
}(evt)
case <-signalChannel:
glog.Infof("recieved a termination signal, shutting down the service")
os.Exit(0)

View file

@ -201,6 +201,8 @@ func writeResource(rn *VaultResource, data map[string]interface{}) error {
glog.Errorf("failed to write the ca certificate file, errro: %s", err)
return err
}
return nil
}
if rn.format == "env" {
@ -251,7 +253,7 @@ func writeResource(rn *VaultResource, data map[string]interface{}) error {
// 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 {
if err := writeFile(filename, []byte(fmt.Sprintf("%v", content))); err != nil {
glog.Errorf("failed to write resource: %s, elemment: %s, filename: %s, error: %s",
rn, suffix, filename, err)
continue

View file

@ -314,19 +314,20 @@ func (r VaultService) revoke(lease string) error {
// rn : the watched resource
func (r VaultService) get(rn *watchedResource) (err error) {
var secret *api.Secret
// step: not sure who to cast map[string]string to map[string]interface{} doesn't like it anyway i try and do it
params := make(map[string]interface{}, 0)
for k, v := range rn.resource.options {
params[k] = interface{}(v)
}
glog.V(10).Infof("get path: %s, params: %v", rn.resource.path, params)
glog.V(5).Infof("attempting to retrieve the resource: %s from vault", rn.resource)
// step: perform a request to vault
switch rn.resource.resource {
case "pki":
secret, err = r.client.Logical().Write(fmt.Sprintf(rn.resource.path),
map[string]interface{}{
"common_name": rn.resource.options[optionCommonName],
})
secret, err = r.client.Logical().Write(fmt.Sprintf(rn.resource.path), params)
case "transit":
secret, err = r.client.Logical().Write(fmt.Sprintf(rn.resource.path),
map[string]interface{}{
"cipertext": rn.resource.options[optionCiphertext],
})
secret, err = r.client.Logical().Write(fmt.Sprintf(rn.resource.path), params)
case "aws":
fallthrough
case "cubbyhole":
@ -338,7 +339,7 @@ func (r VaultService) get(rn *watchedResource) (err error) {
case "secret":
secret, err = r.client.Logical().Read(rn.resource.path)
}
// step: return on error
// step: check the error if any
if err != nil {
if strings.Contains(err.Error(), "missing client token") {
// decision: until the rewrite, lets just exit for now

View file

@ -19,7 +19,6 @@ package main
import (
"fmt"
"regexp"
"strconv"
"time"
)
@ -28,8 +27,6 @@ const (
optionFilename = "file"
// optionFormat set the output format (yaml, xml, json)
optionFormat = "fmt"
// optionCommonName set the PKI common name of the resource
optionCommonName = "cn"
// optionTemplatePath is the full path to a template
optionTemplatePath = "tpl"
// optionRenewal sets the duration to renew the resource
@ -40,8 +37,6 @@ const (
optionsRevokeDelay = "delay"
// optionUpdate overrides the lease of the resource
optionUpdate = "update"
// optionCiphertext
optionCiphertext = "ciphertext"
)
var (
@ -86,8 +81,10 @@ type VaultResource struct {
revokeDelay time.Duration
// the lease duration
update time.Duration
// the cipertext for transit
ciphertext string
// the filename to save the secret
filename string
// the template file
templateFile string
// additional options to the resource
options map[string]string
}
@ -95,8 +92,8 @@ type VaultResource struct {
// 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
if r.filename != "" {
return r.filename
}
return fmt.Sprintf("%s.%s", r.path, r.resource)
@ -109,11 +106,6 @@ func (r *VaultResource) IsValid() error {
return fmt.Errorf("unsupported resource type: %s", r.resource)
}
// step: check the options
if err := r.isValidOptions(); err != nil {
return fmt.Errorf("invalid resource options, %s", err)
}
// step: check is have all the required options to this resource type
if err := r.isValidResource(); err != nil {
return fmt.Errorf("invalid resource: %s, %s", r, err)
@ -126,11 +118,11 @@ func (r *VaultResource) IsValid() error {
func (r *VaultResource) isValidResource() error {
switch r.resource {
case "pki":
if _, found := r.options[optionCommonName]; !found {
if _, found := r.options["common_name"]; !found {
return fmt.Errorf("pki resource requires a common name specified")
}
case "transit":
if _, found := r.options[optionCiphertext]; !found {
if _, found := r.options["ciphertext"]; !found {
return fmt.Errorf("transit requires a ciphertext option")
}
case "tpl":
@ -142,56 +134,6 @@ func (r *VaultResource) isValidResource() error {
return nil
}
// isValidOptions iterates through the options, converts the options and so forth
func (r *VaultResource) isValidOptions() error {
// check the filename directive
for opt, val := range r.options {
switch opt {
case optionFormat:
if matched := resourceFormatRegex.MatchString(r.options[optionFormat]); !matched {
return fmt.Errorf("unsupported output format: %s", r.options[optionFormat])
}
r.format = val
case optionUpdate:
duration, err := time.ParseDuration(val)
if err != nil {
return fmt.Errorf("the update option: %s is not value, should be a duration format", val)
}
r.update = duration
case optionRevoke:
choice, err := strconv.ParseBool(val)
if err != nil {
return fmt.Errorf("the revoke option: %s is invalid, should be a boolean", val)
}
r.revoked = choice
case optionsRevokeDelay:
duration, err := time.ParseDuration(val)
if err != nil {
return fmt.Errorf("the revoke delay option: %s is not value, should be a duration format", val)
}
r.revokeDelay = duration
case optionRenewal:
choice, err := strconv.ParseBool(val)
if err != nil {
return fmt.Errorf("the renewal option: %s is invalid, should be a boolean", val)
}
r.renewable = choice
case optionCiphertext:
r.ciphertext = val
case optionFilename:
// @TODO need to check it's valid filename / path
case optionCommonName:
// @TODO need to check it's a valid hostname
case optionTemplatePath:
if exists, _ := fileExists(val); !exists {
return fmt.Errorf("the template file: %s does not exist", val)
}
}
}
return nil
}
// String returns a string representation of the struct
func (r VaultResource) String() string {
return fmt.Sprintf("type: %s, path:%s", r.resource, r.path)

View file

@ -29,8 +29,6 @@ func TestResourceFilename(t *testing.T) {
options: map[string]string{},
}
assert.Equal(t, "test_secret.secret", rn.GetFilename())
rn.options[optionFilename] = "credentials"
assert.Equal(t, "credentials", rn.GetFilename())
}
func TestIsValid(t *testing.T) {
@ -43,7 +41,4 @@ func TestIsValid(t *testing.T) {
assert.NotNil(t, resource.IsValid())
resource.resource = "pki"
assert.NotNil(t, resource.IsValid())
resource.options[optionCommonName] = "common.example.com"
assert.Nil(t, resource.IsValid())
}

View file

@ -18,7 +18,9 @@ package main
import (
"fmt"
"strconv"
"strings"
"time"
)
// VaultResources is a collection of type resource
@ -59,8 +61,48 @@ func (r *VaultResources) Set(value string) error {
if kp[1] == "" {
return fmt.Errorf("invalid resource option: %s, must have a value", x)
}
// step: set the name and value
name := kp[0]
value := strings.Replace(kp[1], "|", ",", -1)
rn.options[kp[0]] = kp[1]
// step: extract the control options from the path resource parameteres
switch name {
case optionFormat:
if matched := resourceFormatRegex.MatchString(value); !matched {
return fmt.Errorf("unsupported output format: %s", value)
}
rn.format = value
case optionUpdate:
duration, err := time.ParseDuration(value)
if err != nil {
return fmt.Errorf("update option: %s is not value, should be a duration format", value)
}
rn.update = duration
case optionRevoke:
choice, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("the revoke option: %s is invalid, should be a boolean", value)
}
rn.revoked = choice
case optionsRevokeDelay:
duration, err := time.ParseDuration(value)
if err != nil {
return fmt.Errorf("the revoke delay option: %s is not value, should be a duration format", value)
}
rn.revokeDelay = duration
case optionRenewal:
choice, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("the renewal option: %s is invalid, should be a boolean", value)
}
rn.renewable = choice
case optionFilename:
rn.filename = value
case optionTemplatePath:
rn.templateFile = value
default:
rn.options[name] = value
}
}
}
// step: append to the list of resources

View file

@ -30,9 +30,9 @@ func TestSetResources(t *testing.T) {
assert.Nil(t, items.Set("secret:/db/prod/username"))
assert.Nil(t, items.Set("secret:/db/prod:file=filename.test,fmt=yaml"))
assert.Nil(t, items.Set("secret:test:fn=filename.test"))
assert.Nil(t, items.Set("pki:example-dot-com:cn=blah.example.com"))
assert.Nil(t, items.Set("pki:example-dot-com:cn=blah.example.com,file=/etc/certs/ssl/blah.example.com"))
assert.Nil(t, items.Set("pki:example-dot-com:cn=blah.example.com,renew=10s"))
assert.Nil(t, items.Set("pki:example-dot-com:common_name=blah.example.com"))
assert.Nil(t, items.Set("pki:example-dot-com:common_name=blah.example.com,file=/etc/certs/ssl/blah.example.com"))
assert.Nil(t, items.Set("pki:example-dot-com:common_name=blah.example.com,renew=true"))
assert.NotNil(t, items.Set("secret:"))
assert.NotNil(t, items.Set("secret:test:file=filename.test,fmt="))
assert.NotNil(t, items.Set("secret::file=filename.test,fmt=yaml"))
@ -40,6 +40,7 @@ func TestSetResources(t *testing.T) {
assert.NotNil(t, items.Set("file=filename.test,fmt=yaml"))
}
/*
func TestResources(t *testing.T) {
var items VaultResources
items.Set("secret:test:file=filename.test,fmt=yaml")
@ -61,3 +62,4 @@ func TestResources(t *testing.T) {
assert.Equal(t, 1, len(rn.options))
assert.Equal(t, "fileame.test", rn.options[optionFilename])
}
*/