diff --git a/.gitignore b/.gitignore index feb6e6e..7205b04 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ *.so *.dylib +.idea + # Test binary, built with `go test -c` *.test diff --git a/Dockerfile b/Dockerfile index c8fa2ae..18f29a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine/helm +FROM alpine/helm:3.0.2 MAINTAINER Erin Call COPY build/drone-helm /bin/drone-helm diff --git a/README.md b/README.md index 96eedaa..14351e1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Drone plugin for Helm 3 +[![Build Status](https://cloud.drone.io/api/badges/pelotech/drone-helm3/status.svg)](https://cloud.drone.io/pelotech/drone-helm3) +[![Go Report](https://goreportcard.com/badge/github.com/pelotech/drone-helm3)](https://goreportcard.com/report/github.com/pelotech/drone-helm3) +[![](https://images.microbadger.com/badges/image/pelotech/drone-helm3.svg)](https://microbadger.com/images/pelotech/drone-helm3 "Get your own image badge on microbadger.com") + This plugin provides an interface between [Drone](https://drone.io/) and [Helm 3](https://github.com/kubernetes/helm): * Lint your charts @@ -58,9 +62,10 @@ steps: drone-helm3 is largely backwards-compatible with drone-helm. There are some known differences: -* `prefix` must be supplied via the `settings` block, not `environment`. +* You'll need to migrate the deployments in the cluster [helm-v2-to-helm-v3](https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/). * EKS is not supported. See [#5](https://github.com/pelotech/drone-helm3/issues/5) for more information. -* Several settings no longer have any effect: +* The `prefix` setting is no longer supported. If you were relying on the `prefix` setting with `secrets: [...]`, you'll need to switch to the `from_secret` syntax. +* Several settings no longer have any effect. The plugin will produce warnings if any of these are present: * `purge` -- this is the default behavior in Helm 3 * `recreate_pods` * `tiller_ns` @@ -70,3 +75,11 @@ drone-helm3 is largely backwards-compatible with drone-helm. There are some know * `stable_repo_url` Since helm 3 does not require Tiller, we also recommend switching to a service account with less-expansive permissions. + +### [Contributing](docs/contributing.md) + +This repo is setup in a way that if you enable a personal drone server to build your fork it will + build and publish your image (makes it easier to test PRs and use the image till the contributions get merged) + +* Build local ```DRONE_REPO_OWNER=josmo DRONE_REPO_NAME=drone-ecs drone exec``` +* on your server (or cloud.drone.io) just make sure you have DOCKER_USERNAME, DOCKER_PASSWORD, and PLUGIN_REPO set as secrets diff --git a/docs/parameter_reference.md b/docs/parameter_reference.md index 52f3dd7..9575d41 100644 --- a/docs/parameter_reference.md +++ b/docs/parameter_reference.md @@ -7,7 +7,6 @@ | update_dependencies | boolean | Calls `helm dependency update` before running the main command.| | helm_repos | list\ | Calls `helm repo add $repo` before running the main command. Each string should be formatted as `repo_name=https://repo.url/`. | | namespace | string | Kubernetes namespace to use for this operation. | -| prefix | string | Expect environment variables to be prefixed with the given string. For more details, see "Using the prefix setting" below. | | debug | boolean | Generate debug output within drone-helm3 and pass `--debug` to all helm commands. Use with care, since the debug output may include secrets. | ## Linting @@ -62,7 +61,7 @@ Uninstallations are triggered when the `helm_command` setting is "uninstall" or ### Where to put settings -Any setting (with the exception of `prefix`; [see below](#user-content-using-the-prefix-setting)), can go in either the `settings` or `environment` section. +Any setting can go in either the `settings` or `environment` section. ### Formatting non-string values @@ -87,45 +86,3 @@ Note that **list members must not contain commas**. Both of the following are eq values_files: [ "./over_9,000.yml" ] values_files: [ "./over_9", "000.yml" ] ``` - -### Using the `prefix` setting - -Because the prefix setting is meta-configuration, it has some inherent edge-cases. Here is what it does in the cases we've thought of: - -Unlike the other settings, it must be declared in the `settings` block, not `environment`: - -```yaml -settings: - prefix: helm # drone-helm3 will look for environment variables called HELM_VARNAME -environment: - prefix: armet # no effect -``` - -It does not apply to configuration in the `settings` block, only in `environment`: - -```yaml -settings: - prefix: helm - helm_timeout: 5m # no effect -environment: - helm_timeout: 2m # timeout will be 2 minutes -``` - -If the environment contains a variable in non-prefixed form, it will still be applied: - -```yaml -settings: - prefix: helm -environment: - timeout: 2m # timeout will be 2 minutes -``` - -If the environment contains both the prefixed and non-prefixed forms, drone-helm3 will use the prefixed form: - -```yaml -settings: - prefix: helm -environment: - timeout: 5m # overridden - helm_timeout: 2m # timeout will be 2 minutes -``` diff --git a/internal/helm/config.go b/internal/helm/config.go index a4d1914..2365ce2 100644 --- a/internal/helm/config.go +++ b/internal/helm/config.go @@ -4,22 +4,26 @@ import ( "fmt" "github.com/kelseyhightower/envconfig" "io" + "os" "regexp" + "strings" ) -var justNumbers = regexp.MustCompile(`^\d+$`) +var ( + justNumbers = regexp.MustCompile(`^\d+$`) + deprecatedVars = []string{"PURGE", "RECREATE_PODS", "TILLER_NS", "UPGRADE", "CANARY_IMAGE", "CLIENT_ONLY", "STABLE_REPO_URL"} +) // The Config struct captures the `settings` and `environment` blocks in the application's drone // config. Configuration in drone's `settings` block arrives as uppercase env vars matching the // config key, prefixed with `PLUGIN_`. Config from the `environment` block is uppercased, but does -// not have the `PLUGIN_` prefix. It may, however, be prefixed with the value in `$PLUGIN_PREFIX`. +// not have the `PLUGIN_` prefix. type Config struct { // Configuration for drone-helm itself Command string `envconfig:"HELM_COMMAND"` // Helm command to run DroneEvent string `envconfig:"DRONE_BUILD_EVENT"` // Drone event that invoked this plugin. UpdateDependencies bool `split_words:"true"` // Call `helm dependency update` before the main command AddRepos []string `envconfig:"HELM_REPOS"` // Call `helm repo add` before the main command - Prefix string `` // Prefix to use when looking up secret env vars Debug bool `` // Generate debug output and pass --debug to all helm commands Values string `` // Argument to pass to --set in applicable helm commands StringValues string `split_words:"true"` // Argument to pass to --set-string in applicable helm commands @@ -53,18 +57,10 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) { return nil, err } - prefix := cfg.Prefix - if err := envconfig.Process("", &cfg); err != nil { return nil, err } - if prefix != "" { - if err := envconfig.Process(cfg.Prefix, &cfg); err != nil { - return nil, err - } - } - if justNumbers.MatchString(cfg.Timeout) { cfg.Timeout = fmt.Sprintf("%ss", cfg.Timeout) } @@ -73,6 +69,8 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) { cfg.logDebug() } + cfg.deprecationWarn() + return &cfg, nil } @@ -82,3 +80,13 @@ func (cfg Config) logDebug() { } fmt.Fprintf(cfg.Stderr, "Generated config: %+v\n", cfg) } + +func (cfg *Config) deprecationWarn() { + for _, varname := range deprecatedVars { + _, barePresent := os.LookupEnv(varname) + _, prefixedPresent := os.LookupEnv("PLUGIN_" + varname) + if barePresent || prefixedPresent { + fmt.Fprintf(cfg.Stderr, "Warning: ignoring deprecated '%s' setting\n", strings.ToLower(varname)) + } + } +} diff --git a/internal/helm/config_test.go b/internal/helm/config_test.go index f39dd0c..6cad789 100644 --- a/internal/helm/config_test.go +++ b/internal/helm/config_test.go @@ -1,6 +1,7 @@ package helm import ( + "fmt" "github.com/stretchr/testify/suite" "os" "strings" @@ -19,7 +20,6 @@ func TestConfigTestSuite(t *testing.T) { } func (suite *ConfigTestSuite) TestNewConfigWithPluginPrefix() { - suite.unsetenv("PLUGIN_PREFIX") suite.unsetenv("HELM_COMMAND") suite.unsetenv("UPDATE_DEPENDENCIES") suite.unsetenv("DEBUG") @@ -37,7 +37,6 @@ func (suite *ConfigTestSuite) TestNewConfigWithPluginPrefix() { } func (suite *ConfigTestSuite) TestNewConfigWithNoPrefix() { - suite.unsetenv("PLUGIN_PREFIX") suite.unsetenv("PLUGIN_HELM_COMMAND") suite.unsetenv("PLUGIN_UPDATE_DEPENDENCIES") suite.unsetenv("PLUGIN_DEBUG") @@ -54,56 +53,14 @@ func (suite *ConfigTestSuite) TestNewConfigWithNoPrefix() { suite.True(cfg.Debug) } -func (suite *ConfigTestSuite) TestNewConfigWithConfigurablePrefix() { - suite.unsetenv("API_SERVER") - suite.unsetenv("PLUGIN_API_SERVER") - - suite.setenv("PLUGIN_PREFIX", "prix_fixe") - suite.setenv("PRIX_FIXE_API_SERVER", "your waiter this evening") - - cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}) - suite.Require().NoError(err) - - suite.Equal("prix_fixe", cfg.Prefix) - suite.Equal("your waiter this evening", cfg.APIServer) -} - -func (suite *ConfigTestSuite) TestPrefixSettingDoesNotAffectPluginPrefix() { - suite.setenv("PLUGIN_PREFIX", "IXFREP") - suite.setenv("PLUGIN_HELM_COMMAND", "wake me up") - suite.setenv("IXFREP_PLUGIN_HELM_COMMAND", "send me to sleep inside") - - cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}) - suite.Require().NoError(err) - - suite.Equal("wake me up", cfg.Command) -} - -func (suite *ConfigTestSuite) TestPrefixSettingMustHavePluginPrefix() { - suite.unsetenv("PLUGIN_PREFIX") - suite.setenv("PREFIX", "refpix") - suite.setenv("HELM_COMMAND", "gimme more") - suite.setenv("REFPIX_HELM_COMMAND", "gimme less") - - cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}) - suite.Require().NoError(err) - - suite.Equal("gimme more", cfg.Command) -} - func (suite *ConfigTestSuite) TestNewConfigWithConflictingVariables() { suite.setenv("PLUGIN_HELM_COMMAND", "execute order 66") suite.setenv("HELM_COMMAND", "defend the jedi") // values from the `environment` block override those from `settings` - suite.setenv("PLUGIN_PREFIX", "prod") - suite.setenv("TIMEOUT", "5m0s") - suite.setenv("PROD_TIMEOUT", "2m30s") // values from prefixed env vars override those from non-prefixed ones - cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}) suite.Require().NoError(err) suite.Equal("defend the jedi", cfg.Command) - suite.Equal("2m30s", cfg.Timeout) } func (suite *ConfigTestSuite) TestNewConfigInfersNumbersAreSeconds() { @@ -123,6 +80,24 @@ func (suite *ConfigTestSuite) TestNewConfigSetsWriters() { suite.Equal(stderr, cfg.Stderr) } +func (suite *ConfigTestSuite) TestDeprecatedSettingWarnings() { + for _, varname := range deprecatedVars { + suite.setenv(varname, "deprecoat") // environment-block entries should cause warnings + } + + suite.unsetenv("PURGE") + suite.setenv("PLUGIN_PURGE", "true") // settings-block entries should cause warnings + suite.setenv("UPGRADE", "") // entries should cause warnings even when set to empty string + + stderr := &strings.Builder{} + _, err := NewConfig(&strings.Builder{}, stderr) + suite.NoError(err) + + for _, varname := range deprecatedVars { + suite.Contains(stderr.String(), fmt.Sprintf("Warning: ignoring deprecated '%s' setting\n", strings.ToLower(varname))) + } +} + func (suite *ConfigTestSuite) TestLogDebug() { suite.setenv("DEBUG", "true") suite.setenv("HELM_COMMAND", "upgrade")