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

Add Support for Exec-based Credential Plugin in Kustomization Provider #260

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ provider "kustomization" {
- `context` - (Optional) Context to use in kubeconfig with multiple contexts, if not specified the default context is used.
- `legacy_id_format` - (Optional) Defaults to `false`. Provided for backward compability, set to `true` to use the legacy ID format. Removed starting `0.9.0`.
- `gzip_last_applied_config` - (Optional) Defaults to `true`. Use a gzip compressed and base64 encoded value for the lastAppliedConfig annotation if a resource would otherwise exceed the Kubernetes max annotation size. All other resources use the regular uncompressed annotation. Set to `false` to never use the compressed annotation.
- `exec` - (Optional) Configuration block to use an [exec-based credential plugin] (https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins), e.g. call an external command to receive user credentials.
- `api_version` - (Required) API version to use when decoding the ExecCredentials resource, e.g. `client.authentication.k8s.io/v1beta1`.
- `command` - (Required) Command to execute.
- `args` - (Optional) List of arguments to pass when executing the plugin.
- `env` - (Optional) Map of environment variables to set when executing the plugin.

## Migrating resource IDs from legacy format to format enabling API version upgrades

Expand Down
46 changes: 46 additions & 0 deletions kustomize/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"

"github.com/mitchellh/go-homedir"
)
Expand Down Expand Up @@ -75,12 +76,41 @@ func Provider() *schema.Provider {
Default: true,
Description: "When 'true' compress the lastAppliedConfig annotation for resources that otherwise would exceed K8s' max annotation size. All other resources use the regular uncompressed annotation. Set to 'false' to disable compression entirely.",
},
"exec": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"api_version": {
Type: schema.TypeString,
Required: true,
},
"command": {
Type: schema.TypeString,
Required: true,
},
"env": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"args": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
Description: "",
},
},
}

p.ConfigureFunc = func(d *schema.ResourceData) (interface{}, error) {
var config *rest.Config
var err error
overrides := &clientcmd.ConfigOverrides{}

raw := d.Get("kubeconfig_raw").(string)
path := d.Get("kubeconfig_path").(string)
Expand Down Expand Up @@ -113,6 +143,22 @@ func Provider() *schema.Provider {
}
}

if v, ok := d.GetOk("exec"); ok {
exec := &clientcmdapi.ExecConfig{}
if spec, ok := v.([]interface{})[0].(map[string]interface{}); ok {
exec.InteractiveMode = clientcmdapi.IfAvailableExecInteractiveMode
exec.APIVersion = spec["api_version"].(string)
exec.Command = spec["command"].(string)
exec.Args = expandStringSlice(spec["args"].([]interface{}))
for kk, vv := range spec["env"].(map[string]interface{}) {
exec.Env = append(exec.Env, clientcmdapi.ExecEnvVar{Name: kk, Value: vv.(string)})
}
} else {
return nil, fmt.Errorf("Failed to parse exec")
}
overrides.AuthInfo.Exec = exec
}

// empty default config required to support
// using a cluster resource or data source
// that may not exist yet, to configure the provider
Expand Down
14 changes: 14 additions & 0 deletions kustomize/structures.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package kustomize

func expandStringSlice(s []interface{}) []string {
result := make([]string, len(s), len(s))
for k, v := range s {
// Handle the Terraform parser bug which turns empty strings in lists to nil.
if v == nil {
result[k] = ""
} else {
result[k] = v.(string)
}
}
return result
}
40 changes: 40 additions & 0 deletions kustomize/structures_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package kustomize

import (
"reflect"
"testing"
)

func Test_expandStringSlice(t *testing.T) {
type args struct {
s []interface{}
}
tests := []struct {
name string
given []interface{}
then []string
}{
{
"validate non empty strings not mutated",
[]interface{}{"one", "two"},
[]string{"one", "two"},
},
{
"validate nil elements are empty strings",
[]interface{}{nil, "two"},
[]string{"", "two"},
},
{
"validate empty array",
[]interface{}{},
[]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := expandStringSlice(tt.given); !reflect.DeepEqual(got, tt.then) {
t.Errorf("expandStringSlice() = %v, want %v", got, tt.then)
}
})
}
}