diff --git a/.gitignore b/.gitignore index b4dde86..c1d3cd2 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ drone-helm *.test *.out .vscode +.idea \ No newline at end of file diff --git a/README.md b/README.md index 521ce7d..aae1a6f 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ pipeline: Last update of Drone expect you to declare the secrets you want to use: ```YAML +pipeline: helm_deploy: image: quay.io/ipedrazas/drone-helm chart: ./chart/blog @@ -43,6 +44,7 @@ Last update of Drone expect you to declare the secrets you want to use: ``` Use Kubernetes Certificate Authority Data. Just add the `_kubernetes_certificate` secret + ```diff helm_deploy: image: quay.io/ipedrazas/drone-helm @@ -61,7 +63,7 @@ Use Kubernetes Certificate Authority Data. Just add the `_kubernetes_cer Values can be passed using the `values_files` key. Use this option to define your values in a set of files and pass them to `helm`. This option trigger the `-f` or ``--values`` flag in `helm`: -``` +```plain --values valueFiles specify values in a YAML file (can specify multiple) (default []) ``` @@ -84,8 +86,10 @@ pipeline: Charts can also be fetched from your own private Chart Repository. `helm_repos` accepts a comma separated list of key value pairs where the key is the repository name and the value is the repository url. For Example: -``` -helm_deploy_staging: + +```YAML +pipeline: + helm_deploy_staging: image: quay.io/ipedrazas/drone-helm skip_tls_verify: true helm_repos: hb-charts=http://helm-charts.honestbee.com @@ -98,11 +102,37 @@ helm_deploy_staging: exclude: [ master ] ``` +## Updating Chart dependencies + +In some cases, the local Chart might contain external dependencies defined in `./charts/my-chart/requirements.yaml`, e.g.: + +```YAML +dependencies: + - name: redis + version: 3.3.6 + repository: '@stable' +``` + +To restore these dependecies before the deployment `update_dependencies` parameter should be used, e.g.: + +```YAML +pipeline: + helm_deploy: + image: quay.io/ipedrazas/drone-helm + skip_tls_verify: true + chart: ./charts/my-chart + update_dependencies: true + release: ${DRONE_BRANCH} + values_files: ["global-values.yaml", "myenv-values.yaml"] + when: + branch: [master] +``` + ## Drone Secrets There are two secrets you have to create (Note that if you specify the prefix, your secrets have to be created using that prefix): -```Bash +```bash drone secret add --image=quay.io/ipedrazas/drone-helm \ your-user/your-repo STAGING_API_SERVER https://mykubernetesapiserver @@ -114,6 +144,7 @@ drone secret add --image=quay.io/ipedrazas/drone-helm \ ``` `Prefix` helps you to use the same block in different environments: + ```YAML pipeline: helm_deploy_staging: @@ -145,7 +176,6 @@ pipeline_production: This last block defines how the plugin will deploy - ## Testing with Minikube To test the plugin, you can run `minikube` and just run the docker image as follows: @@ -169,7 +199,8 @@ KUBERNETES_TOKEN=$(kubectl get secret $(kubectl get sa default -o jsonpath='{.se ``` Run the local image (or replace `drone-helm` with `quay.io/ipedrazas/drone-helm`: -```Bash + +```bash docker run --rm \ -e API_SERVER="https://$(minikube ip):8443" \ -e KUBERNETES_TOKEN="${KUBERNETES_TOKEN}" \ @@ -189,6 +220,7 @@ docker run --rm \ This plugin installs [Tiller](https://github.com/kubernetes/helm/blob/master/docs/architecture.md) in the cluster, if you want to specify the namespace where `tiller` ins installed, use the `tiller_ns` attribute. The following example will install `tiller` in the `operations` namespace: + ```YAML pipeline_production: helm_deploy: @@ -218,6 +250,7 @@ pipeline_production: when: branch: [master] ``` + Happy Helming! ## Known issues diff --git a/main.go b/main.go index a3e5e1e..b4727ed 100644 --- a/main.go +++ b/main.go @@ -134,6 +134,11 @@ func main() { Usage: "purge on delete", EnvVar: "PLUGIN_PURGE,PURGE", }, + cli.BoolFlag{ + Name: "update-dependencies", + Usage: "update dependency charts based on the contents of requirements.yaml file of the local chart", + EnvVar: "PLUGIN_UPDATE_DEPENDENCIES,UPDATE_DEPENDENCIES", + }, } if err := app.Run(os.Args); err != nil { logrus.Fatal(err) @@ -146,33 +151,34 @@ 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"), + 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"), }, } return plugin.Exec() diff --git a/plugin.go b/plugin.go index 14ef1c0..b27c034 100644 --- a/plugin.go +++ b/plugin.go @@ -19,34 +19,35 @@ 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"` + 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"` } // Plugin default Plugin struct { @@ -206,6 +207,17 @@ func doHelmInit(p *Plugin) []string { } +func doDependencyUpdate(chart string) []string { + dependencyUpdate := []string{ + "dependency", + "update", + chart, + } + + return dependencyUpdate +} + + // Exec default method func (p *Plugin) Exec() error { if p.Config.Debug { @@ -254,6 +266,12 @@ func (p *Plugin) Exec() error { } } + if p.Config.UpdateDependencies { + if err = runCommand(doDependencyUpdate(p.Config.Chart)); err != nil { + return fmt.Errorf("Error updating dependencies: " + err.Error()) + } + } + setHelmCommand(p) if p.Config.Debug { diff --git a/plugin_test.go b/plugin_test.go index d450ee0..4a45cb2 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -292,6 +292,16 @@ func TestDetHelmRepoAdd(t *testing.T) { } } +func TestDependencyUpdate(t *testing.T) { + chart := "./chart" + expected := "dependency update ./chart" + result := strings.Join(doDependencyUpdate(chart), " ") + + if expected != result { + t.Errorf("Helm cannot update repositories - expected %q - got %q", expected, result) + } +} + func TestHelmAddRepositoryError(t *testing.T) { _, err := doHelmRepoAdd("drone-helm=bad://drone-helm.example.com:443/stable") if err == nil {