Skip to content

Commit

Permalink
Merge pull request #1 from PaddleHQ/add-aws-client-and-get-params-by-…
Browse files Browse the repository at this point in the history
…path

DT-95 Add parameter store client logic
  • Loading branch information
geototti21 authored Jan 25, 2019
2 parents 6453f15 + b420470 commit 38233f1
Show file tree
Hide file tree
Showing 10 changed files with 533 additions and 2 deletions.
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## Base image that contains everything needed for local dev
FROM golang:1.11.3-alpine AS development

# Install everything for development
RUN apk add bash git gcc g++

RUN go get -u golang.org/x/lint/golint

WORKDIR /go/src/github.com/PaddleHQ/go-aws-ssm

## Copy go.mod and go.sum to install go modules and force it to use them
ENV GO111MODULE=on
COPY go.mod .
COPY go.sum .
RUN go mod download

## Copy the rest of our code in
COPY . .
37 changes: 37 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
pipeline {
agent any
environment {
CI = "true"
UNIQUE_BUILD_ID = "${GIT_COMMIT}".substring(0,7)
}
stages {
stage('Build') {
steps {
sh('make build')
}
}
stage('Static analysis') {
steps {
sh('make lint vet')
}
}
stage('Unit tests') {
steps {
sh('make unit-test')
}
}

}
post {
failure {
script {
sh('make docker-dump-logs')
}
}
cleanup {
script {
cleanWs() /* clean up our workspace */
}
}
}
}
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

ifndef UNIQUE_BUILD_ID
UNIQUE_BUILD_ID=latest
endif

.PHONY: build
.SILENT: help
help: ## Show this help message
set -x

echo ""
echo "Available targets:"
grep ':.* ##\ ' ${MAKEFILE_LIST} | awk '{gsub(":[^#]*##","\t"); print}' | column -t -c 2 -s $$'\t' | sort

# Build the container
build: ## Build the container
docker build --tag=go-aws-ssm:$(UNIQUE_BUILD_ID) .

unit-test:
docker run go-aws-ssm:$(UNIQUE_BUILD_ID) /bin/bash -c "go test ./... -v"

vet:
docker run go-aws-ssm:$(UNIQUE_BUILD_ID) /bin/bash -c "go vet ./..."

lint:
docker run go-aws-ssm:$(UNIQUE_BUILD_ID) /bin/bash -c "golint -set_exit_status ./..."
62 changes: 60 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,60 @@
# go-awspsclient
Client library that interfaces with AWS Parameter Store
# go-aws-ssm
Client library that interfaces with AWS System Manager Agent

## Install

```bash
go get github.com/PaddleHQ/go-aws-ssm
```

## Examples

#### Basic Usage

```go
//Assuming you have the parameters in the following format:
//my-service/dev/param-1 -> with value `a`
//my-service/dev/param-2 -> with value `b`
pmstore, err := awsssm.NewParameterStore()
if err != nil {
return err
}
//Requesting the base path
params, err := pmstore.GetAllParametersByPath("/my-service/dev/")
if err!=nil{
return err
}

//And getting a specific value
value:=params.GetValueByName("param-1")
//value should be `a`


```

#### Integrates easy with [viper](https://github.com/spf13/viper)
```go
//Assuming you have the parameters in the following format:
//my-service/dev/param-1 -> with value `a`
//my-service/dev/param-2 -> with value `b`
pmstore, err := awsssm.NewParameterStore()
if err != nil {
return err
}
//Requesting the base path
params, err := pmstore.GetAllParametersByPath("/my-service/dev/")
if err!=nil{
return err
}

//Configure viper to handle it as json document, nothing special here!
v := viper.New()
v.SetConfigType(`json`)
//params object implements the io.Reader interface that is required
err = v.ReadConfig(params)
if err != nil {
return err
}
value := v.Get(`param-1`)
//value should be `a`
```
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/PaddleHQ/go-aws-ssm

require (
github.com/aws/aws-sdk-go v1.16.24
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mitchellh/mapstructure v1.1.2
github.com/stretchr/testify v1.3.0 // indirect
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e // indirect
golang.org/x/text v0.3.0 // indirect
)
19 changes: 19 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
github.com/aws/aws-sdk-go v1.16.24 h1:I/A3Hwbgs3IEAP6v1bFpHKXiT7wZDoToX9cb00nxZnM=
github.com/aws/aws-sdk-go v1.16.24/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e h1:MDa3fSUp6MdYHouVmCCNz/zaH2a6CRcxY3VhT/K3C5Q=
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
80 changes: 80 additions & 0 deletions parameter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package awsssm

import (
"encoding/json"
"io"
"strings"

"github.com/aws/aws-sdk-go/service/ssm"
"github.com/mitchellh/mapstructure"
)

