Skip to content

Commit 21c4910

Browse files
committed
fix: add more docs
1 parent 8e11c8d commit 21c4910

File tree

12 files changed

+281
-114
lines changed

12 files changed

+281
-114
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11

22
OWNER := dnitsch
33
NAME := configmanager
4-
GIT_TAG := "1.2.0"
5-
VERSION := "v1.2.0"
4+
GIT_TAG := "1.3.0"
5+
VERSION := "v1.3.0"
66
# VERSION := "$(shell git describe --tags --abbrev=0)"
77
REVISION := $(shell git rev-parse --short HEAD)
88

README.md

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -82,66 +82,67 @@ The token is made up of 3 parts:
8282

8383
If contents of the `AWSSECRETS#/appxyz/service1-password` are a string then `service1-password` will be the key and converted to UPPERCASE e.g. `SERVICE1_PASSWORD=som3V4lue`
8484

85+
### Special AZKVSECRETS
8586

87+
For Azure KeyVault the first part of the token needs to be the name of the vault.
88+
89+
> Azure Go SDK (v2) requires the vault Uri on initializing the client
90+
91+
`AZKVSECRET#/test-vault//token/1` ==> will use KeyVault implementation to retrieve the `/token/1` from a `test-vault`.
92+
93+
`AZKVSECRET#/test-vault/no-slash-token-1` ==> will use KeyVault implementation to retrieve the `no-slash-token-1` from a `test-vault`.
8694

8795
## Go API
8896

