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

Publish to helm registry #91

Closed
Closed
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This plugin provides an interface between [Drone](https://drone.io/) and [Helm 3
* Lint your charts
* Deploy your service
* Delete your service
* Publish it to a Helm Registry (OCI-based registries)

The plugin is inpsired by [drone-helm](https://github.com/ipedrazas/drone-helm), which fills the same role for Helm 2. It provides a comparable feature-set and the configuration settings are backward-compatible.

Expand Down Expand Up @@ -58,6 +59,26 @@ steps:
from_secret: kubernetes_token
```

### Publish

```yaml
steps:
- name: publish
image: pelotech/drone-helm3
settings:
mode: publish
chart: ./
environment:
REGISTRY_URL:
from_secret: registry_url
REGISTRY_LOGIN_USER_ID:
from_secret: registry_login_user_id
REGISTRY_LOGIN_PASSWORD:
from_secret: registry_login_password
REGISTRY_REPO_NAME: <helm_repository_name>
CHART_VERSION: <version>
Comment on lines +70 to +79
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of nitpicks here:

  1. Since registry_repo_name and chart_version don't need to come from secrets, the settings block is a more appropriate spot for them.
  2. The README examples should only contain the required params, to minimize the amount of scrolling people have to do.
Suggested change
chart: ./
environment:
REGISTRY_URL:
from_secret: registry_url
REGISTRY_LOGIN_USER_ID:
from_secret: registry_login_user_id
REGISTRY_LOGIN_PASSWORD:
from_secret: registry_login_password
REGISTRY_REPO_NAME: <helm_repository_name>
CHART_VERSION: <version>
registry_repo_name: <helm_repository_name>
chart_version: <version>
environment:
REGISTRY_URL:
from_secret: registry_url
REGISTRY_LOGIN_USER_ID:
from_secret: registry_login_user_id
REGISTRY_LOGIN_PASSWORD:
from_secret: registry_login_password

```

## Upgrading from drone-helm

drone-helm3 is largely backward-compatible with drone-helm. There are some known differences:
Expand Down
Binary file modified build/drone-helm
Binary file not shown.
14 changes: 14 additions & 0 deletions docs/parameter_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ Uninstallations are triggered when the `mode` setting is "uninstall" or "delete.
| skip_tls_verify | boolean | | | Connect to the Kubernetes cluster without checking for a valid TLS certificate. Not recommended in production. |
| chart | string | | | Required when the global `update_dependencies` parameter is true. No effect otherwise. |

## Publish

Publish are triggered when the `mode` setting is "publish."

| Param name | Type | Required | Alias | Purpose |
|------------------------|----------|----------|------------------------|---------|
| registry_url | string | yes | | Registry url to where the chart be published|
| registry_login_user_id | string | yes | | Login ID for the Helm registry |
| registry_login_password| string | yes | | Login Password for the Helm registry |
| registry_repo_name | string | yes | | Repository name in the Helm registry |
| chart_version | string | yes | | Version of the chart |
| chart | string | | | Local path of the chart |
Comment on lines +73 to +80
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind aligning the whitespace here? It doesn't matter for the rendered markdown, but it makes the file easier to read while editing :)

Suggested change
| Param name | Type | Required | Alias | Purpose |
|------------------------|----------|----------|------------------------|---------|
| registry_url | string | yes | | Registry url to where the chart be published|
| registry_login_user_id | string | yes | | Login ID for the Helm registry |
| registry_login_password| string | yes | | Login Password for the Helm registry |
| registry_repo_name | string | yes | | Repository name in the Helm registry |
| chart_version | string | yes | | Version of the chart |
| chart | string | | | Local path of the chart |
| Param name | Type | Required | Alias | Purpose |
| ------------------------ | ---------- | ---------- | ------------------------ | --------- |
| registry_url | string | yes | | Registry url to where the chart be published |
| registry_login_user_id | string | yes | | Login ID for the Helm registry |
| registry_login_password | string | yes | | Login Password for the Helm registry |
| registry_repo_name | string | yes | | Repository name in the Helm registry |
| chart_version | string | yes | | Version of the chart |
| chart | string | | | Local path of the chart |



### Where to put settings

Any setting can go in either the `settings` or `environment` section. If a setting exists in _both_ sections, the version in `environment` will override the version in `settings`.
Expand Down
102 changes: 57 additions & 45 deletions internal/env/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,39 @@ var (
// not have the `PLUGIN_` prefix.
type Config struct {
// Configuration for drone-helm itself
Command string `envconfig:"mode"` // Helm command to run
DroneEvent string `envconfig:"drone_build_event"` // Drone event that invoked this plugin.
UpdateDependencies bool `split_words:"true"` // [Deprecated] Call `helm dependency update` before the main command (deprecated, use dependencies_action: update instead)
DependenciesAction string `split_words:"true"` // Call `helm dependency build` or `helm dependency update` before the main command
AddRepos []string `split_words:"true"` // Call `helm repo add` before the main command
RepoCertificate string `envconfig:"repo_certificate"` // The Helm chart repository's self-signed certificate (must be base64-encoded)
RepoCACertificate string `envconfig:"repo_ca_certificate"` // The Helm chart repository CA's self-signed certificate (must be base64-encoded)
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
ValuesFiles []string `split_words:"true"` // Arguments to pass to --values in applicable helm commands
Namespace string `` // Kubernetes namespace for all helm commands
KubeToken string `split_words:"true"` // Kubernetes authentication token to put in .kube/config
SkipTLSVerify bool `envconfig:"skip_tls_verify"` // Put insecure-skip-tls-verify in .kube/config
Certificate string `envconfig:"kube_certificate"` // The Kubernetes cluster CA's self-signed certificate (must be base64-encoded)
APIServer string `envconfig:"kube_api_server"` // The Kubernetes cluster's API endpoint
ServiceAccount string `envconfig:"kube_service_account"` // Account to use for connecting to the Kubernetes cluster
ChartVersion string `split_words:"true"` // Specific chart version to use in `helm upgrade`
DryRun bool `split_words:"true"` // Pass --dry-run to applicable helm commands
Wait bool `envconfig:"wait_for_upgrade"` // Pass --wait to applicable helm commands
ReuseValues bool `split_words:"true"` // Pass --reuse-values to `helm upgrade`
KeepHistory bool `split_words:"true"` // Pass --keep-history to `helm uninstall`
Timeout string `` // Argument to pass to --timeout in applicable helm commands
Chart string `` // Chart argument to use in applicable helm commands
Release string `` // Release argument to use in applicable helm commands
Force bool `envconfig:"force_upgrade"` // Pass --force to applicable helm commands
AtomicUpgrade bool `split_words:"true"` // Pass --atomic to `helm upgrade`
CleanupOnFail bool `envconfig:"cleanup_failed_upgrade"` // Pass --cleanup-on-fail to `helm upgrade`
LintStrictly bool `split_words:"true"` // Pass --strict to `helm lint`
Command string `envconfig:"mode"` // Helm command to run
DroneEvent string `envconfig:"drone_build_event"` // Drone event that invoked this plugin.
UpdateDependencies bool `split_words:"true"` // [Deprecated] Call `helm dependency update` before the main command (deprecated, use dependencies_action: update instead)
DependenciesAction string `split_words:"true"` // Call `helm dependency build` or `helm dependency update` before the main command
AddRepos []string `split_words:"true"` // Call `helm repo add` before the main command
RepoCertificate string `envconfig:"repo_certificate"` // The Helm chart repository's self-signed certificate (must be base64-encoded)
RepoCACertificate string `envconfig:"repo_ca_certificate"` // The Helm chart repository CA's self-signed certificate (must be base64-encoded)
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
ValuesFiles []string `split_words:"true"` // Arguments to pass to --values in applicable helm commands
Namespace string `` // Kubernetes namespace for all helm commands
KubeToken string `split_words:"true"` // Kubernetes authentication token to put in .kube/config
SkipTLSVerify bool `envconfig:"skip_tls_verify"` // Put insecure-skip-tls-verify in .kube/config
Certificate string `envconfig:"kube_certificate"` // The Kubernetes cluster CA's self-signed certificate (must be base64-encoded)
APIServer string `envconfig:"kube_api_server"` // The Kubernetes cluster's API endpoint
ServiceAccount string `envconfig:"kube_service_account"` // Account to use for connecting to the Kubernetes cluster
ChartVersion string `split_words:"true"` // Specific chart version to use in `helm upgrade`
DryRun bool `split_words:"true"` // Pass --dry-run to applicable helm commands
Wait bool `envconfig:"wait_for_upgrade"` // Pass --wait to applicable helm commands
ReuseValues bool `split_words:"true"` // Pass --reuse-values to `helm upgrade`
KeepHistory bool `split_words:"true"` // Pass --keep-history to `helm uninstall`
Timeout string `` // Argument to pass to --timeout in applicable helm commands
Chart string `` // Chart argument to use in applicable helm commands
Release string `` // Release argument to use in applicable helm commands
Force bool `envconfig:"force_upgrade"` // Pass --force to applicable helm commands
AtomicUpgrade bool `split_words:"true"` // Pass --atomic to `helm upgrade`
CleanupOnFail bool `envconfig:"cleanup_failed_upgrade"` // Pass --cleanup-on-fail to `helm upgrade`
LintStrictly bool `split_words:"true"` // Pass --strict to `helm lint`
RegistryURL string `envconfig:"registry_url"` // Helm Registry URL
RegistryLoginUserID string `envconfig:"registry_login_user_id"` // Helm Registry login user ID
RegistryLoginPassword string `envconfig:"registry_login_password"` // Helm Registry login password
RegistryRepoName string `envconfig:"registry_repo_name"` // Helm Registry repository name

Stdout io.Writer `ignored:"true"`
Stderr io.Writer `ignored:"true"`
Expand All @@ -67,14 +71,18 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
}

cfg := Config{
Command: aliases.Command,
AddRepos: aliases.AddRepos,
APIServer: aliases.APIServer,
ServiceAccount: aliases.ServiceAccount,
Wait: aliases.Wait,
Force: aliases.Force,
KubeToken: aliases.KubeToken,
Certificate: aliases.Certificate,
Command: aliases.Command,
AddRepos: aliases.AddRepos,
APIServer: aliases.APIServer,
ServiceAccount: aliases.ServiceAccount,
Wait: aliases.Wait,
Force: aliases.Force,
KubeToken: aliases.KubeToken,
Certificate: aliases.Certificate,
RegistryURL: aliases.RegistryURL,
RegistryLoginUserID: aliases.RegistryLoginUserID,
RegistryLoginPassword: aliases.RegistryLoginPassword,
RegistryRepoName: aliases.RegistryRepoName,

Stdout: stdout,
Stderr: stderr,
Expand Down Expand Up @@ -145,12 +153,16 @@ func (cfg *Config) deprecationWarn() {
}

type settingAliases struct {
Command string `envconfig:"helm_command"`
AddRepos []string `envconfig:"helm_repos"`
APIServer string `envconfig:"api_server"`
ServiceAccount string `split_words:"true"`
Wait bool ``
Force bool ``
KubeToken string `envconfig:"kubernetes_token"`
Certificate string `envconfig:"kubernetes_certificate"`
Command string `envconfig:"helm_command"`
AddRepos []string `envconfig:"helm_repos"`
APIServer string `envconfig:"api_server"`
ServiceAccount string `split_words:"true"`
Wait bool ``
Force bool ``
KubeToken string `envconfig:"kubernetes_token"`
Certificate string `envconfig:"kubernetes_certificate"`
RegistryURL string `envconfig:"registry_url"`
RegistryLoginUserID string `envconfig:"registry_login_user_id"`
RegistryLoginPassword string `envconfig:"registry_login_password"`
RegistryRepoName string `envconfig:"registry_repo_name"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newly-added settings don't need entries in settingAliases—the aliases are for backwards compatibility if a setting gets renamed.

}
18 changes: 17 additions & 1 deletion internal/helm/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package helm
import (
"errors"
"fmt"
"os"

"github.com/pelotech/drone-helm3/internal/env"
"github.com/pelotech/drone-helm3/internal/run"
"os"
)

const (
Expand Down Expand Up @@ -61,6 +62,8 @@ func determineSteps(cfg env.Config) *func(env.Config) []Step {
return &uninstall
case "lint":
return &lint
case "publish":
return &publish
case "help":
return &help
default:
Expand Down Expand Up @@ -136,3 +139,16 @@ var lint = func(cfg env.Config) []Step {
var help = func(cfg env.Config) []Step {
return []Step{run.NewHelp(cfg)}
}

var publish = func(cfg env.Config) []Step {
fmt.Println("inside publish")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some leftover debug logging here

Suggested change
fmt.Println("inside publish")

var steps []Step

os.Setenv("HELM_EXPERIMENTAL_OCI", "1")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than a bare os.Setenv, please set this in the environment for the cmd in Chart and Registry:

	environ := os.Environ()
	environ = append(environ, "HELM_EXPERIMENTAL_OCI=1")
	reg.cmd.Env(environ)

Changing the environment mid-execution doesn't always cause problems, but when problems do arise it's a headache trying to track them down.


steps = append(steps, run.NewRegistry("login", cfg))
steps = append(steps, run.NewChart("save", cfg))
steps = append(steps, run.NewChart("push", cfg))
steps = append(steps, run.NewRegistry("logout", cfg))
return steps
}
63 changes: 63 additions & 0 deletions internal/run/chart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package run

import (
"fmt"

"github.com/pelotech/drone-helm3/internal/env"
)

// Chart is an execution step that calls `helm chart` when executed.
type Chart struct {
*config
chartPath string
registryURL string
registryRepoName string
chartVersion string
subCommand string
cmd cmd
}

// NewChart creates a Chart using fields from the given Config. No validation is performed at this time.
func NewChart(subCommand string, cfg env.Config) *Chart {
return &Chart{
config: newConfig(cfg),
chartPath: cfg.Chart,
registryURL: cfg.RegistryURL,
registryRepoName: cfg.RegistryRepoName,
chartVersion: cfg.ChartVersion,
subCommand: subCommand,
}
}

// Execute executes the `helm chart` command.
func (reg *Chart) Execute() error {
return reg.cmd.Run()
Comment on lines +33 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: there's a bit of copy-paste left over here (and the same thing in Prepare)

Suggested change
func (reg *Chart) Execute() error {
return reg.cmd.Run()
func (chart *Chart) Execute() error {
return chart.cmd.Run()

}

// Prepare gets the Chart ready to execute.
func (reg *Chart) Prepare() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prepare()s should verify that their required params are present:

Suggested change
func (reg *Chart) Prepare() error {
func (reg *Chart) Prepare() error {
if reg.registryURL == "" {
return fmt.Errorf("registry_url is required")
}
if reg.registryRepoName == "" {
return fmt.Errorf("registry_repo_name is required")
}
if reg.chartVersion == "" {
return fmt.Errorf("chart_version is required")
}

A test for those cases would also be nice, though not completely necessary.

args := []string{}

args = append(args, "chart")

if reg.subCommand == "save" {
args = append(args, "save")
args = append(args, reg.chartPath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to check for an empty chartPath? I haven't checked how helm save "" [ref] behaves, but I wouldn't expect it to be the same as helm save [ref].

Suggested change
args = append(args, reg.chartPath)
if reg.chartPath != "" {
args = append(args, reg.chartPath)
}

cmd := fmt.Sprintf("%s/%s:%s", reg.registryURL, reg.registryRepoName, reg.chartVersion)
args = append(args, cmd)
} else if reg.subCommand == "push" {
args = append(args, "push")
cmd := fmt.Sprintf("%s/%s:%s", reg.registryURL, reg.registryRepoName, reg.chartVersion)
args = append(args, cmd)
Comment on lines +50 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines are the same as their counterparts in reg.subCommand == "save", right? If so they should go after the if clause, so they're less likely to accidentally diverge in the future.

}

reg.cmd = command(helmBin, args...)
reg.cmd.Stdout(reg.stdout)
reg.cmd.Stderr(reg.stderr)

if reg.debug {
fmt.Fprintf(reg.stderr, "Generated command: '%s'\n", reg.cmd.String())
}

return nil
}
Loading