//Parameter holds a Systems Manager parameter from AWS Parameter Store
type Parameter struct {
ssmParameter *ssm.Parameter
}

//GetValue return the actual value of the parameter
func (p *Parameter) GetValue() string {
if p.ssmParameter.Value == nil {
return ""
}
return *p.ssmParameter.Value
}

//NewParameters creates a Parameters
func NewParameters(basePath string, parameters map[string]*Parameter) *Parameters {
return &Parameters{
basePath: basePath,
parameters: parameters,
}
}

//Parameters holds the output and all AWS Parameter Store that have the same base path
type Parameters struct {
basePath string
parameters map[string]*Parameter
}

//Read implements the io.Reader interface for the key/value pair
func (p *Parameters) Read(des []byte) (n int, err error) {
bytesJSON, err := json.Marshal(p.getKeyValueMap())
if err != nil {
return 0, err
}
return copy(des, bytesJSON), io.EOF
}

//GetValueByName returns the value based on the name
//so the AWS Parameter Store parameter name is base path + name
func (p *Parameters) GetValueByName(name string) string {
parameter, ok := p.parameters[p.basePath+name]
if !ok {
return ""
}
return parameter.GetValue()
}

//GetValueByFullPath returns the value based on the full path
func (p *Parameters) GetValueByFullPath(name string) string {
parameter, ok := p.parameters[name]
if !ok {
return ""
}
return parameter.GetValue()
}

//Decode decodes the parameters into the given struct
//We are using this package to decode the values to the struct https://github.com/mitchellh/mapstructure
//For more details how you can use this check the parameter_test.go file
func (p *Parameters) Decode(output interface{}) error {
return mapstructure.Decode(p.getKeyValueMap(), output)
}

func (p *Parameters) getKeyValueMap() map[string]string {
keyValue := make(map[string]string, len(p.parameters))
for k, v := range p.parameters {
keyValue[strings.Replace(k, p.basePath, "", 1)] = v.GetValue()
}
return keyValue
}
77 changes: 77 additions & 0 deletions parameter_store_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package awsssm

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
)

type ssmClient interface {
GetParametersByPath(input *ssm.GetParametersByPathInput) (*ssm.GetParametersByPathOutput, error)
}

//ParameterStore holds all the methods tha are supported against AWS Parameter Store
type ParameterStore struct {
ssm ssmClient
}

//SetSSM sets a new ssm client
func (c *ParameterStore) SetSSM(ssm ssmClient) {
c.ssm = ssm
}

//GetAllParametersByPath is returning all the Parameters that are hierarchy linked to this path
//For example a request with path as /my-service/dev/
//Will return /my-service/dev/param-a, /my-service/dev/param-b, etc... but will not return recursive paths
//the `ssm:GetAllParametersByPath` permission is required
//to the `arn:aws:ssm:us-east-2:aws-account-id:/my-service/dev/*`
func (c *ParameterStore) GetAllParametersByPath(path string, decrypt bool) (*Parameters, error) {
var input = &ssm.GetParametersByPathInput{}
input.SetWithDecryption(decrypt)
input.SetPath(path)
return c.getParameters(input)
}

func (c *ParameterStore) getParameters(input *ssm.GetParametersByPathInput) (*Parameters, error) {
result, err := c.ssm.GetParametersByPath(input)
if err != nil {
return nil, err
}
parameters := NewParameters(*input.Path, make(map[string]*Parameter, len(result.Parameters)))
for _, v := range result.Parameters {
if v.Name == nil {
continue
}
parameters.parameters[*v.Name] = &Parameter{ssmParameter: v}
}
return parameters, nil
}

var defaultSessionOptions = session.Options{
Config: aws.Config{Region: aws.String("us-east-1")},
SharedConfigState: session.SharedConfigEnable,
}

//NewParameterStoreWithOptions is creating a new parameterstore ParameterStore with the specified Session options
func NewParameterStoreWithOptions(sessionOptions *session.Options, ssmConfig ...*aws.Config) (*ParameterStore, error) {
options := defaultSessionOptions
if sessionOptions != nil {
options = *sessionOptions
}
sessionAWS, err := session.NewSessionWithOptions(options)
if err != nil {
return nil, err
}
svc := ssm.New(sessionAWS, ssmConfig...)
return &ParameterStore{ssm: svc}, nil
}

//NewParameterStore is creating a new parameterstore ParameterStore
func NewParameterStore(ssmConfig ...*aws.Config) (*ParameterStore, error) {
sessionAWS, err := session.NewSessionWithOptions(defaultSessionOptions)
if err != nil {
return nil, err
}
svc := ssm.New(sessionAWS, ssmConfig...)
return &ParameterStore{ssm: svc}, nil
}
Loading

0 comments on commit 38233f1

Please sign in to comment.