97+
latest api [here](https://pkg.go.dev/github.com/dnitsch/configmanager)
98+
8999
### Sample Use case
90100

91101
One of the sample use cases includes implementation in a K8s controller.
92102

93103
E.g. your Custom CRD stores some values in plain text that should really be secrets/nonpublic config parameters - something like this can be invoked from inside the controller code using the generator pkg API.
94104

95-
```go
96-
func replaceTokens(in string, t *v1alpha.CustomFooCrdSpec) error {
97-
98-
tokens := []string{}
105+
See [examples](./examples/examples.go) for more examples and tests for sample input/usage
99106

100-
for k := range generator.VarPrefix {
101-
matches := regexp.MustCompile(`(?s)`+regexp.QuoteMeta(k)+`.([^\"]+)`).FindAllString(in, -1)
102-
tokens = append(tokens, matches...)
103-
}
104-
105-
cnf := generator.GenVarsConfig{}
106-
m, err := configmanager.Retrieve(tokens, cnf)
107+
```go
108+
import (
109+
"context"
110+
"fmt"
111+
112+
"github.com/dnitsch/configmanager/pkg/generator"
113+
"github.com/dnitsch/configmanager"
114+
)
115+
116+
func main() {
117+
cm := &configmanager.ConfigManager{}
118+
cnf := generator.NewConfig()
119+
// JSON Marshal K8s CRD into
120+
exampleK8sCrdMarshalled := `apiVersion: crd.foo.custom/v1alpha1
121+
kind: CustomFooCrd
122+
metadata:
123+
name: foo
124+
namespace: bar
125+
spec:
126+
name: baz
127+
secret_val: AWSSECRETS#/customfoo/secret-val
128+
129+
`
130+
pm, err := cm.RetrieveWithInputReplaced(exampleK8sCrdMarshalled, *cnf)
107131

108132
if err != nil {
109-
return err
110-
}
111-
112-
if err := json.Unmarshal(replaceString(m, in), &t); err != nil {
113-
return err
114-
}
115-
return nil
116-
}
117-
118-
func replaceString(inputMap generator.ParsedMap, inputString string) []byte {
119-
for oldVal, newVal := range inputMap {
120-
inputString = strings.ReplaceAll(inputString, oldVal, fmt.Sprint(newVal))
133+
panic(err)
121134
}
122-
return []byte(inputString)
135+
fmt.Println(pm)
123136
}
124137
```
125138

126-
```yaml
127-
apiVersion: crd.foo.custom/v1alpha1
128-
kind: CustomFooCrd
129-
metadata:
130-
name: foo
131-
namespace: bar
132-
spec:
133-
name: baz
134-
secret_val: AWSSECRETS#/customfoo/secret-val
135-
136-
```
137-
138139
Above example would ensure that you can safely store config/secret values on a CRD in plain text.
139140

140141
> Beware logging out the CRD after tokens have been replaced.
141142
142143
## Help
143144

144-
- More implementations should be easily added with a specific implementation under the strategy interface
145+
- More implementations should be easily added with a specific implementation under the strategy interface
145146
- e.g. AzureKMS or GCP equivalent
146147

147148
- maybe run as cron in the background to perform a periodic sync in case values change?

cmd/configmanager/fromfileinput.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/dnitsch/configmanager/cmd/utils"
7+
"github.com/dnitsch/configmanager/pkg/generator"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
var (
12+
input string
13+
retrieveFromStrInput = &cobra.Command{
14+
Use: "string-input",
15+
Aliases: []string{"fromstr", "getfromstr"},
16+
Short: `Retrieves all found token values in a specified string input`,
17+
Long: `Retrieves all found token values in a specified string input and optionally writes to a file or to stdout in a bash compliant`,
18+
RunE: retrieveFromStr,
19+
PreRunE: func(cmd *cobra.Command, args []string) error {
20+
// if len(input) < 1 && !getStdIn() {
21+
if len(input) < 1 {
22+
return fmt.Errorf("must include input")
23+
}
24+
return nil
25+
},
26+
}
27+
)
28+
29+
func init() {
30+
retrieveFromStrInput.PersistentFlags().StringVarP(&input, "input", "i", "", "Contents of a string inside a variable to be searched for tokens. e.g. -i $(cat /som/file)")
31+
retrieveFromStrInput.MarkPersistentFlagRequired("input")
32+
retrieveFromStrInput.PersistentFlags().StringVarP(&path, "path", "p", "./app.env", "Path where to write out the replaced a config/secret variables. Special value of stdout can be used to return the output to stdout e.g. -p stdout, unix style output only")
33+
configmanagerCmd.AddCommand(retrieveFromStrInput)
34+
}
35+
36+
func retrieveFromStr(cmd *cobra.Command, args []string) error {
37+
conf := generator.NewConfig().WithTokenSeparator(tokenSeparator).WithOutputPath(path)
38+
gv := generator.NewGenerator().WithConfig(conf)
39+
return utils.GenerateStrOut(gv, input)
40+
}

cmd/configmanager/retrieve.go

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"fmt"
55

6+
"github.com/dnitsch/configmanager/cmd/utils"
67
"github.com/dnitsch/configmanager/pkg/generator"
78
"github.com/spf13/cobra"
89
)
@@ -11,10 +12,9 @@ import (
1112
var tokenArray []string
1213

1314
var (
14-
tokens []string
15-
path string
16-
tokenSeparator string
17-
retrieveCmd = &cobra.Command{
15+
tokens []string
16+
path string
17+
retrieveCmd = &cobra.Command{
1818
Use: "retrieve",
1919
Aliases: []string{"r", "fetch", "get"},
2020
Short: `Retrieves a value for token(s) specified`,
@@ -33,16 +33,11 @@ func init() {
3333
retrieveCmd.PersistentFlags().StringArrayVarP(&tokens, "token", "t", tokenArray, "Token pointing to a config/secret variable. This can be specified multiple times.")
3434
retrieveCmd.MarkPersistentFlagRequired("token")
3535
retrieveCmd.PersistentFlags().StringVarP(&path, "path", "p", "./app.env", "Path where to write out the replaced a config/secret variables. Special value of stdout can be used to return the output to stdout e.g. -p stdout, unix style output only")
36-
retrieveCmd.PersistentFlags().StringVarP(&tokenSeparator, "token-separator", "s", "#", "Separator to use to mark concrete store and the key within it")
3736
configmanagerCmd.AddCommand(retrieveCmd)
3837
}
3938

4039
func retrieveRun(cmd *cobra.Command, args []string) error {
4140
conf := generator.NewConfig().WithTokenSeparator(tokenSeparator).WithOutputPath(path)
4241
gv := generator.NewGenerator().WithConfig(conf)
43-
err := gv.GenerateFromCmd(tokens)
44-
if err != nil {
45-
return err
46-
}
47-
return nil
42+
return utils.GenerateFromCmd(gv, tokens)
4843
}

cmd/configmanager/root.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010

1111
var (
1212
verbose bool
13+
tokenSeparator string
14+
keySeparator string
1315
configmanagerCmd = &cobra.Command{
1416
Use: config.SELF_NAME,
1517
Short: fmt.Sprintf("%s CLI for retrieving and inserting config or secret variables", config.SELF_NAME),
@@ -28,4 +30,6 @@ func Execute() {
2830

2931
func init() {
3032
configmanagerCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Verbosity level")
33+
configmanagerCmd.PersistentFlags().StringVarP(&tokenSeparator, "token-separator", "s", "#", "Separator to use to mark concrete store and the key within it")
34+
configmanagerCmd.PersistentFlags().StringVarP(&keySeparator, "key-separator", "k", "|", "Separator to use to mark a key look up in a map. e.g. AWSSECRETS#/token/map|key1")
3135
}

cmd/utils/utils.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// command line utils
2+
package utils
3+
4+
import (
5+
"fmt"
6+
7+
"github.com/dnitsch/configmanager"
8+
"github.com/dnitsch/configmanager/pkg/generator"
9+
"github.com/dnitsch/configmanager/pkg/log"
10+
)
11+
12+
// GenerateFromTokens is a helper cmd method to call from retrieveCmd
13+
func GenerateFromCmd(gv *generator.GenVars, tokens []string) error {
14+
_, err := gv.Generate(tokens)
15+
if err != nil {
16+
log.Errorf("%e", err)
17+
return err
18+
}
19+
// Conver to ExportVars
20+
gv.ConvertToExportVar()
21+
return gv.FlushToFile()
22+
}
23+
24+
func GenerateStrOut(gv *generator.GenVars, input string) error {
25+
c := configmanager.ConfigManager{}
26+
27+
str, err := c.RetrieveWithInputReplaced(input, *gv.Config())
28+
if err != nil {
29+
return err
30+
}
31+
32+
return gv.StrToFile(str)
33+
}
34+
35+
//UploadTokensWithVals takes in a map of key/value pairs and uploads them
36+
func UploadTokensWithVals(tokens map[string]string) error {
37+
return fmt.Errorf("notYetImplemented")
38+
}

configmanager_test.go

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@ func (m *mockGenVars) Generate(tokens []string) (generator.ParsedMap, error) {
2020
return pm, nil
2121
}
2222

23-
func (m *mockGenVars) ConvertToExportVar() {
23+
func (m *mockGenVars) ConvertToExportVar() []string {
24+
return []string{}
2425
}
2526

26-
func (m *mockGenVars) FlushToFile() (string, error) {
27-
return "", nil
27+
func (m *mockGenVars) FlushToFile() error {
28+
return nil
29+
}
30+
31+
func (m *mockGenVars) StrToFile(str string) error {
32+
return nil
2833
}
2934

3035
func Test_retrieve(t *testing.T) {
@@ -60,3 +65,63 @@ func Test_retrieve(t *testing.T) {
6065
})
6166
}
6267
}
68+
69+
var strT1 = `
70+
space: preserved
71+
indents: preserved
72+
arr: [ "FOO#/test" ]
73+
// comments preserved
74+
arr:
75+
- "FOO#/test"
76+
`
77+
78+
var strT2 = `
79+
// TOML
80+
[[somestuff]]
81+
key = "FOO#/test"
82+
`
83+
84+
func Test_retrieveWithInputReplaced(t *testing.T) {
85+
tests := []struct {
86+
name string
87+
input string
88+
genvar generator.Generatoriface
89+
expect string
90+
}{
91+
{
92+
name: "strYaml",
93+
input: strT1,
94+
genvar: &mockGenVars{},
95+
expect: `
96+
space: preserved
97+
indents: preserved
98+
arr: [ "val1" ]
99+
// comments preserved
100+
arr:
101+
- "val1"
102+
`,
103+
},
104+
{
105+
name: "strToml",
106+
input: strT2,
107+
genvar: &mockGenVars{},
108+
expect: `
109+
// TOML
110+
[[somestuff]]
111+
key = "val1"
112+
`,
113+
},
114+
}
115+
116+
for _, tt := range tests {
117+
t.Run(tt.name, func(t *testing.T) {
118+
got, err := retrieveWithInputReplaced(tt.input, tt.genvar)
119+
if err != nil {
120+
t.Errorf("failed with %v", err)
121+
}
122+
if got != tt.expect {
123+
t.Errorf(testutils.TestPhrase, tt.expect, got)
124+
}
125+
})
126+
}
127+
}

examples/examples.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/dnitsch/configmanager"
7+
"github.com/dnitsch/configmanager/pkg/generator"
8+
)
9+
10+
const DO_STUFF_WITH_VALS_HERE = "connstring:user@%v:host=%s/someschema..."
11+
12+
func main() {
13+
retrieveExample()
14+
retrieveStringOut()
15+
}
16+
17+
// retrieveExample uses the standard Retrieve method on the API
18+
// this will return generator.ParsedMap which can be later used for more complex use cases
19+
func retrieveExample() {
20+
cm := &configmanager.ConfigManager{}
21+
cnf := generator.NewConfig()
22+
23+
pm, err := cm.Retrieve([]string{"token1", "token2"}, *cnf)
24+
25+
if err != nil {
26+
panic(err)
27+
}
28+
29+
// put in a loop for many config params
30+
if pwd, ok := pm["token1"]; ok {
31+
if host, ok := pm["token2"]; ok {
32+
fmt.Println(fmt.Sprintf(DO_STUFF_WITH_VALS_HERE, pwd, fmt.Sprintf("%s", host)))
33+
}
34+
}
35+
}
36+
37+
// retrieveStringOut accepts a string as an input
38+
func retrieveStringOut() {
39+
cm := &configmanager.ConfigManager{}
40+
cnf := generator.NewConfig()
41+
// JSON Marshal K8s CRD into
42+
exampleK8sCrdMarshalled := `apiVersion: crd.foo.custom/v1alpha1
43+
kind: CustomFooCrd
44+
metadata:
45+
name: foo
46+
namespace: bar
47+
spec:
48+
name: baz
49+
secret_val: AWSSECRETS#/customfoo/secret-val
50+
51+
`
52+
pm, err := cm.RetrieveWithInputReplaced(exampleK8sCrdMarshalled, *cnf)
53+
54+
if err != nil {
55+
panic(err)
56+
}
57+
fmt.Println(pm)
58+
}

0 commit comments

Comments
 (0)