Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(providers): Add support for fetching from k8s secrets #186

Merged
merged 7 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore(test): add a test, improve error handling
Signed-off-by: Bernardo Salazar <[email protected]>
  • Loading branch information
bersalazar committed Dec 12, 2023
commit 4616e3d2c77e30d118dbb357d3eb4e56c5186be6
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ Fetch values from Kubernetes secrets:
- `ref+k8s://NAMESPACE/SECRET_NAME/KEY[?kubeConfigPath=<path_to_kubeconfig>&kubeContext=<kubernetes context name>]`

Authentication to the Kubernetes cluster is done by referencing the local kubeconfig file.
The path to the kubeconfig can be specified as a URI parameter, read from the `KUBECONFIG` environment variable or the provider with attempt to read `$HOME/.kube/config`.
The path to the kubeconfig can be specified as a URI parameter, read from the `KUBECONFIG` environment variable or the provider will attempt to read `$HOME/.kube/config`.
The Kubernetes context can be specified as a URI parameteter.

Environment variables:
Expand Down Expand Up @@ -792,7 +792,7 @@ That's not the business of vals.

Instead, use vals solely for composing sets of values that are then input to another templating engine or data manipulation language like Jsonnet and CUE.

Note though, `vals` dose have support for simple string interpolation like usage. See [Expression Syntax](#expression-syntax) for more information.
Note though, `vals` does have support for simple string interpolation like usage. See [Expression Syntax](#expression-syntax) for more information.

### Merge

Expand Down
16 changes: 8 additions & 8 deletions pkg/providers/k8s/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ type provider struct {
KubeContext string
}

func New(l *log.Logger, cfg api.StaticConfig) *provider {
func New(l *log.Logger, cfg api.StaticConfig) (*provider, error) {
p := &provider{
log: l,
}

kubeConfig, err := getKubeConfig(cfg)
if err != nil {
fmt.Printf("An error occurred getting the Kubeconfig path: %s\n", err)
return p
fmt.Printf("Unable to get a valid Kubeconfig path: %s\n", err)
bersalazar marked this conversation as resolved.
Show resolved Hide resolved
return nil, err
}

p.KubeConfigPath = kubeConfig
p.KubeContext = getKubeContext(cfg)

return p
return p, nil
}

func getKubeConfig(cfg api.StaticConfig) (string, error) {
Expand Down Expand Up @@ -79,13 +79,13 @@ func (p *provider) GetString(path string) (string, error) {
secretName := splits[1]
key := splits[2]

if p.KubeConfigPath == "" {
return "", fmt.Errorf("No Kubeconfig path was found")
secretData, err := getSecret(namespace, secretName, p.KubeConfigPath, p.KubeContext, context.Background())
if err != nil {
return "", fmt.Errorf("Unable to get secret %s/%s: %s", namespace, secretName, err)
}

secretData, err := getSecret(namespace, secretName, p.KubeConfigPath, p.KubeContext, context.Background())
secret, exists := secretData[key]
if err != nil || !exists {
if !exists {
return "", fmt.Errorf("Key %s does not exist in %s/%s", key, namespace, secretName)
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/stringmapprovider/stringmapprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func New(l *log.Logger, provider api.StaticConfig) (api.LazyLoadedStringMapProvi
case "gkms":
return gkms.New(l, provider), nil
case "k8s":
return k8s.New(l, provider), nil
return k8s.New(l, provider)
}

return nil, fmt.Errorf("failed initializing string-map provider from config: %v", provider)
Expand Down
2 changes: 1 addition & 1 deletion pkg/stringprovider/stringprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func New(l *log.Logger, provider api.StaticConfig) (api.LazyLoadedStringProvider
case "gkms":
return gkms.New(l, provider), nil
case "k8s":
return k8s.New(l, provider), nil
return k8s.New(l, provider)
}

return nil, fmt.Errorf("failed initializing string provider from config: %v", provider)
Expand Down
3 changes: 1 addition & 2 deletions vals.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,7 @@ func (r *Runtime) prepare() (*expansion.ExpandRegexMatch, error) {
p := gkms.New(r.logger, conf)
return p, nil
case ProviderK8s:
p := k8s.New(r.logger, conf)
return p, nil
return k8s.New(r.logger, conf)
}
return nil, fmt.Errorf("no provider registered for scheme %q", scheme)
}
Expand Down
69 changes: 69 additions & 0 deletions vals_k8s_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package vals

import (
"fmt"
"os"
"testing"

"github.com/google/go-cmp/cmp"
)

func TestValues_k8s(t *testing.T) {
// Setup:
// create a local Kubernetes cluster using minikube:
// minikube start
// create a namespace:
// kubectl create namespace test-namespace
// create a secret:
// kubectl create secret generic mysecret -n test-namespace --from-literal=key=p4ssw0rd

type testcase struct {
template map[string]interface{}
expected map[string]interface{}
}

namespace := "test-namespace"
key := "key"
homeDir, _ := os.UserHomeDir()

testcases := []testcase{
{
template: map[string]interface{}{
"test_key": fmt.Sprintf("secretref+k8s://%s/%s/%s", namespace, "mysecret", key),
},
expected: map[string]interface{}{
"test_key": "p4ssw0rd",
},
},
{
template: map[string]interface{}{
"test_key": fmt.Sprintf("secretref+k8s://%s/%s/%s?kubeContext=minikube", namespace, "mysecret", key),
},
expected: map[string]interface{}{
"test_key": "p4ssw0rd",
},
},
{
template: map[string]interface{}{
"test_key": fmt.Sprintf("secretref+k8s://%s/%s/%s?kubeContext=minikube&kubeConfigPath=%s/.kube/config", namespace, "mysecret", key, homeDir),
},
expected: map[string]interface{}{
"test_key": "p4ssw0rd",
},
},
}

for i := range testcases {
tc := testcases[i]
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
vals, err := Eval(tc.template)
if err != nil {
t.Fatalf("%v", err)
}
diff := cmp.Diff(tc.expected, vals)
if diff != "" {
t.Errorf("unexpected diff: %s", diff)
}
})
}
}