├── .gitignore ├── CHANGELOG.md ├── README.md ├── conduits.helmfile.yaml ├── custom-configs ├── .gitignore └── README.md ├── deployments.helmfile.yaml ├── docs └── diag1.png ├── examples ├── README.md ├── chartvalues │ ├── appconduits │ │ └── values │ │ │ └── testapps │ │ │ └── values.yaml │ └── appdeploy │ │ └── values │ │ ├── hogapp │ │ └── stage │ │ │ └── stage-qa │ │ │ └── values.yaml │ │ └── testapps │ │ └── values.yaml ├── environments │ ├── catapp │ │ ├── chartconfigs.yaml │ │ └── stage-qa.yaml │ ├── dogapp │ │ ├── chartconfigs.yaml │ │ └── stage-qa.yaml │ ├── hogapp │ │ ├── chartconfigs.yaml │ │ └── stage-qa.yaml │ └── index.yaml └── statevalues │ ├── customized-chartconfigs.yaml │ └── customized-cluster.yaml ├── fileexists.sh ├── helmDefaults.yaml └── statevalues ├── 000-globals.yaml └── 001-clusters.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | helmfile 2 | custom-configs/* 3 | !custom-configs/README.md 4 | .DS_Store 5 | x.txt 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.4.1 2 | * Upgraded to `appdeploy:1.4.7` (minor update) 3 | * Upgraded to `appconduits:1.1.4` (minor update) 4 | 5 | # 1.4.0 6 | * Upgraded to `appdeploy:1.4.6` 7 | * Upgraded to `appconduits:1.1.3` 8 | 9 | # 1.3.0 10 | * Upgraded to `appconduits:1.1.2` 11 | 12 | # 1.2.0 13 | * Upgraded to `appdeploy:1.4.3` 14 | 15 | # 1.1.0 16 | * Upgraded to `appdeploy:1.4.2` 17 | * Upgraded to `appconduits:1.1.1` 18 | 19 | # 1.0.20 20 | * Update `helmDefaults.yaml` for new `stable` chart location 21 | 22 | # 1.0.19 23 | 24 | * Support for `appdeploy:1.3.0` pod disruption budgets: https://github.com/bitsofinfo/helmfile-deploy/pull/2 25 | 26 | # 1.0.18 27 | 28 | * Support for `appdeploy:1.2.0` autoscaling: https://github.com/bitsofinfo/helmfile-deploy/pull/1 29 | 30 | # 1.0.17 31 | 32 | * Add support for toggling helmfile's `helmDefaults.createNamespace: true|false` per `statevalues/[cluster].yaml` failes. Relevant for helm 3.2+, you can force this and override cluster settings via `--state-values-set forceHelmCreateNamespace=` 33 | 34 | # 1.0.16 35 | 36 | * Upgraded to `appdeploy:1.1.16` 37 | 38 | # 1.0.15 39 | 40 | * Change `helmDefaults.yaml` to use `targetCluster` variables for `verify, force, recreatePods` 41 | 42 | # 1.0.14 43 | 44 | * Upgraded to `appdeploy:1.1.15` 45 | 46 | # 1.0.13 47 | 48 | * Upgraded to `appdeploy:1.1.14` 49 | 50 | # 1.0.12 51 | 52 | * If `hooks.custom.[n]` blocks are declared in `chartconfigs.yaml` under `chartConfigs.appdeploy.chartValues.values` no longer assume the existance of the `enabled` and or `variables` sub-keys. 53 | 54 | # 1.0.11 55 | 56 | * Upgraded to `appdeploy:1.1.13` 57 | * Added support for `releaseBaseName` in environment context files, which will be used in preference to `appname` if present when generating each `helmfile` `release.name` 58 | 59 | # 1.0.10 60 | 61 | * Upgraded to `appdeploy:1.1.12` 62 | 63 | # 1.0.9 64 | 65 | * Upgraded to `appdeploy:1.1.11` 66 | * Added option for per-invocation override for: `--state-values-set forceHelmTimeout=[N seconds]` 67 | 68 | # 1.0.8 69 | 70 | * `deployments.helmfile.yaml` fix `version` (`currentService.version`) to have invalid characters replaced w/ dashes `-` see: https://github.com/roboll/helmfile/issues/970 71 | 72 | # 1.0.7 73 | 74 | * `deployments.helmfile.yaml` added `version` (`currentService.version`) and `name` (`currentService.name`) as additional helmfile `release` labels:` 75 | 76 | # 1.0.6 77 | 78 | * Added new helmfile state value `forceHelmTillerNamespace` to permit per-invocation overrides of the default helm `tillerNamespace` as defined in `helmDefaults.yaml`. This can be set via `--state-values-set forceHelmTillerNamespace=`. This will result in `tillerNamespace: ` being defined for each generated release under `releases:` by `[deployments|conduits].helmfile.yaml` 79 | 80 | # 1.0.5 81 | 82 | * Fix `deployments|conduits.helmfile.yaml` to leverage `deepCopy` to properly permit `chartConfigs` overrides to not mutate global `chartConfigs` and only the current iteration. Requires `helmfile` `v0.86.0+` 83 | 84 | # 1.0.4 85 | 86 | * Fix `conduits.helmfile.yaml` to process annotations/labels for `appconduits:1.0.12` 87 | 88 | # 1.0.3 89 | 90 | * Upgraded to `appconduits:1.0.12` 91 | 92 | # 1.0.2 93 | 94 | * Upgraded to `appdeploy:1.1.10` 95 | 96 | # 1.0.1 97 | 98 | * Works with `appdeploy:1.1.9+` and `appconduits:1.0.11+` which introduced breaking changes 99 | 100 | # 1.0.0 101 | 102 | Initial version, works with `appdeploy:1.1.8` and `appconduits:1.0.10` 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # helmfile-deploy 2 | 3 | This project provides a framework of *helmfiles* using the amazing tool [helmfile](https://github.com/roboll/helmfile) for declaring intended Kubernetes state for apps deployed with the [appdeploy](https://github.com/bitsofinfo/appdeploy) Helm chart and accessed via Ingress "conduits" installed by the [appconduits](https://github.com/bitsofinfo/appconduits) Helm chart. 4 | 5 | *helmfile-deploy* is a opinionated *framework* (for lack of a better term) that provides a set of *helmfiles* that generate *releases* for the [appdeploy](https://github.com/bitsofinfo/appdeploy) and [appconduits](https://github.com/bitsofinfo/appconduits) charts. To generate those releases, *helmfile-deploy* defines its own YAML syntax for summarizing the desired state for applications it manages. This desired "state" is expressed as *helmfile* `environments:`, one per named target application. 6 | 7 | * [Overview](#overview) 8 | * [Examples](examples/) 9 | * [Install/Setup](#setup) 10 | * [Dry running & debugging](#dry) 11 | * [helmfile concepts](#helmfile-term) 12 | * [helmfile-deploy concepts](#helmfile-dep-term) 13 | * [What this provides](#provide) 14 | 15 | ## Overview 16 | 17 | What does `helmfile-deploy` do? How does it work? 18 | 19 | Basically `helmfile-deploy` provides a set of *helmfiles* that can generate releases for the [appdeploy](https://github.com/bitsofinfo/appdeploy) and [appconduits](https://github.com/bitsofinfo/appconduits) helm charts. Out of the box they provided helmfiles are a bit useless and in order to actually be able to do anything with the *helmfiles* this project provides, you have to provide configuration expressed in the form of helmfile `environments:` so that `helmfile` can apply those environment values against the helmfile templates. 20 | 21 | ![diag](/docs/diag1.png "Diagram1") 22 | 23 | Before you venture off to do any of this we, need to cover some basics and get on the same page w/ our terminology. 24 | 25 | ## Helmfile specific concepts 26 | 27 | First, some basic *helmfile* concepts you need to understand: 28 | 29 | (for full documentation on this see: https://github.com/roboll/helmfile) 30 | 31 | **helmfile**: 32 | A *helmfile* is a YAML file that declares one or more *releases* for Helm charts. You use the [helmfile](https://github.com/roboll/helmfile) command against a target *helmfile yaml file*. Helmfiles themselves can contain [golang template](https://golang.org/pkg/text/template/) markup as well as utilize many [extended functions provided by Sprig.](http://masterminds.github.io/sprig/) 33 | 34 | **release**: 35 | A Helmfile *release* is an expression in YAML for an installation/upgrade of a specific Helm chart and its desired `values`. With [helmfile](https://github.com/roboll/helmfile), the goal is to be able to define many *releases* and then ensure all or some of the defined *releases* are applied against a target Kubernetes cluster. 36 | 37 | **state values**: 38 | Helmfile *state values* are variables/values that are only relevant during the execution of `helmfile` while it is processing a target *helmfile yaml*. *State values* are distinctly different than Helm chart values. *State values* can be referenced in helmfiles in golang templates to dynamically generate helmfile *releases*. 39 | 40 | **environment values**: 41 | When you invoke the `helmfile` command against a target *helmfile yaml file*, you also specify a target `--environment [name]`. Doing so will instruct *helmfile* to load a set of *environment values* specific to that target `--environment`. These values (in combination with *state values*) can be used as variables to also drive the generation of *releases* in the target *helmfile yaml file* that the `helmfile` command will process. It is important to NOT confuse *helmfile environment values* with traditional OS ENVIRONMENT variables... they are NOT the same thing. It is also important to note that a target *environment* can mean *anything* and does NOT necessarily have to mean something like "stage" or "production", but could simply be a target *application* to generate a release for etc. 42 | 43 | ## helmfile-deploy concepts 44 | 45 | ... and some basic *helmfile-deploy* concepts to understand: 46 | 47 | **appdeploy and appconduits helm charts**: 48 | To even begin using *helmfile-deploy* you first need to be familiar with the concepts and functionality of the [appdeploy](https://github.com/bitsofinfo/appdeploy) and [appconduits](https://github.com/bitsofinfo/appconduits) Helm charts. If you have not already done so, now would be a good time to review them and run through their examples: 49 | * https://github.com/bitsofinfo/appdeploy 50 | * https://github.com/bitsofinfo/appconduits 51 | 52 | **environments**: 53 | When using *helmfile-deploy*, each helmfile *environment* that you target with `--environment [name]` is considered to be a named **"application"** you want to both deploy with [the appdeploy chart](https://github.com/bitsofinfo/appdeploy) and manage customized Ingress routes (conduits) for with [the appconduits chart](https://github.com/bitsofinfo/appconduits). With *helmfile-deploy* each *target application* IS expressed through a *helmfile environment*. (*helmfile environments* = *application desired state definitions*) [See examples](https://github.com/bitsofinfo/helmfile-deploy/tree/master/examples/environments) 54 | 55 | **applications**: 56 | With *helmfile-deploy* each *target application* IS expressed through a *helmfile environment*. To configure a new *application* that *helmfile-deploy* can manage, you define a new helmfile *environment* and express it's desired state in a custom YAML syntax that lets you define an `appname`, its app `environments`, `contexts` and most importantly `services` and `ingresses` within each app's environment/context. For definitions of app *environments/contexts* [see the appdeploy chart README](https://github.com/bitsofinfo/appdeploy) which describes these terms. [Click here for example environment definitions](https://github.com/bitsofinfo/helmfile-deploy/tree/master/examples/environments) 57 | 58 | **services**: 59 | Within each application's helmfile *environment* YAML files ([see examples](https://github.com/bitsofinfo/helmfile-deploy/tree/master/examples/environments)), the key element that you must declare per each `appname's` app `environment` and app `context` are `services`. Each `service` is a super simplified expression of what you really want to express; *"here are all versions of 'appname' that I want running on my target cluster"*. This data drives the [deployments.helmfile.yaml](deployments.helmfile.yaml) helmfile. 60 | 61 | **ingress**: 62 | Likewise, within each application's helmfile *environment* YAML files ([see examples](https://github.com/bitsofinfo/helmfile-deploy/tree/master/examples/environments)), the other key element that you can *optionally* declare per each `appname's` app `environment` and app `context` are `ingresses`. Each `ingress` contains one or more `mappings` and each is a simplified expression of what you really want to express; *"Here are some custom Host/Path definitions I want exposed as Ingress and here is the list of services to bind the traffic to"*. This data drives the [conduits.helmfile.yaml](conduits.helmfile.yaml) helmfile. 63 | 64 | **target cluster**: 65 | When you execute the `helmfile` command against a *helmfile-deploy provided helmfile*, you also need to specify a `targetCluster` via the `--state-values-set targetCluster=[clusterName]`. This tells `helmfile` which cluster to apply the desired releases against. You can define your own `clusters` in custom *helmfile state values files* see [here](https://github.com/bitsofinfo/helmfile-deploy/blob/master/statevalues/001-clusters.yaml) and [here](https://github.com/bitsofinfo/helmfile-deploy/blob/master/examples/statevalues/customized-cluster.yaml). [See documentation here](https://github.com/bitsofinfo/helmfile-deploy/blob/master/statevalues/001-clusters.yaml) 66 | 67 | **You may be asking... "where do all the other chart values come from??!"** 68 | 69 | Good question. Each application's helmfile *environment* can also declare its own `chartConfigs.appdeploy` and/or `chartConfigs.appconduits` sections. Here is where you can tailor `chartValues` for each app directly inline and even reference shared `baseValueSets` (that reference `availableBaseValueSets`) per chart for common configuration shared across many apps. `chartConfigs` blocks can be defined and/or overridden and supplemented at various levels all the way down to the `service` level. See examples [here](https://github.com/bitsofinfo/helmfile-deploy/blob/master/examples/environments/catapp/chartconfigs.yaml), [here](https://github.com/bitsofinfo/helmfile-deploy/blob/master/examples/statevalues/customized-chartconfigs.yaml), [here](https://github.com/bitsofinfo/helmfile-deploy/blob/master/examples/chartvalues/appdeploy/values/testapps/values.yaml) and [here](https://github.com/bitsofinfo/helmfile-deploy/blob/master/statevalues/000-globals.yaml). For more details on this see: https://github.com/bitsofinfo/helmfile-deploy/blob/master/statevalues/000-globals.yaml 70 | 71 | ## What does helmfile-deploy provide? 72 | 73 | **helmfiles**: 74 | Two [helmfile](https://github.com/roboll/helmfile) *helmfiles* are provided: 75 | * [deployments.helmfile.yaml](deployments.helmfile.yaml): Generates a unique helmfile **release** of the [appdeploy Helm chart](https://github.com/bitsofinfo/appdeploy) for each declared `service` within a target helmfile `environment` 76 | * [conduits.helmfile.yaml](conduits.helmfile.yaml): Generates a unique helmfile **release** of the [appconduits Helm chart](https://github.com/bitsofinfo/appdeploy) for each declared `service` within a target helmfile `environment` 77 | 78 | **core state values**: 79 | The project also provides some core helmfile state values files which define the core `chartConfigs` (which you can override at lower levels), and a basic `cluster` definition for minikube. [See here for more info](https://github.com/bitsofinfo/helmfile-deploy/tree/master/statevalues) 80 | 81 | **custom configs**: 82 | As noted above, in order to actually be able to do anything with this project you need to provide your own configuration. Since currently `helmfile` depends on relative path references, we've added a [custom-configs/](custom-configs/) directory where you can build out your custom configuration. Due to `.gitignore` changes will never be committed here, however you can clone your own configs within this sub-directory and manage independently. 83 | 84 | ## Install & Setup 85 | 86 | Note: before you try your own custom configuration its highly recommended that [you take a look at the examples/](examples/) 87 | 88 | Pre-requisites: 89 | 90 | 1. have the latest [helm](https://helm.sh/) installed 91 | 1. have the latest [helmfile](https://github.com/roboll/helmfile) installed **>= 0.80.2** 92 | 1. have the latest [helmfile-diff](https://github.com/databus23/helm-diff) plugin installed 93 | 94 | **Clone the helmfile-deploy project** 95 | 96 | You need to clone the `helmfile-deploy` project: 97 | ``` 98 | git clone --branch [TAG] https://github.com/bitsofinfo/helmfile-deploy.git 99 | ``` 100 | 101 | **Create your own configs** 102 | 103 | Next lets create a place for your own configuration (custom *state values*, *base chart values* and *environments*): 104 | 105 | *NOTE: change `myconfig` to whatever name you want, in fact any of the directories here can be named anything you want.* 106 | 107 | ``` 108 | cd helmfile-deploy/custom-configs 109 | 110 | mkdir -p myconfigs/environments 111 | mkdir -p myconfigs/statevalues 112 | ``` 113 | 114 | These are optional, but handy if you will be taking advantage of the `chartConfigs.[chartname].chartValues.baseValueSets` functionality. See [statevalues/000-globals.yaml](statevalues/000-globals.yaml) for more info. 115 | ``` 116 | mkdir -p myconfigs/chartvalues/appdeploy 117 | mkdir -p myconfigs/chartvalues/appconduits 118 | ``` 119 | 120 | *Note you can OPTIONALLY manage your own configs in a separate git project* 121 | ``` 122 | cd helmfile-deploy/custom-configs/myconfigs 123 | git init 124 | ... 125 | ``` 126 | 127 | **Configure helmfile-deploy ENVIRONMENT variables** 128 | 129 | These variables tell the *helmfile-deploy* templates where to find your custom configuration. Note this is relative from the ROOT of the helm-deploy project. 130 | ``` 131 | export HELMFILE_DEPLOY_STATE_VALUES_DIR=custom-configs/myconfigs/statevalues 132 | export HELMFILE_DEPLOY_ENVIRONMENTS_DIR=custom-configs/myconfigs/environments 133 | ``` 134 | 135 | **Optionally configure your targetCluster** 136 | 137 | Out of the box, [helmfile-deploy](https://github.com/bitsofinfo/helmfile-deploy) declares one named *cluster* that it can target [named minikube](https://github.com/bitsofinfo/helmfile-deploy/blob/master/statevalues/001-clusters.yaml) which you can reference for local development. 138 | 139 | Additional *clusters* can be defined by declaring them in YAML state values under the configured `HELMFILE_DEPLOY_STATE_VALUES_DIR`. 140 | 141 | When you invoke `helmfile` the argument that specifies the cluster you want to apply the declared state to is: 142 | ``` 143 | --state-values-set targetCluster=[clustername] 144 | ``` 145 | 146 | **Invoke helmfile against one of your custom app environments** 147 | 148 | We need to be running the `helmfile` commands from the root of the helmfile-deploy project 149 | ``` 150 | cd path/to/helmfile-deploy/ 151 | ``` 152 | 153 | Apply deployments: 154 | ``` 155 | helmfile \ 156 | --file deployments.helmfile.yaml \ 157 | --state-values-set targetCluster=minikube \ 158 | --namespace [YOUR NAMESPACE] \ 159 | --environment [TARGET APP ENVIRONMENT] \ 160 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=custom-configs/myconfigs/chartvalues/appdeploy \ 161 | apply 162 | ``` 163 | 164 | Apply conduits: 165 | ``` 166 | helmfile \ 167 | --file conduits.helmfile.yaml \ 168 | --state-values-set targetCluster=minikube \ 169 | --namespace [YOUR NAMESPACE] \ 170 | --environment [TARGET APP ENVIRONMENT] \ 171 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=custom-configs/myconfigs/chartvalues/appdeploy \ 172 | apply 173 | ``` 174 | 175 | ## Dry running & debugging 176 | 177 | *helmfile-deploy* can generate a lot of YAML. It can often be useful to both get more information on what its constructing as well as simply output the raw YAML to STDOUT/disk and save it and NOT send it to Kubernetes. 178 | 179 | **template mode**: 180 | The `helmfile` command supports the `template` argument. Simply changing `apply | sync etc` to `template` will output all generated YAML to stdout. Due to the nature of helm charts and generating some extra whitespace, it can also be useful to use something like [python-yq](https://formulae.brew.sh/formula/python-yq) to reformat the the YAML 181 | 182 | ``` 183 | helmfile \ 184 | --file deployments.helmfile.yaml \ 185 | --state-values-set targetCluster=minikube \ 186 | --namespace [YOUR NAMESPACE] \ 187 | --environment [TARGET APP ENVIRONMENT] \ 188 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=custom-configs/myconfigs/chartvalues/appdeploy \ 189 | --quiet \ 190 | template | yq --yaml-output . 191 | ``` 192 | 193 | **enable debugging**: 194 | Simply adding the `--log-level debug` flag to helmfile can also aid in figuring out whats going on: 195 | -------------------------------------------------------------------------------- /conduits.helmfile.yaml: -------------------------------------------------------------------------------- 1 | values: 2 | - statevalues/000-globals.yaml 3 | - statevalues/001-clusters.yaml 4 | - {{ requiredEnv "HELMFILE_DEPLOY_STATE_VALUES_DIR" }}/* 5 | --- 6 | bases: 7 | - {{ requiredEnv "HELMFILE_DEPLOY_ENVIRONMENTS_DIR" }}/index.yaml 8 | --- 9 | bases: 10 | - helmDefaults.yaml 11 | --- 12 | 13 | 14 | releases: 15 | {{- /* 16 | --------------------------------------------------------- 17 | Basic values declarations 18 | --------------------------------------------------------- 19 | */ -}} 20 | {{- $envVals := .Environment.Values -}} 21 | {{- $classifier := (hasKey $envVals "classifier") | ternary ($envVals | getOrNil "classifier") "" -}} 22 | {{- $ports := (hasKey $envVals "ports") | ternary ($envVals | getOrNil "ports") (list (dict "port" 80 "tls" false)) }} 23 | {{- $namespace := .Namespace -}} 24 | {{- $stateVals := .Values }} 25 | 26 | {{- /* 27 | The target "cluster" 28 | Note: simpler syntax should just be: https://github.com/Masterminds/sprig/issues/151 29 | */ -}} 30 | {{- $targetCluster := (pick $stateVals.clusters $stateVals.targetCluster) | values | first }} 31 | 32 | 33 | {{- /* 34 | --------------------------------------------------------- 35 | For each [helmfile-environment].environments.[environment] loaded/found 36 | in yaml files under environments/[helmfile-environment]/* we loop over 37 | each environment's context one by one and generate a "appconduits" 38 | helmfile "release" for it. 39 | --------------------------------------------------------- 40 | */ -}} 41 | {{- range $currEnvironmentName,$currEnvironment := .Environment.Values.environments }} 42 | {{- range $currContextName,$currContext := $currEnvironment.contexts }} 43 | 44 | 45 | {{- /* 46 | Used by `tpl` variable parsing below. The caller can specify tpl parsed values i.e. 47 | "{{ .helmfile.[path] }}" If ".helmfile" is NOT present the "{{bracketed}}" will 48 | be left alone and assumed to be consumable by the chart in the release 49 | */ -}} 50 | {{- $helmfileVarsDict := dict "helmfile" (dict "clusters" $stateVals.clusters "targetCluster" $targetCluster "envVals" $envVals "stateVals" $stateVals "namespace" $namespace "context" $currContext "environment" $currEnvironment) }} 51 | 52 | 53 | {{- /* 54 | --------------------------------------------------------- 55 | "chartConfigs" 56 | --------------------------------------------------------- 57 | Here we grab the "chartConfigs" from the following possible locations in order of priority: 58 | 59 | 1. The current "[helmfile-environment].environments.[environment].contexts.[context]" we are iterating over 60 | i.e. this would be declared in environments/[helmfile-environment]/[any-env-file.yaml] 61 | 62 | 2. The current "[helmfile-environment].environments.[environment]" we are iterating over 63 | i.e. this would be declared in environments/[helmfile-environment]/[any-env-file.yaml] 64 | 65 | 3. The top level helmfile-environment's "chartConfigs" 66 | i.e. this would be declared in environments/[helmfile-environment]/chartconfigs.yaml 67 | 68 | 4. The targetCluster's "chartConfigs" (clusters.[targetCluster].chartConfigs) 69 | i.e. this would be declared in: statevalues/001-clusters.yaml or any other HELMFILE_DEPLOY_STATE_VALUES_DIR yaml 70 | 71 | 5. The global state values "chartConfigs" 72 | i.e. this would be declared in: statevalues/000-globals.yaml or any other HELMFILE_DEPLOY_STATE_VALUES_DIR yaml 73 | 74 | 6. Note with the sprig "merge" function: see oddities: https://github.com/Masterminds/sprig/issues/120 75 | 76 | */ -}} 77 | {{ $chartConfigs := dict }} 78 | {{ $chartConfigs := hasKey $stateVals "chartConfigs" | ternary (deepCopy ((hasKey $stateVals "chartConfigs") | ternary ($stateVals | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 79 | {{ $chartConfigs := hasKey $targetCluster "chartConfigs" | ternary (deepCopy ((hasKey $targetCluster "chartConfigs") | ternary ($targetCluster | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 80 | {{ $chartConfigs := hasKey $envVals "chartConfigs" | ternary (deepCopy ((hasKey $envVals "chartConfigs") | ternary ($envVals | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 81 | {{ $chartConfigs := hasKey $currEnvironment "chartConfigs" | ternary (deepCopy ((hasKey $currEnvironment "chartConfigs") | ternary ($currEnvironment | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 82 | {{ $chartConfigs := hasKey $currContext "chartConfigs" | ternary (deepCopy ((hasKey $currContext "chartConfigs") | ternary ($currContext | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 83 | 84 | 85 | {{- /* 86 | --------------------------------------------------------- 87 | Generate a "conduitName" for each release 88 | 89 | [envVals.appname]-[currContextName][-[classifier]] 90 | --------------------------------------------------------- 91 | */ -}} 92 | {{- $conduitName := printf "%s-%s-%sREMOVEME-conduits" (hasKey $envVals "releaseBaseName" | ternary ($envVals | getOrNil "releaseBaseName") $envVals.appname) $currContextName $classifier }} 93 | 94 | {{- /* 95 | this is annoying, due to lack of reliable ternary operators and nested IF garbage 96 | in golang conditionals and variable scope, we have no scratch like key-resolver... 97 | helm uses http://masterminds.github.io/sprig under the covers nicely. 98 | */ -}} 99 | {{- $conduitName := $conduitName | replace "-REMOVEME" "" }} 100 | {{- $conduitName := $conduitName | replace "REMOVEME" "" }} 101 | {{- $conduitName := $conduitName | replace "-%!s()" "" }} 102 | 103 | 104 | 105 | {{- /* 106 | --------------------------------------------------------- 107 | Helmfile RELEASE generation: 108 | 109 | Generate an actual helmfile release of the "appconduits" chart 110 | for the currentEnvironment.currentContext 111 | with its desired state expressed in helm values 112 | --------------------------------------------------------- 113 | */ -}} 114 | - name: {{ $conduitName }} 115 | labels: 116 | app: {{ $envVals.appname }} 117 | context: {{ $currContextName }} 118 | namespace: {{ $namespace }} 119 | chart: {{ $chartConfigs.appconduits.chart }} 120 | {{ if hasKey $chartConfigs.appconduits "version" }} 121 | version: {{ $chartConfigs.appconduits.version }} 122 | {{ end }} 123 | 124 | {{- /* Permit forced override of the default helm tillerNamespace (defined in helmDefaults.yaml) 125 | (set this via helmfile --state-values-set forceHelmTillerNamespace=[namespace]) */ -}} 126 | {{ if hasKey $stateVals "forceHelmTillerNamespace" }} 127 | tillerNamespace: {{ $stateVals.forceHelmTillerNamespace }} 128 | {{ end }} 129 | 130 | {{- /* Permit forced override of the default helm timeout (defined in helmDefaults.yaml) 131 | (set this via helmfile --state-values-set forceHelmTimeout=[N seconds]) */ -}} 132 | {{ if hasKey $stateVals "forceHelmTimeout" }} 133 | timeout: {{ $stateVals.forceHelmTimeout }} 134 | {{ end }} 135 | 136 | {{- /* Permit forced override of the default helm 3.2+ create-namespace behavior (defined in helmDefaults.yaml) 137 | (set this via helmfile --state-values-set forceHelmCreateNamespace=[true|false]) */ -}} 138 | {{ if hasKey $stateVals "forceHelmCreateNamespace" }} 139 | createNamespace: {{ $stateVals.forceHelmCreateNamespace }} 140 | {{ end }} 141 | 142 | values: 143 | 144 | {{- /* 145 | --------------------------------------------------------- 146 | BASE values file references 147 | @see statevalues/000-globals.yaml for more info 148 | note: "exec" hack can go away when 149 | https://github.com/roboll/helmfile/issues/766 is addressed 150 | --------------------------------------------------------- 151 | */ -}} 152 | {{ $valueBaseParseVars := dict "appname" $envVals.appname "classifier" ($envVals | getOrNil "classifier") "environmentName" $currEnvironmentName "contextName" $currContextName }} 153 | 154 | {{- /* Get list of all named "sets" we actually want to use */ -}} 155 | {{ range $baseValueSetName := $chartConfigs.appconduits.chartValues.baseValueSets }} 156 | 157 | {{- /* Load the actual referenced set by name from 'availableBaseValueSets' */ -}} 158 | {{ $baseValues := (pick $chartConfigs.appconduits.chartValues.availableBaseValueSets $baseValueSetName) | values | first }} 159 | 160 | {{- /* For each one, lets add it to the list of overall values files we will reference below */ -}} 161 | {{ range $valueBase := $baseValues }} 162 | {{ $valueFile := printf "%s/values/%s/values.yaml" $chartConfigs.appconduits.chartValues.baseValuesRootDir (tpl $valueBase $valueBaseParseVars) }} 163 | {{ if eq (exec "./fileexists.sh" (list $valueFile)) "true" }} 164 | - {{ $valueFile }} 165 | {{ end }} 166 | {{ end }} 167 | {{ end }} 168 | 169 | 170 | {{/* 171 | --------------------------------------------------------- 172 | Inline custom values 173 | 174 | These values, combined with the "bases" above yield 175 | the overall set of "values" customizations passed to helm 176 | --------------------------------------------------------- 177 | */ -}} 178 | - conduitname: "{{ $envVals.appname }}" 179 | classifier: "{{ $classifier }}" 180 | 181 | {{ if hasKey $chartConfigs.appconduits.chartValues "values" }} 182 | 183 | 184 | {{ if hasKey $chartConfigs.appconduits.chartValues.values "ingress" }} 185 | ingress: 186 | {{ if hasKey $chartConfigs.appconduits.chartValues.values.ingress "dns" }} 187 | dns: 188 | {{ toYaml $chartConfigs.appconduits.chartValues.values.ingress.dns | indent 0 }} 189 | {{ end }} 190 | {{ if hasKey $chartConfigs.appconduits.chartValues.values.ingress "tls" }} 191 | tls: 192 | {{ toYaml $chartConfigs.appconduits.chartValues.values.ingress.tls | indent 0 }} 193 | {{ end }} 194 | {{ if hasKey $chartConfigs.appconduits.chartValues.values.ingress "metadata" }} 195 | metadata: 196 | {{ if hasKey $chartConfigs.appconduits.chartValues.values.ingress.metadata "labels" }} 197 | labels: 198 | {{ range $labelName,$labelValue := $chartConfigs.appconduits.chartValues.values.ingress.metadata.labels }} 199 | "{{$labelName}}": "{{ contains ".helmfile" $labelValue | ternary (tpl $labelValue $helmfileVarsDict) $labelValue }}" 200 | {{ end }} 201 | {{ end }} 202 | {{ if hasKey $chartConfigs.appconduits.chartValues.values.ingress.metadata "annotations" }} 203 | annotations: 204 | {{ range $annotationName,$annotationValue := $chartConfigs.appconduits.chartValues.values.ingress.metadata.annotations }} 205 | "{{$annotationName}}": "{{ contains ".helmfile" $annotationValue | ternary (tpl $annotationValue $helmfileVarsDict) $annotationValue }}" 206 | {{ end }} 207 | {{ end }} 208 | {{ end }} 209 | {{ end }} 210 | 211 | 212 | {{ end }} 213 | 214 | 215 | conduits: 216 | contexts: 217 | {{ $currContextName }}: 218 | services: 219 | {{- range $service := $currContext.services }} 220 | {{- range $port := $ports }} 221 | {{- $serviceName := printf "REMOVEME%s-%s-%d" $classifier $service.name $port.port }} 222 | {{- $serviceName := $serviceName | replace "REMOVEME-" "" }} 223 | {{- $serviceName := $serviceName | replace "REMOVEME" "" }} 224 | {{- $serviceName := $serviceName | replace "%!s()-" "" }} 225 | {{ $serviceName }}: 226 | port: {{ $port.port }} 227 | targetPort: {{ $port.port }} 228 | tls: {{ $port.tls }} 229 | selector: 230 | "{{$stateVals.releaseConfigs.conduits.selectorlabelNames.appName}}": "{{ $envVals.appname }}" 231 | "{{$stateVals.releaseConfigs.conduits.selectorlabelNames.appVersion}}": "{{ $service.version }}" 232 | {{ if $classifier }} 233 | "{{$stateVals.releaseConfigs.conduits.selectorlabelNames.appClassifier}}": {{ $classifier }} 234 | {{ end }} 235 | {{ end }} 236 | {{ end }} 237 | 238 | ingress: 239 | 240 | {{/* 241 | For each "targetedPort" we generate a unique "appconduits" 242 | "ingressname-[classifier]-[targetedport]" that contains 243 | the appconduits ingress mappings array 244 | 245 | By default the targetedPorts is the FIRST known port 246 | that is listed in the top level "$ports" list 247 | 248 | */ -}} 249 | {{- $targetedPorts := dict "ports" (list (first $ports)) -}} 250 | 251 | {{ range $ingressName, $ingress := $currContext.ingress }} 252 | 253 | {{/* 254 | Does the current ingress stanza declare its own "targetedPorts"? 255 | If so we will utilize that... 256 | */ -}} 257 | {{ if hasKey $ingress "targetedPorts" }} 258 | {{ $_ := set $targetedPorts "ports" list }} 259 | {{ range $p := $ports }} 260 | {{ if has $p.port $ingress.targetedPorts }} 261 | {{ $_ := set $targetedPorts "ports" (append $targetedPorts.ports $p) }} 262 | {{ end }} 263 | {{ end }} 264 | {{ end }} 265 | 266 | {{/* 267 | Now for each port we will generate a unique appconduits "ingress" declaration 268 | */ -}} 269 | {{- range $port := $targetedPorts.ports }} 270 | {{ $ingressName := printf "%s-%sREMOVEME-%d" $ingressName $classifier $port.port }} 271 | {{- $ingressName := $ingressName | replace "-REMOVEME" "" }} 272 | {{- $ingressName := $ingressName | replace "REMOVEME" "" }} 273 | {{- $ingressName := $ingressName | replace "-%!s()" "" }} 274 | 275 | {{ $ingressName }}: 276 | mappings: 277 | {{ range $mapping := $ingress.mappings }} 278 | - name: {{ $mapping.name }} 279 | {{ if hasKey $mapping "annotations" }} 280 | annotations: 281 | {{ range $annotation,$val := $mapping.annotations }} 282 | {{ $annotation }}: {{ $val }} 283 | {{ end }} 284 | {{ end }} 285 | labels: 286 | {{ range $label, $value := $mapping.labels }} 287 | {{ $label }}: "{{ $value }}" 288 | {{ end }} 289 | hosts: 290 | {{ range $host := $mapping.hosts }} 291 | {{- $hostName := $host.name | replace "[[#port]]" ($port.port | toString) -}} 292 | {{- $hostName := $hostName | replace "-." "." -}} 293 | - name: "{{ $hostName }}" 294 | {{ if hasKey $host "annotations" }} 295 | annotations: 296 | {{ range $annotation,$val := $host.annotations }} 297 | {{ $annotation }}: {{ $val }} 298 | {{ end }} 299 | {{ end }} 300 | {{ if hasKey $host "labels" }} 301 | labels: 302 | {{ range $label,$val := $host.labels }} 303 | {{ $label }}: {{ $val }} 304 | {{ end }} 305 | {{ end }} 306 | dns: {{ (eq ((getOrNil "dns" $host) | toString) "false") | ternary false true }} 307 | {{ if hasKey $host "tls" }} 308 | tls: 309 | enabled: {{ (eq ((getOrNil "enabled" $host.tls) | toString) "true") | ternary true false }} 310 | {{ if hasKey $host.tls "secretName" }} 311 | secretName: {{ getOrNil "secretName" $host.tls }} 312 | {{ end }} 313 | {{ end }} 314 | {{ end }} 315 | {{ if hasKey $mapping "paths" }} 316 | paths: 317 | {{ range $path := $mapping.paths }} 318 | - "{{ $path }}" 319 | {{ end }} 320 | {{ end }} 321 | {{ if hasKey $mapping "pathType" }} 322 | pathType: {{ $mapping.pathType }} 323 | {{ end }} 324 | serviceBindings: 325 | {{ range $serviceName,$percentage := $mapping.serviceBindings }} 326 | {{- $serviceName := printf "REMOVEME%s-%s-%d" $classifier $serviceName $port.port }} 327 | {{- $serviceName := $serviceName | replace "REMOVEME-" "" }} 328 | {{- $serviceName := $serviceName | replace "REMOVEME" "" }} 329 | {{- $serviceName := $serviceName | replace "%!s()-" "" }} 330 | {{ $serviceName }}: "{{ $percentage }}" 331 | {{ end }} 332 | {{ end }} 333 | {{ end }} 334 | {{ end }} 335 | 336 | {{ end }} 337 | {{ end }} 338 | -------------------------------------------------------------------------------- /custom-configs/.gitignore: -------------------------------------------------------------------------------- 1 | # In this folder you place your custom configs 2 | # we never commit anything other than README to helmfile-deploy 3 | 4 | * 5 | 6 | # we retain the readme... 7 | !README.md 8 | -------------------------------------------------------------------------------- /custom-configs/README.md: -------------------------------------------------------------------------------- 1 | # custom-configs 2 | 3 | Within this directory you can place custom directories for your `environments` and `statevalues` yamls. 4 | 5 | You don't have to place them here, but this directory is a convenient place to drop them as it is relative from the root of the helmfile-deploy project and nothing in here will ever be committed. 6 | 7 | ## How to make use of this: 8 | 9 | You will need to declare some ENVIRONMENT variables pointing to the custom configuration locations within this directory for `helmfile-deploy`. 10 | 11 | Note that currently these locations referred to by the following variables must be *RELATIVE* from the root of the `helmfile-deploy` project (see [743](https://github.com/roboll/helmfile/issues/743)) 12 | 13 | ### HELMFILE_DEPLOY_STATE_VALUES_DIR 14 | This variable designates where your custom `helmfile` *state values* can be found. Within this directory you can have one or more custom `*.yaml` that override and customize known `helmfile-deploy` state values defined in [statevalues/](../statevalues) Its important to note that these values are not `helm` chart values, but rather values that are consumed by the helmfile release templates themselves ([deployments.helmfile.yaml](../deployments.helmfile.yaml) & [conduits.helmfile.yaml](../conduits.helmfile.yaml)) 15 | ``` 16 | export HELMFILE_DEPLOY_STATE_VALUES_DIR=custom-configs/[pathOfYourStateValuesDir] 17 | ``` 18 | 19 | ### HELMFILE_DEPLOY_ENVIRONMENTS_DIR 20 | This variable designates where your custom `helmfile` *environments* can be found. This directory should declare a single `index.yaml` that declares your helmfile `environments:` block. For the purposes of the `helmfile-deploy` framework, each helmfile *environment* represents a target application that can be deployed. 21 | ``` 22 | export HELMFILE_DEPLOY_ENVIRONMENTS_DIR=custom-configs/[pathOfYourEnvironmentsDir] 23 | ``` 24 | -------------------------------------------------------------------------------- /deployments.helmfile.yaml: -------------------------------------------------------------------------------- 1 | values: 2 | - statevalues/000-globals.yaml 3 | - statevalues/001-clusters.yaml 4 | - {{ requiredEnv "HELMFILE_DEPLOY_STATE_VALUES_DIR" }}/* 5 | --- 6 | bases: 7 | - {{ requiredEnv "HELMFILE_DEPLOY_ENVIRONMENTS_DIR" }}/index.yaml 8 | --- 9 | bases: 10 | - helmDefaults.yaml 11 | --- 12 | 13 | 14 | releases: 15 | {{- /* 16 | --------------------------------------------------------- 17 | Basic values declarations 18 | --------------------------------------------------------- 19 | */ -}} 20 | {{- $stateVals := .Values }} 21 | {{- $envVals := .Environment.Values -}} 22 | {{- $namespace := .Namespace -}} 23 | 24 | {{- /* 25 | The target "cluster" 26 | Note: simpler syntax should just be: https://github.com/Masterminds/sprig/issues/151 27 | */ -}} 28 | {{- $targetCluster := (pick $stateVals.clusters $stateVals.targetCluster) | values | first }} 29 | 30 | 31 | {{- /* 32 | --------------------------------------------------------- 33 | These two values are fetched from the helmfile "state values" 34 | and are typically set via invocation: 35 | "--state-values-set bootstrapSecret=x,creatorId=y" 36 | --------------------------------------------------------- 37 | */ -}} 38 | {{ $bootstrapSecretValue := $stateVals | getOrNil "bootstrapSecret" }} 39 | {{ $creatorId := $stateVals | getOrNil "creatorId" }} 40 | 41 | {{- /* 42 | --------------------------------------------------------- 43 | For each [helmfile-environment].environments.[environment] loaded/found 44 | in yaml files under environments/[helmfile-environment]/* we loop over 45 | each environment's context(s) declared "services", one by one 46 | and generate a "appdeploy" helmfile "release" 47 | 48 | NOTE! since the environment's context yaml file can potentially 49 | list the same service.version across multiple service elements 50 | we keep track of this as to not create duplicate releases. 51 | 52 | Why can a "version" be declared across multiple "services"? 53 | Because "services" are just named things we can route traffic 54 | to and the conduits.helmfile.yaml template also processes the same 55 | file. In this case the "version" just nets diff selectors 56 | on conduit services. In any case, it is a legit case and permitted 57 | --------------------------------------------------------- 58 | */ -}} 59 | 60 | {{- range $currEnvironmentName,$currEnvironment := $envVals.environments }} 61 | {{- range $currContextName,$currContext := $currEnvironment.contexts }} 62 | {{ $versionsReleased := list }} 63 | {{- range $currService := $currContext.services }} 64 | 65 | {{- /* version check */ -}} 66 | {{ if not (has $currService.version $versionsReleased) }} 67 | 68 | {{ $versionsReleased = append $versionsReleased $currService.version }} 69 | 70 | {{- /* 71 | Used by `tpl` variable parsing below. The caller can specify tpl parsed values i.e. 72 | "{{ .helmfile.[path] }}" If ".helmfile" is NOT present the "{{bracketed}}" will 73 | be left alone and assumed to be consumable by the chart in the release 74 | */ -}} 75 | {{- $helmfileVarsDict := dict "helmfile" (dict "clusters" $stateVals.clusters "targetCluster" $targetCluster "envVals" $envVals "stateVals" $stateVals "namespace" $namespace "service" $currService "context" $currContext "environment" $currEnvironment) }} 76 | 77 | {{- /* 78 | --------------------------------------------------------- 79 | "chartConfigs" 80 | --------------------------------------------------------- 81 | Here we grab the "chartConfigs" from the following possible locations in order of priority: 82 | 83 | 1. The current "[helmfile-environment].environments.[environment].contexts.[context].services.[service]" we are iterating over 84 | i.e. this would be declared in environments/[helmfile-environment]/[any-env-file.yaml] 85 | 86 | 2. The current "[helmfile-environment].environments.[environment].contexts.[context]" we are iterating over 87 | i.e. this would be declared in environments/[helmfile-environment]/[any-env-file.yaml] 88 | 89 | 3. The current "[helmfile-environment].environments.[environment]" we are iterating over 90 | i.e. this would be declared in environments/[helmfile-environment]/[any-env-file.yaml] 91 | 92 | 4. The top level helmfile-environment's "chartConfigs" 93 | i.e. this would be declared in environments/[helmfile-environment]/chartconfigs.yaml 94 | 95 | 5. The targetCluster's "chartConfigs" (clusters.[targetCluster].chartConfigs) 96 | i.e. this would be declared in: statevalues/001-clusters.yaml or any other HELMFILE_DEPLOY_STATE_VALUES_DIR yaml 97 | 98 | 6. The global state values "chartConfigs" 99 | i.e. this would be declared in: statevalues/000-globals.yaml or any other HELMFILE_DEPLOY_STATE_VALUES_DIR yaml 100 | 101 | 7. Note with the sprig "merge" function: see oddities: https://github.com/Masterminds/sprig/issues/120 102 | 103 | */ -}} 104 | {{ $chartConfigs := dict }} 105 | {{ $chartConfigs := hasKey $stateVals "chartConfigs" | ternary (deepCopy ((hasKey $stateVals "chartConfigs") | ternary ($stateVals | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 106 | {{ $chartConfigs := hasKey $targetCluster "chartConfigs" | ternary (deepCopy ((hasKey $targetCluster "chartConfigs") | ternary ($targetCluster | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 107 | {{ $chartConfigs := hasKey $envVals "chartConfigs" | ternary (deepCopy ((hasKey $envVals "chartConfigs") | ternary ($envVals | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 108 | {{ $chartConfigs := hasKey $currEnvironment "chartConfigs" | ternary (deepCopy ((hasKey $currEnvironment "chartConfigs") | ternary ($currEnvironment | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 109 | {{ $chartConfigs := hasKey $currContext "chartConfigs" | ternary (deepCopy ((hasKey $currContext "chartConfigs") | ternary ($currContext | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 110 | {{ $chartConfigs := hasKey $currService "chartConfigs" | ternary (deepCopy ((hasKey $currService "chartConfigs") | ternary ($currService | getOrNil "chartConfigs") dict) | mergeOverwrite $chartConfigs) $chartConfigs }} 111 | 112 | {{- /* 113 | --------------------------------------------------------- 114 | Generate a "releaseIdentifier" 115 | 116 | [envVals.appname]-[environment.context]-[currEnvironment.service.version]-[envVals.classifier] 117 | --------------------------------------------------------- 118 | */ -}} 119 | {{- $releaseIdentifier := printf "%s-%s-%s-%s" (hasKey $envVals "releaseBaseName" | ternary ($envVals | getOrNil "releaseBaseName") $envVals.appname) $currContextName ($currService.version | replace "." "-") (hasKey $envVals "classifier" | ternary ($envVals | getOrNil "classifier") "NOCLASSIFIER") }} 120 | {{- $releaseIdentifier := $releaseIdentifier | replace "-NOCLASSIFIER" "" -}} 121 | {{- $releaseIdentifier := $releaseIdentifier | replace "NOCLASSIFIER" "" -}} 122 | {{- $releaseIdentifier := $releaseIdentifier | replace "-%!s()" "" }} 123 | 124 | 125 | {{- /* 126 | --------------------------------------------------------- 127 | Build BASE values file references -> $valuesFiles 128 | 129 | The "chartConfigs.appdeploy" chart configuration can declare one or more named 130 | "availableBaseValueSets" which define sets of supplemental "values.yaml" files. 131 | Here we inspect the "baseValuesSets" list of referenced "set" names to determine 132 | the actual list of "values.yaml" files to apply to the chart which are ultimately 133 | located under the configured "chartConfigs.appdeploy.chartValues.baseValuesRootDir" path 134 | 135 | @see statevalues/000-globals.yaml for more info 136 | --------------------------------------------------------- 137 | */ -}} 138 | {{ $valuesFiles := dict "files" list }} 139 | {{ $valueBaseParseVars := dict "appname" $envVals.appname "classifier" ($envVals | getOrNil "classifier") "environmentName" $currEnvironmentName "contextName" $currContextName "service" $currService }} 140 | 141 | {{- /* Get list of all named "sets" we actually want to use */ -}} 142 | {{ range $baseValueSetName := $chartConfigs.appdeploy.chartValues.baseValueSets }} 143 | 144 | {{- /* Load the actual referenced set by name from 'availableBaseValueSets' */ -}} 145 | {{ $baseValues := (pick $chartConfigs.appdeploy.chartValues.availableBaseValueSets $baseValueSetName) | values | first }} 146 | 147 | {{- /* For each one, lets add it to the list of overall values files we will reference below */ -}} 148 | {{ range $valueBase := $baseValues }} 149 | {{ $parsedValueBase := tpl $valueBase $valueBaseParseVars }} 150 | {{ $_ := append $valuesFiles.files (printf "%s/values/%s/values.yaml" $chartConfigs.appdeploy.chartValues.baseValuesRootDir $parsedValueBase) | set $valuesFiles "files" }} 151 | {{ end }} 152 | {{ end }} 153 | 154 | {{- /* 155 | Each "service" can declare a "hooks" object, each top-level "key" of it 156 | designates the name of the hook and it's values folder within the 157 | "chartConfigs.appdeploy.chartValues.baseValuesRootDir" location 158 | for supplemental values particular to that hook. 159 | */ -}} 160 | {{ if hasKey $currService "hooks"}} 161 | {{ range $currHookName,$currHook := $currService.hooks }} 162 | {{ $_ := append $valuesFiles.files (printf "%s/values/hooks/%s/values.yaml" $chartConfigs.appdeploy.chartValues.baseValuesRootDir $currHookName) | set $valuesFiles "files" }} 163 | {{ end }} 164 | {{ end }} 165 | 166 | 167 | {{- /* 168 | --------------------------------------------------------- 169 | Helmfile RELEASE generation: 170 | 171 | Generate an actual helmfile release of the "appdeploy" chart 172 | for the currentEnvironment.currentContext.currService 173 | with its desired state expressed in helm values 174 | --------------------------------------------------------- 175 | */ -}} 176 | - name: {{ $releaseIdentifier }} 177 | labels: 178 | app: {{ $envVals.appname }} 179 | context: {{ $currContextName }} 180 | version: {{ ($currService.version | replace "." "-") }} 181 | name: {{ $currService.name }} 182 | namespace: {{ $namespace }} 183 | chart: {{ $chartConfigs.appdeploy.chart }} 184 | {{ if hasKey $chartConfigs.appdeploy "version" }} 185 | version: {{ $chartConfigs.appdeploy.version }} 186 | {{ end }} 187 | 188 | {{ if $currService.installed }} 189 | installed: true 190 | {{ else }} 191 | installed: false 192 | {{ end }} 193 | 194 | {{- /* Permit forced override of the default helm tillerNamespace (defined in helmDefaults.yaml) 195 | (set this via helmfile --state-values-set forceHelmTillerNamespace=[namespace]) */ -}} 196 | {{ if hasKey $stateVals "forceHelmTillerNamespace" }} 197 | tillerNamespace: {{ $stateVals.forceHelmTillerNamespace }} 198 | {{ end }} 199 | 200 | {{- /* Permit forced override of the default helm timeout (defined in helmDefaults.yaml) 201 | (set this via helmfile --state-values-set forceHelmTimeout=[N seconds]) */ -}} 202 | {{ if hasKey $stateVals "forceHelmTimeout" }} 203 | timeout: {{ $stateVals.forceHelmTimeout }} 204 | {{ end }} 205 | 206 | {{- /* Permit forced override of the default helm 3.2+ create-namespace behavior (defined in helmDefaults.yaml) 207 | (set this via helmfile --state-values-set forceHelmCreateNamespace=[true|false]) */ -}} 208 | {{ if hasKey $stateVals "forceHelmCreateNamespace" }} 209 | createNamespace: {{ $stateVals.forceHelmCreateNamespace }} 210 | {{ end }} 211 | 212 | values: 213 | {{- /* 214 | --------------------------------------------------------- 215 | RENDER: BASE values file references 216 | note: "exec" hack can go away when 217 | https://github.com/roboll/helmfile/issues/766 is addressed 218 | --------------------------------------------------------- 219 | */ -}} 220 | {{- range $valueFile := $valuesFiles.files }} 221 | {{ if eq (exec "./fileexists.sh" (list $valueFile)) "true" }} 222 | - {{$valueFile}} 223 | {{ end }} 224 | {{ end }} 225 | 226 | {{/* 227 | --------------------------------------------------------- 228 | Inline custom values 229 | 230 | These values, combined with the "bases" above yield 231 | the overall set of "values" customizations passed to helm 232 | --------------------------------------------------------- 233 | */ -}} 234 | - app: 235 | name: {{ $envVals.appname }} 236 | context: {{ $currContextName }} 237 | environment: {{ $currEnvironmentName }} 238 | {{ if $envVals | getOrNil "classifier" }} 239 | classifier: {{ $envVals.classifier }} 240 | {{ end }} 241 | 242 | creatorId: {{ $creatorId }} 243 | replicaCount: {{ $chartConfigs.appdeploy.chartValues.values.replicaCount }} 244 | 245 | image: 246 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values "image" }} 247 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values.image "repository" }} 248 | repository: {{ $chartConfigs.appdeploy.chartValues.values.image.repository }} 249 | {{ end }} 250 | {{ end }} 251 | tag: "{{ $currService.version }}" 252 | 253 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values "containerPorts" }} 254 | containerPorts: 255 | {{ toYaml $chartConfigs.appdeploy.chartValues.values.containerPorts | indent 12 }} 256 | {{ end }} 257 | 258 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values "ingress" }} 259 | ingress: 260 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values.ingress "dns" }} 261 | dns: 262 | {{ toYaml $chartConfigs.appdeploy.chartValues.values.ingress.dns | indent 0 }} 263 | {{ end }} 264 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values.ingress "tls" }} 265 | tls: 266 | {{ toYaml $chartConfigs.appdeploy.chartValues.values.ingress.tls | indent 0 }} 267 | {{ end }} 268 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values.ingress "metadata" }} 269 | metadata: 270 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values.ingress.metadata "labels" }} 271 | labels: 272 | {{ range $labelName,$labelValue := $chartConfigs.appdeploy.chartValues.values.ingress.metadata.labels }} 273 | "{{$labelName}}": "{{ contains ".helmfile" $labelValue | ternary (tpl $labelValue $helmfileVarsDict) $labelValue }}" 274 | {{ end }} 275 | {{ end }} 276 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values.ingress.metadata "annotations" }} 277 | annotations: 278 | {{ range $annotationName,$annotationValue := $chartConfigs.appdeploy.chartValues.values.ingress.metadata.annotations }} 279 | "{{$annotationName}}": "{{ contains ".helmfile" $annotationValue | ternary (tpl $annotationValue $helmfileVarsDict) $annotationValue }}" 280 | {{ end }} 281 | {{ end }} 282 | {{ end }} 283 | {{ end }} 284 | 285 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values "healthcheck" }} 286 | healthcheck: 287 | {{ toYaml $chartConfigs.appdeploy.chartValues.values.healthcheck | indent 12 }} 288 | {{ end }} 289 | 290 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values "resources" }} 291 | resources: 292 | {{ toYaml $chartConfigs.appdeploy.chartValues.values.resources | indent 12 }} 293 | {{ end }} 294 | 295 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values "autoscaling" }} 296 | autoscaling: 297 | {{ toYaml $chartConfigs.appdeploy.chartValues.values.autoscaling | indent 12 }} 298 | {{ end }} 299 | 300 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values "disruptionBudget" }} 301 | disruptionBudget: 302 | {{ toYaml $chartConfigs.appdeploy.chartValues.values.disruptionBudget | indent 12 }} 303 | {{ end }} 304 | 305 | bootstrapSecret: 306 | k8Secret: 307 | secretValue: "{{ $bootstrapSecretValue }}" 308 | 309 | {{- if $chartConfigs.appdeploy.chartValues.values | getOrNil "env" }} 310 | env: 311 | {{- range $key,$val := $chartConfigs.appdeploy.chartValues.values.env }} 312 | {{- if and (hasKey $val "value") $val.value }} 313 | {{ $key }}: 314 | {{ if contains ".helmfile" $val.value }} 315 | value: {{ tpl $val.value $helmfileVarsDict }} 316 | {{ else }} 317 | value: {{ $val.value }} 318 | {{ end }} 319 | {{ else }} 320 | {{ $key }}: 321 | value: "" 322 | {{ end }} 323 | {{ end }} 324 | {{ end }} 325 | 326 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values "hooks" }} 327 | hooks: 328 | 329 | {{- /* 330 | DEFAULT hooks (hooks.default.[n]) 331 | 332 | Dump out all non-custom appdeploy "hooks.default" defined in "chartConfigs" blocks 333 | */ -}} 334 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values.hooks "default" }} 335 | {{ toYaml $chartConfigs.appdeploy.chartValues.values.hooks.default | indent 12 }} 336 | {{ end }} 337 | 338 | {{- /* 339 | CUSTOM Hooks: (hooks.custom.[n]) 340 | 341 | Here we manually rewrite it all as w support variable 342 | tpl parsing. This is not entirely complete as it does 343 | not cover re-rendering all possible vars as the use 344 | case at this point is pretty limited whereby the majority 345 | of 'custom' entries only configure the options below 346 | while the majority of other configuration for them is 347 | sourced from baseValues valuesFiles above. Below we 348 | only support enabled, variables and the bootstrapSecret 349 | */ -}} 350 | 351 | {{ if hasKey $chartConfigs.appdeploy.chartValues.values.hooks "custom" }} 352 | custom: 353 | {{ range $currHookName,$currHook := $chartConfigs.appdeploy.chartValues.values.hooks.custom }} 354 | 355 | {{ $currHookName }}: 356 | 357 | {{ if not (empty $currHook) }} 358 | {{ if hasKey $currHook "values" }} 359 | 360 | {{ if hasKey $currHook.values "enabled" }} 361 | enabled: {{ $currHook.values.enabled }} 362 | {{ end }} 363 | 364 | {{ if hasKey $currHook.values "variables" }} 365 | variables: 366 | {{ range $name,$value := $currHook.values.variables }} 367 | {{ if contains ".helmfile" $value }} 368 | {{ $name }}: {{ tpl $value $helmfileVarsDict }} 369 | {{ else }} 370 | {{ $name }}: {{ $value }} 371 | {{ end }} 372 | {{ end }} 373 | {{ end }} 374 | 375 | {{ end }} {{- /* END: if not (empty $currHook) */ -}} 376 | {{ end }} {{- /* END: if hasKey $currHook "values" */ -}} 377 | 378 | {{- /* 379 | --------------------------------------------------------- 380 | Each custom hook's optional "bootstrapSecret" value 381 | can be specified on helmfile invocation like: 382 | "--state-values-set hooks.custom.[hookname].bootstrapSecret=x" 383 | Note that "x" will be passed for 'tpl' parsing so it can 384 | reference another variable in the $helmfileVarsDict 385 | --------------------------------------------------------- 386 | */}} 387 | bootstrapSecret: 388 | k8Secret: 389 | {{- if ($stateVals | getOrNil "hooks.custom") }} 390 | {{- $stateValsCurrHook := $stateVals.hooks.custom | getOrNil $currHookName }} 391 | {{ if $stateValsCurrHook }} 392 | {{ if contains ".helmfile" $stateValsCurrHook.bootstrapSecret }} 393 | secretValue: {{ tpl $stateValsCurrHook.bootstrapSecret $helmfileVarsDict }} 394 | {{ else }} 395 | secretValue: {{ $stateValsCurrHook.bootstrapSecret }} 396 | {{ end }} 397 | {{ else }} 398 | secretValue: "NO_SECRET_VALUE1" 399 | {{ end }} 400 | {{ else }} 401 | secretValue: "NO_SECRET_VALUE2" 402 | {{ end }} 403 | 404 | {{ end }} {{- /* END: range $currHookName,$currHook := ..... */ -}} 405 | {{ end }} {{- /* END: if hasKey $chartConfigs.appdeploy.chartValues.values.hooks "custom"... */ -}} 406 | {{ end }} {{- /* END: if hasKey $chartConfigs.appdeploy.chartValues.values "hooks" */ -}} 407 | 408 | {{ end }} {{- /* END: range $currService := $currContext.services */ -}} 409 | {{ end }} {{- /* END: if not has $currService.version $versionsReleased */ -}} 410 | {{ end }} {{- /* END: range $currContextName,$currContext := $currEnvironment.contexts */ -}} 411 | {{ end }} {{- /* END: range $currEnvironmentName,$currEnvironment := $envVals.environments */ -}} 412 | -------------------------------------------------------------------------------- /docs/diag1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitsofinfo/helmfile-deploy/3c251b9f827df6b05cccb961b897fbd37844e60f/docs/diag1.png -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | Below is a basic example. Slack install/upgrade alerts will be sent to the https://bitsofinfo.slack.com `#bitsofinfo-dev` channel ([self signup to channel](https://join.slack.com/t/bitsofinfo/shared_invite/enQtNzE1OTM1MDY5MDYwLTk4MTc3MjA4Y2YwNjFkYjRlYjZjZWMyNWExY2QxN2JmMmMyOGViMzYzYmE5NjcyOGE5ZWFjYTM5MmVjNzUxMjc)) 4 | 5 | This should be enough to get your feet wet. 6 | 7 | **Note! For all commands below, you should be running them from the ROOT of the helmfile-deploy project!** 8 | 9 | ## Setup 10 | 11 | It assumes you will run against [Minikube](https://kubernetes.io/docs/setup/learning-environment/minikube/) locally. **If you want to point it to another cluster (optional!)**: 12 | * Edit the [customized-cluster.yaml](statevalues/customized-cluster.yaml) (or add your own yaml file under `examples/statevalues/`) 13 | * Add a new `cluster` element for your own named cluster. 14 | ``` 15 | clusters: 16 | ... 17 | : 18 | name: "" 19 | helmDefaults: 20 | tillerNamespace: kube-system 21 | kubeContext: 22 | wait: true 23 | ingressControllers: 24 | default: 25 | url: "[URL FOR YOUR INGRESS CONTROLLER]" 26 | ``` 27 | * For all commands below alter the argument `--state-values-set targetCluster=` 28 | 29 | ### Helm and Helmfile 30 | 31 | You **MUST** have `helm` installed. 32 | 33 | You **MUST** have `helmfile` installed: https://github.com/roboll/helmfile and be using a version **>= 0.80.2**. If the latest version is not available via `brew` (or wherever you want to get it), you can get architecture specific; pre-compiled version binaries here: https://github.com/roboll/helmfile/releases 34 | 35 | You **MUST** have the [helmfile-diff](https://github.com/databus23/helm-diff) helm plugin installed 36 | 37 | ### yq 38 | 39 | You should install `yq` https://github.com/kislyuk/yq (`brew install python-yq`) *(needed for examples only)* 40 | 41 | ### Ingress Controller 42 | 43 | Before we continue, you need to setup an `IngressController` [lets use Traefik, click here for setup instructions](https://github.com/bitsofinfo/appdeploy/blob/master/examples/TRAEFIK_SETUP.md) 44 | 45 | ### /etc/hosts 46 | 47 | For this example: your `/etc/hosts` (unless you have DNS setup) [should have an entry with the contents of this: hosts.txt](https://github.com/bitsofinfo/appconduits/blob/master/examples/hosts.txt) (you need to customize w/ your LB IP..) 48 | 49 | ### Helm repositories 50 | Finally, ensure the following Helm repos exists on your machine: 51 | ``` 52 | helm repo add bitsofinfo-appdeploy https://raw.githubusercontent.com/bitsofinfo/appdeploy/master/repo 53 | helm repo add bitsofinfo-appconduits https://raw.githubusercontent.com/bitsofinfo/appconduits/master/repo 54 | helm repo update 55 | ``` 56 | 57 | ## First steps 58 | 59 | We need to declare some ENVIRONMENT variables defining configuration locations for `helmfile-deploy`. Note that currently these locations must be *RELATIVE* from the root of the `helmfile-deploy` project (see [743](https://github.com/roboll/helmfile/issues/743)) 60 | 61 | ### HELMFILE_DEPLOY_STATE_VALUES_DIR 62 | This variable designates where your custom `helmfile` *state values* can be found. Within this directory you can have one or more custom `*.yaml` that override and customize known `helmfile-deploy` state values defined in [statevalues/](../statevalues) Its important to note that these values are not `helm` chart values, but rather values that are consumed by the helmfile release templates themselves ([deployments.helmfile.yaml](../deployments.helmfile.yaml) & [conduits.helmfile.yaml](../conduits.helmfile.yaml)) 63 | ``` 64 | export HELMFILE_DEPLOY_STATE_VALUES_DIR=examples/statevalues 65 | ``` 66 | 67 | ### HELMFILE_DEPLOY_ENVIRONMENTS_DIR 68 | This variable designates where your custom `helmfile` *environments* can be found. This directory should declare a single `index.yaml` that declares your helmfile `environments:` block. For the purposes of the `helmfile-deploy` framework, each helmfile *environment* represents a target application that can be deployed. 69 | ``` 70 | export HELMFILE_DEPLOY_ENVIRONMENTS_DIR=examples/environments 71 | ``` 72 | 73 | ## Lets deploy all defined "releases" of "catapp" 74 | 75 | Running this will ensure all defined `services` within the `catapp` *environment* (defined in [environments/catapp/stage-qa.yaml](environments/catapp/stage-qa.yaml)) under the *context* `stage-qa` are running against the `targetCluster=minikube` 76 | 77 | A unique helmfile `release` is defined for each of the latter using the [deployments.helmfile.yaml](../deployments.helmfile.yaml) helmfile YAML which subsequently invokes `helm` for each generated *release* using the [appdeploy](https://github.com/bitsofinfo/appdeploy) helm chart. 78 | 79 | (to see debug output add `--log-level debug`) 80 | ``` 81 | ./helmfile \ 82 | --file deployments.helmfile.yaml \ 83 | --state-values-set targetCluster=minikube \ 84 | --namespace bitsofinfo-apps \ 85 | --selector context=stage-qa \ 86 | --environment catapp \ 87 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=examples/chartvalues/appdeploy \ 88 | apply 89 | ``` 90 | 91 | Once this is complete we can access each installed app via its auto generated version specific name as generated by the [appdeploy](https://github.com/bitsofinfo/appdeploy) chart that `helmfile` invoked: 92 | ``` 93 | curl http://catapp-stage-qa-2-0-0-80.local 94 | curl http://catapp-stage-qa-3-0-0-80.local 95 | curl http://catapp-stage-qa-4-0-0-80.local 96 | ``` 97 | 98 | Lets verify that the custom `env` var `INGRESS_CONTROLLER_URL` declared under [environments/catapp/chartconfigs.yaml](environments/catapp/chartconfigs.yaml) actually got replaced w/ the helmfile state value based on the `targetCluster` (minikube) specified above in the `--state-values-set targetCluster=minikube` argument 99 | 100 | ``` 101 | kubectl describe deployment catapp-stage-qa-2-0-0 -n bitsofinfo-apps | grep INGRESS 102 | ``` 103 | 104 | You should see `INGRESS_CONTROLLER_URL: https://bitsofinfo-traefik.test.local` 105 | 106 | ### So... how can I just see what k8s YAML will be generated? 107 | 108 | If you just want to see the raw YAML that will be produced and sent to Kubernetes you can use the helmfile `template` argument as follows. (Here we pipe it to `python-yq` to get it cleaned up/formatted better) 109 | 110 | ``` 111 | ./helmfile \ 112 | --file deployments.helmfile.yaml \ 113 | --state-values-set targetCluster=minikube \ 114 | --namespace bitsofinfo-apps \ 115 | --selector context=stage-qa \ 116 | --environment catapp \ 117 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=examples/chartvalues/appdeploy \ 118 | --quiet \ 119 | template | yq --yaml-output . 120 | ``` 121 | 122 | 123 | ### and... how can I see everything generated, including the helmfile releases? 124 | 125 | If you just want to see all the raw helmfile `releases:` AND all k8s YAML that will be produced you again can use the helmfile `template` argument as follows with a few modifications for log level etc. 126 | 127 | ``` 128 | ./helmfile \ 129 | --log-level debug \ 130 | --file deployments.helmfile.yaml \ 131 | --state-values-set targetCluster=minikube \ 132 | --namespace bitsofinfo-apps \ 133 | --selector context=stage-qa \ 134 | --environment catapp \ 135 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=examples/chartvalues/appdeploy \ 136 | template 137 | ``` 138 | 139 | Great. Let's move on. 140 | 141 | ## Lets deploy all defined "conduits" for "catapp" 142 | 143 | `helmfile-deploy` also provides a helmfile template ([conduits.helmfile.yaml](../conduits.helmfile.yaml)) that generates helmfile *releases* for the [appconduits](https://github.com/bitsofinfo/appconduits) helm chart 144 | 145 | Running this will ensure all defined `ingress` within the `catapp` *environment* (defined in [environments/catapp/stage-qa.yaml](environments/catapp/stage-qa.yaml)) under the *context* `stage-qa` are running against the `targetCluster=minikube` 146 | 147 | A unique helmfile `release` is defined for each of the latter using the [conduits.helmfile.yaml](../conduits.helmfile.yaml) helmfile YAML which subsequently invokes `helm` for each generated *release* using the [appconduits](https://github.com/bitsofinfo/appconduits) helm chart. 148 | 149 | (to see debug output add `--log-level debug`) 150 | ``` 151 | ./helmfile \ 152 | --file conduits.helmfile.yaml \ 153 | --state-values-set targetCluster=minikube \ 154 | --namespace bitsofinfo-apps \ 155 | --selector context=stage-qa \ 156 | --environment catapp \ 157 | --state-values-set chartConfigs.appconduits.chartValues.baseValuesRootDir=examples/chartvalues/appconduits \ 158 | apply 159 | ``` 160 | 161 | Once this is complete we can access "catapp" via its custom `Ingress/Services` name/ingress as generated by the [appconduits](https://github.com/bitsofinfo/appconduits) chart that `helmfile` invoked: 162 | 163 | |Expect|target| 164 | |---|---| 165 | |catapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://catapp-mixed.mydomain.com`| 166 | |catapp:3.0.0|`curl http://catapp-current.mydomain.com`| 167 | |catapp:2.0.0|`curl http://catapp-previous.mydomain.com`| 168 | |catapp:4.0.0|`curl http://catapp-next.mydomain.com`| 169 | 170 | 171 | |Expect|target| 172 | |---|---| 173 | |RED catapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://animals.mydomain.com/red/`| 174 | |ORANGE catapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://animals.mydomain.com/orange/`| 175 | |RED catapp:4.0.0|`curl http://animals-next.mydomain.com/red/`| 176 | |ORANGE catapp:4.0.0|`curl http://animals-next.mydomain.com/orange/`| 177 | |RED catapp:3.0.0|`curl http://animals-current.mydomain.com/red/`| 178 | |ORANGE catapp:3.0.0|`curl http://animals-current.mydomain.com/orange/`| 179 | |RED catapp:2.0.0|`curl http://animals-previous.mydomain.com/red/`| 180 | |ORANGE catapp:2.0.0|`curl http://animals-previous.mydomain.com/orange/`| 181 | 182 | ## Next lets do the same for hogapp 183 | 184 | Note that **hogapp** exposes multiple ports and it's [environments/hogapp/chartconfigs.yaml](environments/hogapp/chartconfigs.yaml) 185 | reflects that by overriding the default `containerPorts` it would otherwise end up using as defined in [chartvalues/appdeploy/values/testapps/values.yaml](chartvalues/appdeploy/values/testapps/values.yaml) 186 | 187 | Ensure all *hogapp* deployment releases: (to see debug output add `--log-level debug`) 188 | ``` 189 | ./helmfile \ 190 | --file deployments.helmfile.yaml \ 191 | --state-values-set targetCluster=minikube \ 192 | --namespace bitsofinfo-apps \ 193 | --selector context=stage-qa \ 194 | --environment hogapp \ 195 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=examples/chartvalues/appdeploy \ 196 | apply 197 | ``` 198 | 199 | Note that `hogapp` has some special `appdeploy` chart overrides defined under [examples/chartvalues/appdeploy/values/hogapp](chartvalues/appdeploy/values/hogapp) if you inspect you will eventually see [stage/stage-qa/values.yaml](chartvalues/appdeploy/values/hogapp/stage/stage-qa/values.yaml) which declares an additional special `env:` variable `SOME_VAR_SPECIFIC_TO_STAGE_QA` 200 | 201 | The custom `values` here automatically get picked up because of our `availableBaseValueSets` and `baseValueSets` definitions in [examples/statevalues/customized-chartconfigs.yaml](statevalues/customized-chartconfigs.yaml) 202 | 203 | Let's verify its applied to the Deployment. 204 | 205 | ``` 206 | kubectl describe deployment hogapp-stage-qa-2-0-0 -n bitsofinfo-apps | grep SPECIFIC 207 | ``` 208 | 209 | You should see `SOME_VAR_SPECIFIC_TO_STAGE_QA: whatever` 210 | 211 | 212 | ``` 213 | curl http://hogapp-stage-qa-2-0-0-80.local 214 | curl http://hogapp-stage-qa-3-0-0-80.local 215 | curl http://hogapp-stage-qa-4-0-0-80.local 216 | curl http://hogapp-stage-qa-2-0-0-443.local 217 | curl http://hogapp-stage-qa-3-0-0-443.local 218 | curl http://hogapp-stage-qa-4-0-0-443.local 219 | ``` 220 | 221 | Ensure all *hogapp* conduit releases: (to see debug output add `--log-level debug`) 222 | ``` 223 | ./helmfile \ 224 | --file conduits.helmfile.yaml \ 225 | --state-values-set targetCluster=minikube \ 226 | --namespace bitsofinfo-apps \ 227 | --selector context=stage-qa \ 228 | --environment hogapp \ 229 | --state-values-set chartConfigs.appconduits.chartValues.baseValuesRootDir=examples/chartvalues/appconduits \ 230 | apply 231 | ``` 232 | 233 | **Note** that [hogapp's stage-qa config](environments/hogapp/stage-qa.yaml) leverages unique edge-case example that lets us generate dynamic conduits with dynamic names/fqdns inclusive of the declared unique backend `ports`. You can take a look at [environments/hogapp/stage-qa.yaml](environments/hogapp/stage-qa.yaml) for more information. 234 | 235 | In any case after applying the above, you can expect the following to work 236 | 237 | |Expect|target|explicit backend-port| 238 | |---|---|---| 239 | |hogapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://hogapp-mixed-443.mydomain.com`|443| 240 | |hogapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://hogapp-mixed-80.mydomain.com`|80| 241 | |hogapp:3.0.0|`curl http://hogapp-current-443.mydomain.com`|443| 242 | |hogapp:3.0.0|`curl http://hogapp-current-80.mydomain.com`|80| 243 | |hogapp:2.0.0|`curl http://hogapp-previous-443.mydomain.com`|443| 244 | |hogapp:2.0.0|`curl http://hogapp-previous-80.mydomain.com`|80| 245 | |hogapp:4.0.0|`curl http://hogapp-next-443.mydomain.com`|443| 246 | 247 | 248 | |Expect|target| 249 | |---|---| 250 | |BROWN hogapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://animals.mydomain.com/brown/`| 251 | |PINK hogapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://animals.mydomain.com/pink/`| 252 | |BROWN hogapp:4.0.0|`curl http://animals-next.mydomain.com/brown/`| 253 | |PINK hogapp:4.0.0|`curl http://animals-next.mydomain.com/pink/`| 254 | |BROWN hogapp:3.0.0|`curl http://animals-current.mydomain.com/brown/`| 255 | |PINK hogapp:3.0.0|`curl http://animals-current.mydomain.com/pink/`| 256 | |BROWN hogapp:2.0.0|`curl http://animals-previous.mydomain.com/brown/`| 257 | |PINK hogapp:2.0.0|`curl http://animals-previous.mydomain.com/pink/`| 258 | 259 | 260 | 261 | ## Next lets do the same for dogapp 262 | 263 | Ensure all *dogapp* deployment releases: (to see debug output add `--log-level debug`) 264 | ``` 265 | ./helmfile \ 266 | --file deployments.helmfile.yaml \ 267 | --state-values-set targetCluster=minikube \ 268 | --namespace bitsofinfo-apps \ 269 | --selector context=stage-qa \ 270 | --environment dogapp \ 271 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=examples/chartvalues/appdeploy \ 272 | apply 273 | ``` 274 | 275 | ``` 276 | curl http://dogapp-stage-qa-2-0-0-80.local 277 | curl http://dogapp-stage-qa-3-0-0-80.local 278 | curl http://dogapp-stage-qa-4-0-0-80.local 279 | ``` 280 | 281 | Ensure all *dogapp* conduit releases: (to see debug output add `--log-level debug`) 282 | ``` 283 | ./helmfile \ 284 | --file conduits.helmfile.yaml \ 285 | --state-values-set targetCluster=minikube \ 286 | --namespace bitsofinfo-apps \ 287 | --selector context=stage-qa \ 288 | --environment dogapp \ 289 | --state-values-set chartConfigs.appconduits.chartValues.baseValuesRootDir=examples/chartvalues/appconduits \ 290 | apply 291 | ``` 292 | 293 | |Expect|target| 294 | |---|---| 295 | |dogapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://dogapp-mixed.mydomain.com`| 296 | |dogapp:3.0.0|`curl http://dogapp-current.mydomain.com`| 297 | |dogapp:2.0.0|`curl http://dogapp-previous.mydomain.com`| 298 | |dogapp:4.0.0|`curl http://dogapp-next.mydomain.com`| 299 | 300 | |Expect|target| 301 | |---|---| 302 | |dogapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://animals.mydomain.com`| 303 | |dogapp:4.0.0|`curl http://animals-next.mydomain.com`| 304 | |dogapp:3.0.0|`curl http://animals-current.mydomain.com`| 305 | |dogapp:2.0.0|`curl http://animals-previous.mydomain.com`| 306 | |BLUE dogapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://animals.mydomain.com/blue/`| 307 | |GREEN dogapp:4.0.0 or 3.0.0 or 2.0.0|`curl http://animals.mydomain.com/green/`| 308 | |BLUE dogapp:4.0.0|`curl http://animals-next.mydomain.com/blue/`| 309 | |GREEN dogapp:4.0.0|`curl http://animals-next.mydomain.com/green/`| 310 | |BLUE dogapp:3.0.0|`curl http://animals-current.mydomain.com/blue/`| 311 | |GREEN dogapp:3.0.0|`curl http://animals-current.mydomain.com/green/`| 312 | |BLUE dogapp:2.0.0|`curl http://animals-previous.mydomain.com/blue/`| 313 | |GREEN dogapp:2.0.0|`curl http://animals-previous.mydomain.com/green/`| 314 | 315 | 316 | ## Cleanup 317 | 318 | Cleanup `catapp` releases: 319 | ``` 320 | ./helmfile \ 321 | --file deployments.helmfile.yaml \ 322 | --state-values-set targetCluster=minikube \ 323 | --namespace bitsofinfo-apps \ 324 | --selector context=stage-qa \ 325 | --environment catapp \ 326 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=examples/chartvalues/appdeploy \ 327 | destroy 328 | 329 | ./helmfile \ 330 | --file conduits.helmfile.yaml \ 331 | --state-values-set targetCluster=minikube \ 332 | --namespace bitsofinfo-apps \ 333 | --selector context=stage-qa \ 334 | --environment catapp \ 335 | --state-values-set chartConfigs.appconduits.chartValues.baseValuesRootDir=examples/chartvalues/appconduits \ 336 | destroy 337 | ``` 338 | 339 | Cleanup `dogapp` releases: 340 | ``` 341 | ./helmfile \ 342 | --file deployments.helmfile.yaml \ 343 | --state-values-set targetCluster=minikube \ 344 | --namespace bitsofinfo-apps \ 345 | --selector context=stage-qa \ 346 | --environment dogapp \ 347 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=examples/chartvalues/appdeploy \ 348 | destroy 349 | 350 | ./helmfile \ 351 | --file conduits.helmfile.yaml \ 352 | --state-values-set targetCluster=minikube \ 353 | --namespace bitsofinfo-apps \ 354 | --selector context=stage-qa \ 355 | --environment dogapp \ 356 | --state-values-set chartConfigs.appconduits.chartValues.baseValuesRootDir=examples/chartvalues/appconduits \ 357 | destroy 358 | ``` 359 | 360 | Cleanup `hogapp` releases: 361 | ``` 362 | ./helmfile \ 363 | --file deployments.helmfile.yaml \ 364 | --state-values-set targetCluster=minikube \ 365 | --namespace bitsofinfo-apps \ 366 | --selector context=stage-qa \ 367 | --environment hogapp \ 368 | --state-values-set chartConfigs.appdeploy.chartValues.baseValuesRootDir=examples/chartvalues/appdeploy \ 369 | destroy 370 | 371 | ./helmfile \ 372 | --file conduits.helmfile.yaml \ 373 | --state-values-set targetCluster=minikube \ 374 | --namespace bitsofinfo-apps \ 375 | --selector context=stage-qa \ 376 | --environment hogapp \ 377 | --state-values-set chartConfigs.appconduits.chartValues.baseValuesRootDir=examples/chartvalues/appconduits \ 378 | destroy 379 | ``` 380 | -------------------------------------------------------------------------------- /examples/chartvalues/appconduits/values/testapps/values.yaml: -------------------------------------------------------------------------------- 1 | # Customized values for appconduits chart 2 | # https://github.com/bitsofinfo/appconduits 3 | # Values here can be overriden or changed 4 | # at any level below (i.e. in each environment) 5 | 6 | hooks: 7 | postInstallUpgrade: 8 | # INTENTIONALLY PUBLIC FOR THIS EXAMPLE! 9 | webhook_url: https://hooks.slack.com/services/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 10 | -------------------------------------------------------------------------------- /examples/chartvalues/appdeploy/values/hogapp/stage/stage-qa/values.yaml: -------------------------------------------------------------------------------- 1 | # Customized values for appdeploy chart 2 | # https://github.com/bitsofinfo/appdeploy 3 | # Values here can be overriden or changed 4 | # at any level below (i.e. in each environment) 5 | 6 | env: 7 | SOME_VAR_SPECIFIC_TO_STAGE_QA: 8 | value: "whatever" 9 | -------------------------------------------------------------------------------- /examples/chartvalues/appdeploy/values/testapps/values.yaml: -------------------------------------------------------------------------------- 1 | # Customized values for appdeploy chart 2 | # https://github.com/bitsofinfo/appdeploy 3 | # Values here can be overriden or changed 4 | # at any level below (i.e. in each environment) 5 | 6 | app: 7 | environment: "stage" 8 | context: "stage-qa" 9 | 10 | containerPorts: 11 | - name: nginx80 12 | port: 80 13 | service: true 14 | ingress: true 15 | tls: false 16 | 17 | healthcheck: 18 | liveness: 19 | containerPort: 80 20 | path: / 21 | scheme: HTTP 22 | readiness: 23 | containerPort: 80 24 | path: / 25 | scheme: HTTP 26 | 27 | hooks: 28 | default: 29 | postInstallUpgrade: 30 | validator: 31 | useIngressHost: false 32 | checksConfig: 33 | - name: "/" 34 | path: / 35 | method: GET 36 | timeout: 5 37 | retries: 15 38 | tags: ["80"] 39 | slackConfig: 40 | postDeploy: 41 | # INTENTIONALLY public for this example! 42 | webhook_url: "https://hooks.slack.com/services/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" 43 | postDelete: 44 | validator: 45 | useIngressHost: false 46 | slackConfig: 47 | postDelete: 48 | # INTENTIONALLY public for this example! 49 | webhook_url: "https://hooks.slack.com/services/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" 50 | 51 | 52 | ingress: 53 | dns: 54 | fqdnSuffix: ".local" 55 | metadata: 56 | annotations: 57 | kubernetes.io/ingress.class: "traefik" 58 | labels: 59 | bitsofinfo-ingress: "yes" 60 | -------------------------------------------------------------------------------- /examples/environments/catapp/chartconfigs.yaml: -------------------------------------------------------------------------------- 1 | chartConfigs: 2 | 3 | # Customized values for appdeploy chart 4 | # https://github.com/bitsofinfo/appdeploy 5 | # values here will override/merge w/ those 6 | # specified anywhere above 7 | appdeploy: 8 | chartValues: 9 | values: 10 | image: 11 | repository: "bitsofinfo/catapp" 12 | 13 | # here is an example (has no actual effect on running app) 14 | # of referencing "helmfile" state value data defined 15 | # in examples/values/customized-cluster.yaml 16 | env: 17 | INGRESS_CONTROLLER_URL: 18 | value: "{{.helmfile.targetCluster.ingressControllers.default.url}}" 19 | -------------------------------------------------------------------------------- /examples/environments/catapp/stage-qa.yaml: -------------------------------------------------------------------------------- 1 | appname: catapp 2 | 3 | # Optional: if present, this will be used instead of 'appname' above 4 | # when generating the helmfile `release` name. 5 | #releaseBaseName: capp 6 | 7 | # Optional: if present, will be appended to the helmfile 'release' name 8 | # as well as set as the 'classifier' value for appconduit/appdeploy charts 9 | #classifier: whatever 10 | 11 | # See ../hogapp/stage-qa.yaml for more info on this 12 | ports: 13 | - port: 80 14 | tls: false 15 | 16 | environments: 17 | stage: 18 | 19 | chartConfigs: 20 | 21 | # Customized values for appdeploy chart 22 | # https://github.com/bitsofinfo/appdeploy 23 | appdeploy: 24 | chartValues: 25 | values: 26 | replicaCount: 1 27 | 28 | contexts: 29 | stage-qa: 30 | 31 | # IMPORTANT! services with installed:true should 32 | # ALWAYS be listed FIRST to avoid downtime assuming 33 | # you run deployments.helmfile.yaml BEFORE conduits.helmfile.yaml 34 | services: 35 | - name: "current" 36 | version: "3.0.0" 37 | installed: true 38 | - name: "next" 39 | version: "4.0.0" 40 | installed: true 41 | - name: "previous" 42 | version: "2.0.0" 43 | installed: true 44 | - name: "old" 45 | version: "1.0.0" 46 | installed: false 47 | 48 | ingress: 49 | 50 | 51 | animals-mixed: 52 | mappings: 53 | - name: paths 54 | labels: 55 | bitsofinfo-ingress: "yes" 56 | hosts: 57 | - name: "animals.mydomain.com" 58 | paths: 59 | - "/orange" 60 | - "/red" 61 | serviceBindings: 62 | current: "40%" 63 | previous: "30%" 64 | next: "30%" 65 | 66 | animals-current: 67 | mappings: 68 | - name: paths 69 | labels: 70 | bitsofinfo-ingress: "yes" 71 | hosts: 72 | - name: "animals-current.mydomain.com" 73 | paths: 74 | - "/orange" 75 | - "/red" 76 | serviceBindings: 77 | current: "100%" 78 | 79 | animals-previous: 80 | mappings: 81 | - name: paths 82 | labels: 83 | bitsofinfo-ingress: "yes" 84 | hosts: 85 | - name: "animals-previous.mydomain.com" 86 | paths: 87 | - "/orange" 88 | - "/red" 89 | serviceBindings: 90 | previous: "100%" 91 | 92 | animals-next: 93 | mappings: 94 | - name: paths 95 | labels: 96 | bitsofinfo-ingress: "yes" 97 | hosts: 98 | - name: "animals-next.mydomain.com" 99 | paths: 100 | - "/orange" 101 | - "/red" 102 | serviceBindings: 103 | next: "100%" 104 | 105 | mixed: 106 | mappings: 107 | - name: all 108 | labels: 109 | bitsofinfo-ingress: "yes" 110 | hosts: 111 | - name: "catapp-mixed.mydomain.com" 112 | serviceBindings: 113 | current: "40%" 114 | previous: "30%" 115 | next: "30%" 116 | 117 | current: 118 | mappings: 119 | - name: all 120 | labels: 121 | bitsofinfo-ingress: "yes" 122 | hosts: 123 | - name: "catapp-current.mydomain.com" 124 | serviceBindings: 125 | current: "100%" 126 | 127 | previous: 128 | mappings: 129 | - name: all 130 | labels: 131 | bitsofinfo-ingress: "yes" 132 | hosts: 133 | - name: "catapp-previous.mydomain.com" 134 | serviceBindings: 135 | previous: "100%" 136 | 137 | next: 138 | mappings: 139 | - name: all 140 | labels: 141 | bitsofinfo-ingress: "yes" 142 | hosts: 143 | - name: "catapp-next.mydomain.com" 144 | serviceBindings: 145 | next: "100%" 146 | -------------------------------------------------------------------------------- /examples/environments/dogapp/chartconfigs.yaml: -------------------------------------------------------------------------------- 1 | chartConfigs: 2 | 3 | # Customized values for appdeploy chart 4 | # https://github.com/bitsofinfo/appdeploy 5 | # values here will override/merge w/ those 6 | # specified anywhere above 7 | appdeploy: 8 | chartValues: 9 | values: 10 | image: 11 | repository: "bitsofinfo/dogapp" 12 | 13 | # here is an example (has no actual effect on running app) 14 | # of referencing "helmfile" state value data defined 15 | # in examples/values/customized-cluster.yaml 16 | env: 17 | INGRESS_CONTROLLER_URL: 18 | value: "{{.helmfile.targetCluster.ingressControllers.default.url}}" 19 | -------------------------------------------------------------------------------- /examples/environments/dogapp/stage-qa.yaml: -------------------------------------------------------------------------------- 1 | appname: dogapp 2 | 3 | # Optional: if present, this will be used instead of 'appname' above 4 | # when generating the helmfile `release` name. 5 | #releaseBaseName: dapp 6 | 7 | # Optional: if present, will be appended to the helmfile 'release' name 8 | # as well as set as the 'classifier' value for appconduit/appdeploy charts 9 | #classifier: whatever 10 | 11 | # See ../hogapp/stage-qa.yaml for more info on this 12 | ports: 13 | - port: 80 14 | tls: false 15 | 16 | environments: 17 | stage: 18 | 19 | chartConfigs: 20 | 21 | # Customized values for appdeploy chart 22 | # https://github.com/bitsofinfo/appdeploy 23 | appdeploy: 24 | chartValues: 25 | values: 26 | replicaCount: 1 27 | 28 | contexts: 29 | stage-qa: 30 | 31 | # IMPORTANT! services with installed:true should 32 | # ALWAYS be listed FIRST to avoid downtime assuming 33 | # you run deployments.helmfile.yaml BEFORE conduits.helmfile.yaml 34 | services: 35 | - name: "current" 36 | version: "3.0.0" 37 | installed: true 38 | - name: "next" 39 | version: "4.0.0" 40 | installed: true 41 | - name: "previous" 42 | version: "2.0.0" 43 | installed: true 44 | - name: "old" 45 | version: "1.0.0" 46 | installed: false 47 | 48 | ingress: 49 | 50 | animals-mixed: 51 | mappings: 52 | - name: root 53 | annotations: 54 | traefik.ingress.kubernetes.io/rule-type: Path 55 | labels: 56 | bitsofinfo-ingress: "yes" 57 | hosts: 58 | - name: "animals.mydomain.com" 59 | paths: 60 | - "/" 61 | serviceBindings: 62 | current: "40%" 63 | previous: "30%" 64 | next: "30%" 65 | 66 | - name: paths 67 | labels: 68 | bitsofinfo-ingress: "yes" 69 | hosts: 70 | - name: "animals.mydomain.com" 71 | paths: 72 | - "/blue" 73 | - "/green" 74 | serviceBindings: 75 | current: "40%" 76 | previous: "30%" 77 | next: "30%" 78 | 79 | animals-current: 80 | mappings: 81 | - name: root 82 | annotations: 83 | traefik.ingress.kubernetes.io/rule-type: Path 84 | labels: 85 | bitsofinfo-ingress: "yes" 86 | hosts: 87 | - name: "animals-current.mydomain.com" 88 | paths: 89 | - "/" 90 | serviceBindings: 91 | current: "100%" 92 | - name: paths 93 | labels: 94 | bitsofinfo-ingress: "yes" 95 | hosts: 96 | - name: "animals-current.mydomain.com" 97 | paths: 98 | - "/blue" 99 | - "/green" 100 | serviceBindings: 101 | current: "100%" 102 | 103 | animals-previous: 104 | mappings: 105 | - name: root 106 | annotations: 107 | traefik.ingress.kubernetes.io/rule-type: Path 108 | labels: 109 | bitsofinfo-ingress: "yes" 110 | hosts: 111 | - name: "animals-previous.mydomain.com" 112 | paths: 113 | - "/" 114 | serviceBindings: 115 | previous: "100%" 116 | - name: paths 117 | labels: 118 | bitsofinfo-ingress: "yes" 119 | hosts: 120 | - name: "animals-previous.mydomain.com" 121 | paths: 122 | - "/blue" 123 | - "/green" 124 | serviceBindings: 125 | previous: "100%" 126 | 127 | animals-next: 128 | mappings: 129 | - name: root 130 | annotations: 131 | traefik.ingress.kubernetes.io/rule-type: Path 132 | labels: 133 | bitsofinfo-ingress: "yes" 134 | hosts: 135 | - name: "animals-next.mydomain.com" 136 | paths: 137 | - "/" 138 | serviceBindings: 139 | next: "100%" 140 | - name: paths 141 | labels: 142 | bitsofinfo-ingress: "yes" 143 | hosts: 144 | - name: "animals-next.mydomain.com" 145 | paths: 146 | - "/blue" 147 | - "/green" 148 | serviceBindings: 149 | next: "100%" 150 | 151 | 152 | mixed: 153 | mappings: 154 | - name: all 155 | labels: 156 | bitsofinfo-ingress: "yes" 157 | hosts: 158 | - name: "dogapp-mixed.mydomain.com" 159 | serviceBindings: 160 | current: "40%" 161 | previous: "30%" 162 | next: "30%" 163 | 164 | current: 165 | mappings: 166 | - name: all 167 | labels: 168 | bitsofinfo-ingress: "yes" 169 | hosts: 170 | - name: "dogapp-current.mydomain.com" 171 | serviceBindings: 172 | current: "100%" 173 | 174 | previous: 175 | mappings: 176 | - name: all 177 | labels: 178 | bitsofinfo-ingress: "yes" 179 | hosts: 180 | - name: "dogapp-previous.mydomain.com" 181 | serviceBindings: 182 | previous: "100%" 183 | 184 | next: 185 | mappings: 186 | - name: all 187 | labels: 188 | bitsofinfo-ingress: "yes" 189 | hosts: 190 | - name: "dogapp-next.mydomain.com" 191 | serviceBindings: 192 | next: "100%" 193 | -------------------------------------------------------------------------------- /examples/environments/hogapp/chartconfigs.yaml: -------------------------------------------------------------------------------- 1 | chartConfigs: 2 | 3 | # Customized values for appdeploy chart 4 | # https://github.com/bitsofinfo/appdeploy 5 | # values here will override/merge w/ those 6 | # specified anywhere above 7 | appdeploy: 8 | chartValues: 9 | values: 10 | image: 11 | repository: "bitsofinfo/hogapp" 12 | 13 | containerPorts: 14 | - name: nginx80 15 | port: 80 16 | service: true 17 | ingress: true 18 | tls: false 19 | - name: nginx443 20 | port: 443 21 | service: true 22 | ingress: true 23 | tls: true 24 | 25 | # here is an example (has no actual effect on running app) 26 | # of referencing "helmfile" state value data defined 27 | # in examples/values/customized-cluster.yaml 28 | env: 29 | INGRESS_CONTROLLER_URL: 30 | value: "{{.helmfile.targetCluster.ingressControllers.default.url}}" 31 | -------------------------------------------------------------------------------- /examples/environments/hogapp/stage-qa.yaml: -------------------------------------------------------------------------------- 1 | appname: hogapp 2 | 3 | # Optional: if present, this will be used instead of 'appname' above 4 | # when generating the helmfile `release` name. 5 | #releaseBaseName: happ 6 | 7 | # Optional: if present, will be appended to the helmfile 'release' name 8 | # as well as set as the 'classifier' value for appconduit/appdeploy charts 9 | #classifier: whatever 10 | 11 | # 12 | # the "ports" section 13 | # 14 | # Optional and if not declared, the default is set to one port of 80 w/ tls:false 15 | # @see conduits.helmfile.yaml 16 | # 17 | # For ~99% of all uses cases this should RARELY be more than one port 18 | # listed as it is the target port that all "appconduits" generated Services 19 | # and Ingresses will route traffic to. 20 | # 21 | # The "ports" section has a direct impact on the generated "values" for each 22 | # "appconduits" chart helm release that gets created by conduits.helmfile.yaml. 23 | # By default, this is entirely optional and will default to one port of 80 w/ tls:false 24 | # 25 | # When specified, for each release, by default the FIRST port listed below will yield a unique 26 | # appconduits "service" declaration for the appconduits chart, where each generated 27 | # appconduits values "service.[name]" will be equal to your services.[N].[name]-[ports.[currentPort.port] below. 28 | # 29 | # Correspondingly by default each listed "Ingress" below will yield a serviceBinding 30 | # that points back to its referenced serviceBindings.[servicename]-[port]. (i.e. the 31 | # first port only in the list below by default) 32 | # 33 | # How do you get it to generate a unique ingress per-port and not just the first 34 | # one? To do this you can specify the "ingress.[ingressname].targetedPorts" property 35 | # to be equal to an list or ports (i.e. ingress.[ingressname].targetedPorts: [port1,port2, etc]) 36 | # Setting this will generate a unique ingress per each port listed pointing back 37 | # to the matching named "service-[port]" 38 | # 39 | # When enabling a "ingress.[ingressname].targetedPorts=[]", it is HIGHLY RECOMMENDED that each 40 | # "hosts:" entry per enclosed ingress mapping, reference a [[#port]] variable directly, 41 | # which will be replaced w/ each port below as conduits.helmfile.yaml iterates 42 | # over the ports... failing to do this can result in unexpected behavior, 43 | # read further below... 44 | # 45 | # WARNING!!!: 46 | # ----------------- 47 | # ITS CRITICAL to note that enabling more than ONE "ingress.[ingressname].targetedPorts=[]" 48 | # below generates a unique IngressName-[port]->Service pathway for each "ingress" below. 49 | # For example, if you list 2 ports below and declare ONE ingress mapping below 50 | # that lists both ports in "ingress.[ingressname].targetedPorts" this will yield 2 51 | # actual k8s Ingress objects. The impact of this is that if you list any "hosts:" 52 | # entries below with FQDNs that do not reference the [[#port]] variable as descibed 53 | # above, you will end up with MULTIPLE k8s "Ingress" objects created referencing the same 54 | # host FQDNs pointing to different k8s Services on the backend.... and the result of such 55 | # a configuration and how it will behave at runtime is entirely dependant upon how your 56 | # Ingress controller merges/manages such a scenario. Its could be last man wins, sending traffic 57 | # to multiple un-intended backend Services etc. 58 | # 59 | # See the ingress.[name].mappings below 60 | # 61 | # The easiest way to understand what is going on is run helmile with 62 | # `log-level debug` and the `template` argument 63 | # 64 | # 65 | ports: 66 | - port: 80 67 | tls: false 68 | - port: 443 69 | tls: true 70 | 71 | 72 | environments: 73 | stage: 74 | 75 | chartConfigs: 76 | 77 | # Customized values for appdeploy chart 78 | # https://github.com/bitsofinfo/appdeploy 79 | appdeploy: 80 | chartValues: 81 | values: 82 | replicaCount: 1 83 | 84 | contexts: 85 | stage-qa: 86 | 87 | # IMPORTANT! services with installed:true should 88 | # ALWAYS be listed FIRST to avoid downtime assuming 89 | # you run deployments.helmfile.yaml BEFORE conduits.helmfile.yaml 90 | services: 91 | - name: "current" 92 | version: "3.0.0" 93 | installed: true 94 | - name: "next" 95 | version: "4.0.0" 96 | installed: true 97 | - name: "previous" 98 | version: "2.0.0" 99 | installed: true 100 | - name: "old" 101 | version: "1.0.0" 102 | installed: false 103 | 104 | ingress: 105 | animals-mixed: 106 | # (by default this will bind to Service:80) as 80 107 | # is the first port listed in "ports:" above 108 | # By default this will generate a single Ingress 109 | # mapped to a single service for port 80 110 | # targetedPorts: [80] 111 | mappings: 112 | - name: paths 113 | labels: 114 | bitsofinfo-ingress: "yes" 115 | hosts: 116 | - name: "animals.mydomain.com" 117 | paths: 118 | - "/brown" 119 | - "/pink" 120 | serviceBindings: 121 | current: "40%" 122 | previous: "30%" 123 | next: "30%" 124 | 125 | animals-current: 126 | # (by default this will bind to Service:80) as 80 127 | # is the first port listed in "ports:" above 128 | # By default this will generate a single Ingress 129 | # mapped to a single service for port 80 130 | # targetedPorts: [80] 131 | mappings: 132 | - name: paths 133 | labels: 134 | bitsofinfo-ingress: "yes" 135 | hosts: 136 | - name: "animals-current.mydomain.com" 137 | paths: 138 | - "/brown" 139 | - "/pink" 140 | serviceBindings: 141 | current: "100%" 142 | 143 | animals-previous: 144 | # (by default this will bind to Service:80) as 80 145 | # is the first port listed in "ports:" above 146 | # By default this will generate a single Ingress 147 | # mapped to a single service for port 80 148 | # targetedPorts: [80] 149 | mappings: 150 | - name: paths 151 | labels: 152 | bitsofinfo-ingress: "yes" 153 | hosts: 154 | - name: "animals-previous.mydomain.com" 155 | paths: 156 | - "/brown" 157 | - "/pink" 158 | serviceBindings: 159 | previous: "100%" 160 | 161 | animals-next: 162 | # (by default this will bind to Service:80) as 80 163 | # is the first port listed in "ports:" above 164 | # By default this will generate a single Ingress 165 | # mapped to a single service for port 80 166 | # targetedPorts: [80] 167 | mappings: 168 | - name: paths 169 | labels: 170 | bitsofinfo-ingress: "yes" 171 | hosts: 172 | - name: "animals-next.mydomain.com" 173 | paths: 174 | - "/brown" 175 | - "/pink" 176 | serviceBindings: 177 | next: "100%" 178 | 179 | mixed: 180 | # Here since we set this to an explicit list of 181 | # ports, this will generate a unique ingress per-port 182 | # that points to the corresponding service for that port 183 | # and it opens up the ability to use the [[#port]] variable 184 | # reference in each "host" name below. See WARNING above! 185 | targetedPorts: [80,443] 186 | mappings: 187 | - name: all 188 | labels: 189 | bitsofinfo-ingress: "yes" 190 | hosts: 191 | - name: "hogapp-mixed-[[#port]].mydomain.com" 192 | serviceBindings: 193 | current: "40%" 194 | previous: "30%" 195 | next: "30%" 196 | 197 | current: 198 | # Here since we set this to an explicit list of 199 | # ports, this will generate a unique ingress per-port 200 | # that points to the corresponding service for that port 201 | # and it opens up the ability to use the [[#port]] variable 202 | # reference in each "host" name below. See WARNING above! 203 | targetedPorts: [80,443] 204 | mappings: 205 | - name: all 206 | labels: 207 | bitsofinfo-ingress: "yes" 208 | hosts: 209 | - name: "hogapp-current-[[#port]].mydomain.com" 210 | serviceBindings: 211 | current: "100%" 212 | 213 | previous: 214 | # Here since we set this to an explicit list of 215 | # ports, this will generate a unique ingress per-port 216 | # that points to the corresponding service for that port 217 | # and it opens up the ability to use the [[#port]] variable 218 | # reference in each "host" name below. See WARNING above! 219 | targetedPorts: [80,443] 220 | mappings: 221 | - name: all 222 | labels: 223 | bitsofinfo-ingress: "yes" 224 | hosts: 225 | - name: "hogapp-previous-[[#port]].mydomain.com" 226 | serviceBindings: 227 | previous: "100%" 228 | 229 | next: 230 | # Here since we set this to an explicit list of 231 | # ports, this will generate a unique ingress per-port 232 | # that points to the corresponding service for that port 233 | # and it opens up the ability to use the [[#port]] variable 234 | # reference in each "host" name below. See WARNING above! 235 | targetedPorts: [80,443] 236 | mappings: 237 | - name: all 238 | labels: 239 | bitsofinfo-ingress: "yes" 240 | hosts: 241 | - name: "hogapp-next-[[#port]].mydomain.com" 242 | serviceBindings: 243 | next: "100%" 244 | -------------------------------------------------------------------------------- /examples/environments/index.yaml: -------------------------------------------------------------------------------- 1 | # IMPORTANT! The paths referenced here are relative 2 | # from the ROOT of this project where the main helmfile 3 | # lives (via. -f [helmfile.yaml]), NOT this directory... 4 | # 5 | # For each application you want to deploy and/or define 6 | # conduits for you should add an new "environments" entry below 7 | # 8 | # To target a specific environment when running helmfile you do 9 | # so via the `--environment [envname]` argument 10 | # 11 | # TODO: https://github.com/roboll/helmfile/issues/747 12 | # 13 | 14 | {{ $environmentsDir := requiredEnv "HELMFILE_DEPLOY_ENVIRONMENTS_DIR" }} 15 | 16 | environments: 17 | hogapp: 18 | values: 19 | - {{ $environmentsDir }}/hogapp/* 20 | catapp: 21 | values: 22 | - {{ $environmentsDir }}/catapp/* 23 | dogapp: 24 | values: 25 | - {{ $environmentsDir }}/dogapp/* 26 | -------------------------------------------------------------------------------- /examples/statevalues/customized-chartconfigs.yaml: -------------------------------------------------------------------------------- 1 | # These customize the defaults in helmfile-deploy/statevalues/000-globals.yaml 2 | # and will affect all environments (unless overridden) 3 | # @see helmfile-deploy/statevalues/000-globals.yaml for details 4 | 5 | chartConfigs: 6 | 7 | # For our examples, whenever the appconduits chart has a declared release 8 | # generated by helmfile, we will also specify the base values under "testapps" 9 | # loaded from [chartConfigs.appconduits.chartValues.baseValuesRootDir]/values/testapps/values.yaml 10 | # PLUS... the other patterns listed below @see helmfile-deploy/statevalues/000-globals.yaml for details 11 | appconduits: 12 | chartValues: 13 | # these are the value sets that will actually be applied 14 | # out of all "availableBaseValueSets" below 15 | baseValueSets: 16 | - defaults 17 | - appspecific 18 | 19 | # These are just named "sets" of values.yaml file locations 20 | # they are only used if referenced by "baseValueSets" above 21 | availableBaseValueSets: 22 | defaults: 23 | - testapps 24 | appspecific: 25 | - "{{.appname}}" 26 | - "{{.appname}}/{{.environmentName}}" 27 | - "{{.appname}}/{{.environmentName}}/{{.contextName}}" 28 | 29 | # here is an example (has no actual effect on running app) 30 | # of referencing "helmfile" state value data defined 31 | # in examples/values/customized-chartconfigs.yaml 32 | values: 33 | ingress: 34 | metadata: 35 | annotations: 36 | ingress-cluster/name: "{{.helmfile.targetCluster.name}}" 37 | 38 | # For our examples, whenever the appdeploy chart has a declared release 39 | # generated by helmfile, we will also specify the base values under "testapps" 40 | # loaded from [chartConfigs.appdeploy.chartValues.baseValuesRootDir]/values/testapps/values.yaml 41 | # PLUS... the other patterns listed below @see helmfile-deploy/statevalues/000-globals.yaml for details 42 | appdeploy: 43 | chartValues: 44 | 45 | # these are the value sets that will actually be applied 46 | # out of all "availableBaseValueSets" below 47 | baseValueSets: 48 | - defaults 49 | - appspecific 50 | 51 | # These are just named "sets" of values.yaml file locations 52 | # they are only used if referenced by "baseValueSets" above 53 | availableBaseValueSets: 54 | defaults: 55 | - testapps 56 | appspecific: 57 | - "{{.appname}}" 58 | - "{{.appname}}/{{.environmentName}}" 59 | - "{{.appname}}/{{.environmentName}}/{{.contextName}}" 60 | - "{{.appname}}/{{.environmentName}}/{{.contextName}}/{{.service.name}}" 61 | 62 | # here is an example (has no actual effect on running app) 63 | # of referencing "helmfile" state value data defined 64 | # in examples/values/customized-chartconfigs.yaml 65 | values: 66 | ingress: 67 | metadata: 68 | annotations: 69 | ingress-cluster/name: "{{.helmfile.targetCluster.name}}" 70 | -------------------------------------------------------------------------------- /examples/statevalues/customized-cluster.yaml: -------------------------------------------------------------------------------- 1 | # These customize the defaults in helmfile-deploy/statevalues/001-clusters.yaml 2 | # and will affect all environments (unless overridden) 3 | # @see helmfile-deploy/statevalues/000-clusters.yaml for details 4 | 5 | clusters: 6 | 7 | minikube: 8 | 9 | name: "minikube" 10 | 11 | # Here we supplement some info: 12 | ingressControllers: 13 | 14 | # assumes you have an ingress controller installed such as: 15 | # https://github.com/bitsofinfo/appdeploy/blob/master/examples/TRAEFIK_SETUP.md 16 | # This value is just here to be referenced in the examples "environments" 17 | # values customizations (i.e. see environments/*/chartconfigs.yaml) for samples 18 | default: 19 | url: "https://bitsofinfo-traefik.test.local" 20 | -------------------------------------------------------------------------------- /fileexists.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # hack until: https://github.com/roboll/helmfile/issues/766 3 | if [ ! -f "$1" ]; then 4 | echo -n 'false' 5 | else 6 | echo -n 'true' 7 | fi 8 | -------------------------------------------------------------------------------- /helmDefaults.yaml: -------------------------------------------------------------------------------- 1 | repositories: 2 | - name: stable 3 | url: https://charts.helm.sh/stable 4 | 5 | - name: bitsofinfo-appdeploy 6 | url: https://raw.githubusercontent.com/bitsofinfo/appdeploy/master/repo 7 | 8 | - name: bitsofinfo-appconduits 9 | url: https://raw.githubusercontent.com/bitsofinfo/appconduits/master/repo 10 | 11 | 12 | {{- $targetCluster := (pick .Values.clusters .Values.targetCluster) | values | first }} 13 | 14 | helmDefaults: 15 | 16 | # this can be overriden per invocation via --state-values-set forceHelmTillerNamespace= 17 | tillerNamespace: {{ $targetCluster.helmDefaults.tillerNamespace }} 18 | 19 | tillerless: {{ $targetCluster.helmDefaults.tillerless }} 20 | kubeContext: {{ $targetCluster.helmDefaults.kubeContext }} 21 | verify: {{ $targetCluster.helmDefaults.verify }} 22 | wait: {{ $targetCluster.helmDefaults.wait }} 23 | 24 | # this can be overriden per invocation via --state-values-set forceHelmTimeout= 25 | timeout: 900 26 | recreatePods: {{ $targetCluster.helmDefaults.recreatePods }} 27 | force: {{ $targetCluster.helmDefaults.force }} # helm3? https://github.com/roboll/helmfile/issues/1000#issuecomment-590619540 28 | 29 | # https://github.com/roboll/helmfile/issues/891 30 | # https://github.com/helm/helm/pull/7648 31 | # this can be overriden per invocation via --state-values-set forceHelmCreateNamespace= 32 | createNamespace: {{ $targetCluster.helmDefaults.createNamespace }} # if this is NOT present, helmfiles default is true... 33 | 34 | # https://github.com/roboll/helmfile/issues/686 35 | {{/* if and .Values.helm .Values.helm.dryRun */}} 36 | #args: 37 | # - "--dry-run" 38 | {{/* end */}} 39 | -------------------------------------------------------------------------------- /statevalues/000-globals.yaml: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # Default settings for the charts that helmfile-deploy generates releases for: 3 | # - i.e. the bitsofinfo/appdeploy and bitsofinfo/appconduits charts 4 | # 5 | # These global defaults can be overriden in the following ways 6 | # (all or a portion thereof) 7 | # 8 | # a) Re-declare in your own custom yaml files by adding additional *.yaml files 9 | # within the HELMFILE_DEPLOY_STATE_VALUES_DIR 10 | # 11 | # b) per helmfile "environment" YAML file. I.E. these could be declared in any 12 | # [HELMFILE_DEPLOY_ENVIRONMENTS_DIR]/[helmfile-environment]/[any-env-file.yaml] 13 | # - at the root level (i.e. just: chartConfigs) 14 | # - at the environments.[environmentName].chartConfigs level 15 | # - at the environments.[environmentName].contexts.[contextName] level 16 | # - at the environments.[environmentName].contexts.[contextName].services.[serviceName] level 17 | #------------------------------------------------------------------------------ 18 | 19 | chartConfigs: 20 | 21 | appconduits: 22 | chart: bitsofinfo-appconduits/appconduits 23 | version: 1.1.4 24 | # 25 | # Further chart values customization: 26 | # 27 | # WTF is a "baseValuesRootDir"? Each helmfile environment can declare its own 28 | # customization of "chartConfigs" at various levels such as the below to 29 | # declare sets of default values to apply to each release using this chart. 30 | # 31 | # EXAMPLE: 32 | # For the below configuration the conduits.helmfile.yaml would expect 33 | # the following values.yaml files to exist: 34 | # (Assuming: ) 35 | # 36 | # - ../custom-configs/myconfigs/chartvalues/appconduits/values/defaults/values.yaml 37 | # - ../custom-configs/myconfigs/chartvalues/appconduits/values/app1/stage/values.yaml 38 | # - ../custom-configs/myconfigs/chartvalues/appconduits/values/misc/stage/stage-qa/values.yaml 39 | # - ../custom-configs/myconfigs/chartvalues/appconduits/values/myappname/values.yaml 40 | # 41 | # Where in the below example you can reference any of the following 4 variables 42 | # in your "availableBaseValueSets" named set array items: 43 | # - {{.appname}} 44 | # - {{.classifier}} (if any) 45 | # - {{.environmentName}} the current environment name for the release being created 46 | # - {{.contextName}}" for the current context name for the release being created 47 | # 48 | # The values for the above TPL vars are from any of your YAML files @: 49 | # $HELMFILE_DEPLOY_ENVIRONMENTS_DIR/environments/