diff --git a/main.go b/main.go index b4727ed..c6e4d4d 100644 --- a/main.go +++ b/main.go @@ -59,6 +59,11 @@ func main() { Usage: "Kubernetes helm release", EnvVar: "PLUGIN_VALUES,VALUES", }, + cli.StringFlag{ + Name: "string_values", + Usage: "Kubernetes helm release", + EnvVar: "PLUGIN_STRING_VALUES,STRING_VALUES", + }, cli.StringFlag{ Name: "values_files", Usage: "Helm values override files", @@ -151,34 +156,35 @@ func run(c *cli.Context) error { } plugin := Plugin{ Config: Config{ - APIServer: c.String("api_server"), - Token: c.String("token"), - Certificate: c.String("certificate"), - ServiceAccount: c.String("service-account"), - KubeConfig: c.String("kube-config"), - HelmCommand: c.String("helm_command"), - Namespace: c.String("namespace"), - SkipTLSVerify: c.Bool("skip_tls_verify"), - Values: c.String("values"), - ValuesFiles: c.String("values_files"), - Release: c.String("release"), - HelmRepos: c.StringSlice("helm_repos"), - Chart: c.String("chart"), - Version: c.String("chart-version"), - Debug: c.Bool("debug"), - DryRun: c.Bool("dry-run"), - Secrets: c.StringSlice("secrets"), - Prefix: c.String("prefix"), - TillerNs: c.String("tiller-ns"), - Wait: c.Bool("wait"), - RecreatePods: c.Bool("recreate-pods"), - ClientOnly: c.Bool("client-only"), - CanaryImage: c.Bool("canary-image"), - Upgrade: c.Bool("upgrade"), - ReuseValues: c.Bool("reuse-values"), - Timeout: c.String("timeout"), - Force: c.Bool("force"), - UpdateDependencies: c.Bool("update-dependencies"), + APIServer: c.String("api_server"), + Token: c.String("token"), + Certificate: c.String("certificate"), + ServiceAccount: c.String("service-account"), + KubeConfig: c.String("kube-config"), + HelmCommand: c.String("helm_command"), + Namespace: c.String("namespace"), + SkipTLSVerify: c.Bool("skip_tls_verify"), + Values: c.String("values"), + StringValues: c.String("string_values"), + ValuesFiles: c.String("values_files"), + Release: c.String("release"), + HelmRepos: c.StringSlice("helm_repos"), + Chart: c.String("chart"), + Version: c.String("chart-version"), + Debug: c.Bool("debug"), + DryRun: c.Bool("dry-run"), + Secrets: c.StringSlice("secrets"), + Prefix: c.String("prefix"), + TillerNs: c.String("tiller-ns"), + Wait: c.Bool("wait"), + RecreatePods: c.Bool("recreate-pods"), + ClientOnly: c.Bool("client-only"), + CanaryImage: c.Bool("canary-image"), + Upgrade: c.Bool("upgrade"), + ReuseValues: c.Bool("reuse-values"), + Timeout: c.String("timeout"), + Force: c.Bool("force"), + UpdateDependencies: c.Bool("update-dependencies"), }, } return plugin.Exec() diff --git a/plugin.go b/plugin.go index b27c034..023c73c 100644 --- a/plugin.go +++ b/plugin.go @@ -19,35 +19,36 @@ var KUBECONFIG = "/root/.kube/kubeconfig" type ( // Config maps the params we need to run Helm Config struct { - APIServer string `json:"api_server"` - Token string `json:"token"` - Certificate string `json:"certificate"` - ServiceAccount string `json:"service_account"` - KubeConfig string `json:"kube_config"` - HelmCommand string `json:"helm_command"` - SkipTLSVerify bool `json:"tls_skip_verify"` - Namespace string `json:"namespace"` - Release string `json:"release"` - Chart string `json:"chart"` - Version string `json:"version"` - Values string `json:"values"` - ValuesFiles string `json:"values_files"` - Debug bool `json:"debug"` - DryRun bool `json:"dry_run"` - Secrets []string `json:"secrets"` - Prefix string `json:"prefix"` - TillerNs string `json:"tiller_ns"` - Wait bool `json:"wait"` - RecreatePods bool `json:"recreate_pods"` - Upgrade bool `json:"upgrade"` - CanaryImage bool `json:"canary_image"` - ClientOnly bool `json:"client_only"` - ReuseValues bool `json:"reuse_values"` - Timeout string `json:"timeout"` - Force bool `json:"force"` - HelmRepos []string `json:"helm_repos"` - Purge bool `json:"purge"` - UpdateDependencies bool `json:"update_dependencies"` + APIServer string `json:"api_server"` + Token string `json:"token"` + Certificate string `json:"certificate"` + ServiceAccount string `json:"service_account"` + KubeConfig string `json:"kube_config"` + HelmCommand string `json:"helm_command"` + SkipTLSVerify bool `json:"tls_skip_verify"` + Namespace string `json:"namespace"` + Release string `json:"release"` + Chart string `json:"chart"` + Version string `json:"version"` + Values string `json:"values"` + StringValues string `json:"string_values"` + ValuesFiles string `json:"values_files"` + Debug bool `json:"debug"` + DryRun bool `json:"dry_run"` + Secrets []string `json:"secrets"` + Prefix string `json:"prefix"` + TillerNs string `json:"tiller_ns"` + Wait bool `json:"wait"` + RecreatePods bool `json:"recreate_pods"` + Upgrade bool `json:"upgrade"` + CanaryImage bool `json:"canary_image"` + ClientOnly bool `json:"client_only"` + ReuseValues bool `json:"reuse_values"` + Timeout string `json:"timeout"` + Force bool `json:"force"` + HelmRepos []string `json:"helm_repos"` + Purge bool `json:"purge"` + UpdateDependencies bool `json:"update_dependencies"` } // Plugin default Plugin struct { @@ -95,6 +96,10 @@ func setUpgradeCommand(p *Plugin) { upgrade = append(upgrade, "--set") upgrade = append(upgrade, unQuote(p.Config.Values)) } + if p.Config.StringValues != "" { + upgrade = append(upgrade, "--set-string") + upgrade = append(upgrade, unQuote(p.Config.StringValues)) + } if p.Config.ValuesFiles != "" { for _, valuesFile := range strings.Split(p.Config.ValuesFiles, ",") { upgrade = append(upgrade, "--values") @@ -217,7 +222,6 @@ func doDependencyUpdate(chart string) []string { return dependencyUpdate } - // Exec default method func (p *Plugin) Exec() error { if p.Config.Debug { @@ -311,6 +315,8 @@ func runCommand(params []string) error { func resolveSecrets(p *Plugin) { p.Config.Values = resolveEnvVar(p.Config.Values, p.Config.Prefix, p.Config.Debug) + p.Config.StringValues = resolveEnvVar(p.Config.StringValues, p.Config.Prefix, p.Config.Debug) + if p.Config.APIServer == "" { p.Config.APIServer = resolveEnvVar("${API_SERVER}", p.Config.Prefix, p.Config.Debug) } @@ -384,6 +390,7 @@ func (p *Plugin) debug() { // debug plugin obj fmt.Printf("Api server: %s \n", p.Config.APIServer) fmt.Printf("Values: %s \n", p.Config.Values) + fmt.Printf("StringValues: %s \n", p.Config.StringValues) fmt.Printf("Secrets: %s \n", p.Config.Secrets) fmt.Printf("Helm Repos: %s \n", p.Config.HelmRepos) fmt.Printf("ValuesFiles: %s \n", p.Config.ValuesFiles) diff --git a/plugin_test.go b/plugin_test.go index 4a45cb2..bffb0fe 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -59,6 +59,7 @@ func TestGetHelmCommandEmptyPushEvent(t *testing.T) { Version: "1.2.3", Release: "test-release", Values: `"image.tag=v.0.1.0,nameOverride=my-over-app"`, + StringValues: `"long_string_value=1234567890"`, Wait: true, ReuseValues: true, Timeout: "500", @@ -67,7 +68,7 @@ func TestGetHelmCommandEmptyPushEvent(t *testing.T) { } setHelmCommand(plugin) res := strings.Join(plugin.command[:], " ") - expected := "upgrade --install test-release ./chart/test --version 1.2.3 --set image.tag=v.0.1.0,nameOverride=my-over-app --namespace default --dry-run --debug --wait --reuse-values --timeout 500 --force" + expected := "upgrade --install test-release ./chart/test --version 1.2.3 --set image.tag=v.0.1.0,nameOverride=my-over-app --set-string long_string_value=1234567890 --namespace default --dry-run --debug --wait --reuse-values --timeout 500 --force" if res != expected { t.Errorf("Result is %s and we expected %s", res, expected) } @@ -88,6 +89,7 @@ func TestGetHelmCommandUpgrade(t *testing.T) { Version: "1.2.3", Release: "test-release", Values: `"image.tag=v.0.1.0,nameOverride=my-over-app"`, + StringValues: `"long_string_value=1234567890"`, Wait: true, ReuseValues: true, Timeout: "500", @@ -96,7 +98,7 @@ func TestGetHelmCommandUpgrade(t *testing.T) { } setHelmCommand(plugin) res := strings.Join(plugin.command[:], " ") - expected := "upgrade --install test-release ./chart/test --version 1.2.3 --set image.tag=v.0.1.0,nameOverride=my-over-app --namespace default --dry-run --debug --wait --reuse-values --timeout 500 --force" + expected := "upgrade --install test-release ./chart/test --version 1.2.3 --set image.tag=v.0.1.0,nameOverride=my-over-app --set-string long_string_value=1234567890 --namespace default --dry-run --debug --wait --reuse-values --timeout 500 --force" if res != expected { t.Errorf("Result is %s and we expected %s", res, expected) } @@ -115,6 +117,7 @@ func TestGetHelmDeleteCommand(t *testing.T) { Chart: "./chart/test", Release: "test-release", Values: "image.tag=v.0.1.0,nameOverride=my-over-app", + StringValues: "long_string_value=1234567890", Wait: true, }, } @@ -141,6 +144,7 @@ func TestGetHelmDeleteCommandOverried(t *testing.T) { Chart: "./chart/test", Release: "test-release", Values: "image.tag=v.0.1.0,nameOverride=my-over-app", + StringValues: "long_string_value=1234567890", Wait: true, Purge: true, }, @@ -189,6 +193,7 @@ func TestResolveSecrets(t *testing.T) { Release: "test-release", Prefix: env.prefix, Values: "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}", + StringValues: "long_string_value=1234567890", }, } @@ -220,12 +225,13 @@ func TestResolveSecrets(t *testing.T) { // Test resolving provided values testInput := []struct { - server string - values string - token string - account string + server string + values string + string_values string + token string + account string }{ - {server: "http://apiserver2", token: "123456", account: "helm2", values: "aval=test"}, + {server: "http://apiserver2", token: "123456", account: "helm2", values: "aval=test", string_values: "long_string_value=1234567890"}, } for _, input := range testInput { plugin := &Plugin{ @@ -234,6 +240,7 @@ func TestResolveSecrets(t *testing.T) { ServiceAccount: input.account, Token: input.token, Values: input.values, + StringValues: input.string_values, }, } @@ -250,6 +257,9 @@ func TestResolveSecrets(t *testing.T) { if plugin.Config.Values != input.values { t.Errorf("failed to keep Values '%s' got '%s'", input.values, plugin.Config.Values) } + if plugin.Config.StringValues != input.string_values { + t.Errorf("failed to keep StringValues '%s' got '%s'", input.string_values, plugin.Config.StringValues) + } } } @@ -265,6 +275,7 @@ func TestDetHelmRepoAdd(t *testing.T) { Release: "test-release", Prefix: "MY", Values: "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}", + StringValues: "long_string_value=1234567890", ClientOnly: true, HelmRepos: []string{ `"r1=http://r1.example.com"`, //handle quoted strings @@ -346,6 +357,7 @@ func TestSetHelpCommand(t *testing.T) { Release: "test-release", Prefix: "MY", Values: "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}", + StringValues: "long_string_value=1234567890", }, } setHelpCommand(plugin) @@ -366,6 +378,7 @@ func TestDetHelmInit(t *testing.T) { Release: "test-release", Prefix: "MY", Values: "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}", + StringValues: "long_string_value=1234567890", TillerNs: "system-test", }, } @@ -390,6 +403,7 @@ func TestDetHelmInitClient(t *testing.T) { Release: "test-release", Prefix: "MY", Values: "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}", + StringValues: "long_string_value=1234567890", ClientOnly: true, }, } @@ -417,6 +431,7 @@ func TestDetHelmInitUpgrade(t *testing.T) { Release: "test-release", Prefix: "MY", Values: "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}", + StringValues: "long_string_value=1234567890", Upgrade: true, }, } @@ -444,6 +459,7 @@ func TestDetHelmInitCanary(t *testing.T) { Release: "test-release", Prefix: "MY", Values: "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}", + StringValues: "long_string_value=1234567890", CanaryImage: true, }, } @@ -477,6 +493,7 @@ func TestResolveSecretsFallback(t *testing.T) { Release: "test-release", Prefix: "MY", Values: "image.tag=$TAG,api=${API_SERVER},nottoken=${NOTTOKEN},nameOverride=my-over-app,second.tag=${TAG}", + StringValues: "long_string_value=1234567890", }, }