Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specify default values for values files #59

Closed
cmeury opened this issue Mar 22, 2018 · 49 comments
Closed

Specify default values for values files #59

cmeury opened this issue Mar 22, 2018 · 49 comments

Comments

@cmeury
Copy link
Contributor

cmeury commented Mar 22, 2018

To simplify building a hierarchy of values files "outside" of the helmfile, it would be useful for deduplication to specify a set of default files to always load. On top of that, the key-value map of the releases could be made available for interpolation:

...

defaults:
  values:
      - "./values/all/traefik.yaml"
      - "./values/{{ release.name }}/traefik.yaml"

releases:
  - name: traefik
    namespace: platform
    chart: stable/traefik
    version: 1.24.1

What do you guys think about this?

@mumoshu
Copy link
Collaborator

mumoshu commented Mar 22, 2018

Hi @cmeury, thanks for the feedback.

Sounds interesting to me!

But if I could ask for more, would you mind providing a more large, or maybe serious example?
I couldn't figure out the exact benefit of the feature only by reading the example you've provided above.

Especially, - "./values/{{ release.name }}/traefik.yaml" seems to fit best under releases[].values.

@cmeury
Copy link
Contributor Author

cmeury commented Mar 24, 2018

Mistake on my end, there should have been no mention of traefik in the defaults. It's a means of deduplication:

...

defaults:
  values:
      - "./values/default/{{ release.name }}.yaml"

      - "./values/{{ context.name }}/{{ release.name }}.yaml"

releases:
  - name: traefik
    namespace: platform
    chart: stable/traefik
    version: 1.24.1 

With a directory structure:

values/default/traefik.yaml
values/minikube/traefik.yaml
values/dev/traefik.yaml
etc.

Or vice versa with having folders named as the
release names and files according to context/environment.

@manics
Copy link
Contributor

manics commented Apr 6, 2018

Following up from #86 (comment)
The use case I have is deploying the same chart multiple times with different configs. This can be achieved with YAML anchors and references, but I personally I think it detracts from the clarity of YAML:

RELEASE_COMMON: &RELEASE_COMMON
  chart: jupyterhub/jupyterhub
  version: v0.7-4104389

releases:

  - <<: *RELEASE_COMMON
    name: jupyterhub-1
    namespace: jupyterhub-1
    ...
  - <<: *RELEASE_COMMON
    name: jupyterhub-2
    namespace: jupyterhub-2
    ...

I like the suggestion here of defaults since it keeps everything inside the helmfile instead of relying on external env-vars.

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 6, 2018

@cmeury Perhaps I should respond to your suggestion above in respect to #86 (comment).

Would this possibly be addressed by allowing templates inside secrets: and values: so that you can explicitly select which set of values to be consumed by helmfile via envvar?

// Well, but I believe that we really should have just a few envvar references inside a single helmfile.yml, ideally just something like {{ env \"HELMFILE_ENV\" }} across all the releases to switch set of values.yml and secrets.yml according to the environment name you're targeting.

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 6, 2018

@manics Interesting use-case indeed! But hey @cmeury, is your intention would be also supporting a default chart name and a default version number in the defaults section you suggested?

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 6, 2018

@manics I guess I have a similar use-case that I want to provision a brigade cluster and a set of network policies per namespace, and repeat it for every namespace in a single helmfile.yml, so that I have a complete declarative spec of all the namespaces in a single helmfile.yaml.

But I'm still unsure if it fits within the scope of helmfile.
To me, helmfile.yaml seems like better fit to a set of microservices possibly tied to one or two namespaces tightly coupled. That being said, my use-case seems to be better addressed by an another tool aggregates multiple helmfile.yml to draw the complete picture of your whole cluster, rather than just a meaningful set of microservices.

Or would there be an elegant solution to somehow allow us to aggregate multiple helmfile.ymls?

@cmeury
Copy link
Contributor Author

cmeury commented Apr 6, 2018

Kubernetes uses mergo to merge multiple kubectl configurations. This might be useful here?

@manics
Copy link
Contributor

manics commented Apr 6, 2018

@mumoshu Thanks. Initially I thought I could use helm chart dependencies to manage multiple charts in one go, but the external jupyterhub chart I'm deploying requires a separate namespace for each deployment.

I went through https://github.com/kubernetes/helm/blob/master/docs/related.md and looked at Landscaper, Armada and Helmfile. Helmfile is the only tool I could get working, and it's also a lot simpler.

If you feel this is outside the scope of Helmfile how about a "best practices" document?

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 6, 2018

@cmeury Thanks for the info! The library does look interesting.

However though, what I guess that I and @manics would eventually need here is more like "merging multiple rendered helmfile.yml's into a single helmfile.yml-like thing". So probably we still need to decide if that fits within the scope of helmfile or not. merge would indeed help us implement the feature anyway.

Oh, I'm just sharing my thoughts. Thanks anyway for sharing the info!

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 6, 2018

@manics I have followed the same path as yours 😃

If you feel this is outside the scope of Helmfile how about a "best practices" document?

Yes. IMHO a "best practices" doc is a must-have, at least.

@danielcb
Copy link

danielcb commented Apr 6, 2018

Just stumbled on this via the whole envvar-stuff. Thought I add my use case to the list. We probably could use defaults as well, but I'm also happy with the proposed standard yaml tooling.. less things that can break.

Here's what we would like to do:

We have a whole bunch of web applications that are part of a single 'stack'. Parts of the configuration for all apps are the same accross the whole stack (e.g. scheme 'https', the main part of the domain, customer data (ids, etc.)).

So as a minimal example helmfile:

releases:
- name: ""{{ env \"CUSTOMER_ID\" }}-app-a"
  namespace: "{{ env \"CUSTOMER_ID\" }}-app-a"
  chart: app-a
  values: ["./global-values.yaml"]
  set:
  - name: global.customer
    value: "{{ env \"CUSTOMER_LONGNAME\" }}"
  - name: global.customerId
    value: "{{ env \"CUSTOMER_ID\" }}"
- name: ""{{ env \"CUSTOMER_ID\" }}-app-b"
  namespace: "{{ env \"CUSTOMER_ID\" }}-app-b"
  chart: app-b
  values: ["./global-values.yaml"]
  set:
  - name: global.customer
    value: "{{ env \"CUSTOMER_LONGNAME\" }}"
  - name: global.customerId
    value: "{{ env \"CUSTOMER_ID\" }}"

the global-values.yaml looks like this (including all global values that don't change for different deployments/customers):

global:
  customer:
  customerId:
  scheme: https
  url: dev.domain.tld

the env vars for example could be: customer1 and c0000001 (i.e. everything that has to change dynamically at deployment time).

And the result would be app-a deployed as c0000001-app-a in namespace c0000001-app-a. The configured url would be https://customer1-appa.dev.domain.tld.

The deployment tool than just just uses the charts, a set of global value files as well as a set of env vars for the deployment. In a real example there are a lot more moving parts, but I think you get the idea.

All of this can of course also be done by generating whole helmfiles, but that's true for basically all cases of {{ env .. }} usage.

If there's anything wrong with this approach or someone has better ideas, I'm happy to hear them.

@sstarcher
Copy link
Contributor

sstarcher commented Apr 7, 2018

I have a similar need for something like this, but I would propose a different approach. First let me specify my exact use-case.

I have a structure as similar to the following for almost every chart I have.

    values:
      - helm/releases/CHART/values.yaml
      - helm/account/{{ env "HELM_ENV" }}/CHART/values.yaml
    secrets:
      - helm/releases/CHART/secrets.yaml
      - helm/account/{{ env "HELM_ENV" }}/CHART/secrets.yaml

In alot of these situations the values are common and the approach that @cmeury presented would work for some of my use-cases such as several charts that all want to be exposed over a ELB.

service:
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-2:123456789:certificate/ARN"

But, that does not work in all cases it only works when things are simple. An example of a case where this fails is the Prometheus chart that exposes multiple services.

alertmanager:
  service:
    annotations: 
       service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-2:123456789:certificate/ARN"
server:
  service:
    annotations: 
       service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-2:123456789:certificate/ARN"

Instead of directly merging the values. I would propose that we load the defaults in as yaml and allow them to be injected and allow the values files to be templated. Which was the start of my proposal in #97.

It would give us something like

values/dev/common.yaml

service:
    annotations: 
       service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-2:123456789:certificate/ARN"

helmfile

defaults:
  values:
      - "./values/default/common.yaml"
      - "./values/{{ env "HELM_DEV"/common.yaml"

releases:
  - name: traefik
    namespace: platform
    chart: stable/traefik
    version: 1.24.1 
    values:
      - helm/releases/CHART/values.yaml

helm/releases/CHART/values.yaml

service:
  annotations:
{{ .Defaults.service.annotations | indent 4 }}

And for my prometheus example

alertmanager:
  service:
    annotations: 
{{ .Defaults.service.annotations | indent 4 }}
server:
  service:
    annotations: 
{{ .Defaults.service.annotations | indent 4 }}

This would allow me to get rid of most of my environment specific values files and instead use the single common file per environment and the main release file that is shared.

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 18, 2018

@sstarcher Sorry if I am not following you correctly, but how about this?

releases:
  - name: traefik
    namespace: platform
    chart: stable/traefik
    version: 1.24.1 
    values:
      - helm/releases/CHART/values.yaml
      - helm/account/{{ env "HELM_ENV_NAME" }}/CHART/values.yaml
    secrets:
      - helm/releases/CHART/secrets.yaml
      - helm/account/{{ env "HELM_ENV_NAME" }}/CHART/secrets.yaml
    set:
    - name: alertmanager.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-ssl-cert
       value: {{ env "HELM_ENV_MY_ACM_CERT_ARN" | quote }}
    - name: server.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-ssl-cert
       value: {{ env "HELM_ENV_MY_ACM_CERT_ARN" | quote }}

I guess you'd want to avoid this so that the only envvar you must maintain becomes HELM_ENV only? (Just trying to understand this deeper

@sstarcher
Copy link
Contributor

@mumoshu that's similar to what I want to do, but it would be handy if it could work similar to a helm values file where I can access parts of the data. And I don't want to have to put everything in the helmfile.yaml and would like to interpolate the values files themselves.

My helmfile.yaml is already long and if I put every templated value in that file it will be very very long.

@sstarcher
Copy link
Contributor

Step one would be to allow - #97
I may have time next week to implement it if everyone thinks it's a good idea. I have not started on it yet since no one has commented.

@sstarcher
Copy link
Contributor

sstarcher commented Apr 18, 2018

Back to the environment variables it's also a pain to have users inject them as I have to have my team run a wrapper around helmfile sync to ensure they have the environment variables already set and the run does not fail.

Something like a defaults: yaml list would allow us to limit the environment variables that need to be set.

I just had another random idea to toss out. Allowing the defaults or something else to be set off of the cluster name.

defaults:
  common: 
    - common.yaml
  myCluster1:
     - special_cluster_1.yaml
  myCluster2:
      -  special_cluster_2.yaml

Something like that would also allow me to not have to set a HELM_ENV throughout and ensure users have it set up correctly. It could work off what cluster the user was connected to.

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 20, 2018

@sstarcher Thanks for the replies!

Just trying to see the overall picture of relevant issues now.

In general, I guess that what we want is "an ability to switch across multiple sets of templated releases", where each release includes chart name, version, values, and so on.

ensure they have the environment variables already set and the run does not fail.

Yeah. I understand this pain too. How about having a dedicated github issue for that?

We may have multiple options to support that out-of-box.
Add requiredEnvVars to helmfile.yml, or introduce requiredValues which you have to set like helmfile sync --set env=prod, or even introduce notion of environment to helmfile, or anything else?
The latter the more helmfile's scope expands, hence more discussion would be needed.
Anyway, I'm eager to support real use-cases like ones discussed here!

@sstarcher
Copy link
Contributor

Agreed just trying to think through the options of the best way to solve any of these issues before diving into a direction.

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 29, 2018

Also see #96 (comment)

@mumoshu
Copy link
Collaborator

mumoshu commented Apr 29, 2018

@sstarcher I think you suggested me to merge #96 (comment) into this issue, but probably we should have a dedicated issue for the idea of an alternative way to parameterize helmfiles other than envvars?

@sstarcher
Copy link
Contributor

@mumoshu I don't think so as my suggestion directly applies to this issue. I recommend against supporting generic values set that gets merged into all other values. And instead recommend having those values be reusable items similar to how helm values.yaml files are treated and can be referenced.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 24, 2018

How about introducing selectable environments that are composed of values files for helmfile?

environments:
  default:
    values:
    - foo: bar
    - ./default.yaml
  # note: values from the default env. aren't inherited to production. Use yaml anchors for inheritance.
  production:
    values:
    - ./production.yaml

releases:
- name: myapp
   chart: mychart
   values:
   # translates to `foo: bar` as `bar` is provided via the default environment
   - foo: {{ .Values.foo }}

helmfile sync loads the default environment, whereas helmfile --environment production sync loads the production environment.

@sstarcher
Copy link
Contributor

I would love to fully think through something like this as first class support for that would be very useful.

I currently do things like and with templating support of values files I could easily just pull that into a common configuration that could be templated down.

  - name: newrelic-master
    namespace: ops
    chart: stable/newrelic-infrastructure
    version: 0.4.3
    values:
      - ../releases/newrelic/values.yaml
      - ../releases/newrelic/master-values.yaml
      - {{ requiredEnv "HELM_ACCOUNT" }}/newrelic/values.yaml
    secrets:
      - {{ requiredEnv "HELM_ACCOUNT" }}/newrelic/master-secret/secrets.yaml

@sstarcher
Copy link
Contributor

Most of what I have above could be handled by templating the values values. So instead of having a different values file for prod/newrelic/values.yaml and dev/newrelic/values.yaml

I would have

environments:
  dev:
    values:
    - ./dev.yaml
  prod:
    values:
    - ./prod.yaml

And

  - name: newrelic-master
    namespace: ops
    chart: stable/newrelic-infrastructure
    version: 0.4.3
    values:
      - ../releases/newrelic/values.yaml

Where the
- ../releases/newrelic/values.yaml

Would now have a template reference to the default values pulled in. This would be expecially useful if it overall worked how helm does where the values can be referenced such as {{ .Values.myvalue.value }}. In our case it might make sense to call it .Defaults to not get confused with helms .Values or .Environment since you have the key currently called environment in your example.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 27, 2018

@sstarcher Thanks for the feedback! The feature would work as you might have expected.

I avoided .Defaults based on my assumption that it depends on the use-case if it is actually used as defaults of values, or not.

In addition, I prefer.Environment.Values over .Environments, if you think that's not too verbose.

The reason is that I assume, we'll end up wanting to customize helmDefaults(for example, TLS-based helm<->tiller auth is required only on production, not on dev box) per environment.
.Environment.Values can't be used for the purpose, because golang templating on helmfile.yaml is done before the environments[].values are parsed and loaded.
I would add environments[].defaults or environments[].helmDefaults(I don't have solid confidence in the name yet) so that options helmDefaults can be configured per environment, too.

The notion of .Environment.Values works without ambiguity, even after I add environments[].helmDefaults.
Could be overthinking, but I thought it worth considering anyway.

WDYT?

@sstarcher
Copy link
Contributor

Hmm so lets take that into consideration. Overall I think the concept of environments conflicts with how we have things layed out atm.

Currently if a user wanted to specify different helmDefaults they could specify different helmfiles.

So helmfile.d with say

  • dev.yaml - has dev defaults and has dev specific releases
  • prod.yaml - has prod defaults and prod specific releases
  • global.yaml - has releases and defaults for common things

With this layout it makes sense to have different helmDefaults as an example dev might want to do --devel, but the Environments does not really make sense with this layout as it's likely a global concept.

Might we consider a helmfile.yaml that lives in the helmfile.d as a common one that gets merged with everything else?

So say you have

* dev.yaml - has dev defaults and has dev specific releases
* prod.yaml - has prod defaults and prod specific releases
* helmfile.yaml - has releases and defaults for common things

You would put your Environments and your helmDefaults that you wanted shared in the helmfile.yaml and say in dev.yaml it would additionally specify a --devel flag for itself to override the defaults. In this structure you could specify things that made sense to be global to all of the helmfiles in a single location.

Open for other structural ideas, but having multiple helmfiles and having the concept of Environments really throws off our current structuring.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 27, 2018

Thanks for the feedback! I believe that your concerns are very valid.

I basically want (1) every file in helmfile.d to be a complete helmfile that is consumable with helmfile -f thefile.yaml sync and (2) something like helmfile -f helmfile.d/ --environment prod sync would just work by syncing only releases relevant to the prod env.

So I would either expect:

  • helmfile.yaml
    • environments/
      • defaults.yaml
      • prod.yaml

Or

  • helmfile.d/
    • 00-frontend.yaml
    • frontend/environments/
      • default.yaml
      • prod.yaml
    • 11-backend.yaml
    • backend/environments/
      • default.yaml
      • prod.yaml

helmfile.yaml in the first example would contain:

environments:
  default:
    values:
    - environments/default.yaml
  prod:
    values:
    - environments/prod.yaml

releases:
- name: mycommonapp
  # ...
{{ if eq .Environment.Name "prod" }}
- name: myprodonly-frontend-app
  # ...

whereas the 00-frontend.yaml in the second example would contain:

environments:
  default:
    values:
    - frontend/environments/default.yaml
  prod:
    values:
    - frontend/environments/prod.yaml

releases:
- name: mycommonapp
  # ...
{{ if eq .Environment.Name "prod" }}
- name: myprodonly-frontend-app
  # ...

To reduce the repetitions of environments over 00-frontend.yaml and 01-backend.yaml in the latter case, I think we want a include-like feature, slightly similar to what we have discussed in #96.

{{ include "_environments.yaml "frontend" }}

releases:
- name: mycommonapp
  # ...
{{ if eq .Environment.Name "prod" }}
- name: myprodonly-frontend-app
  # ...

@sstarcher
Copy link
Contributor

sstarcher commented Aug 27, 2018

Yep, I can fully agree with the desire to keep the helmfile fully functional as is.

My current structure is

  • helm
    • account
      • dev
        • frontend
        • backend
      • etc
      • prod
    • releases
      • frontend
      • backend

So restructuring it in this manner

  • helmfile.d
    • dev.yaml
    • prod.yaml
    • etc.yaml
    • common.yaml
    • releases
      • frontend
      • backend
    • account
      • dev
        • frontend
        • backend
      • etc
      • prod

PROD

environments:
  prod:
    values:
    - account/prod.yaml

{{ if eq .Environment.Name "prod" }}
releases:
- name: mycommonapp
  # ...
- name: myprodonly-frontend-app

Common

environments:
  dev:
    values:
    - account/dev.yaml
  prod:
    values:
    - account/prod.yaml

releases:
- name: other
  # ...
- name: something

I think something like that would be able to cover my specific use-case. And so for
{{ include "_environments.yaml "frontend" }} you are looking at being able to just include a file as a partial inlined.

@sstarcher
Copy link
Contributor

And of course under environments we would likely want to support secrets in addition to values.

@sstarcher
Copy link
Contributor

What happens when someone does helmfile sync and they don't have a default environment?

@czuares
Copy link

czuares commented Aug 27, 2018

@sstarcher Thoughts on an arbitrary label/selector approach? Labels could be assigned to releases that would then associate them with the corresponding values/secrets (or other configurations) in the labelConfig. Default configurations could be specified that would apply to releases that match no labels.

This could potentially leverage the existing labels defined in the helmfile to target charts individually.

Example:

#### Open to suggestions for how to handle this configuration
labelConfig:
  defaults:
  ## define global defaults here?
  environment:
    dev:
      values:
      - account/dev.yaml
    qa:
      values:
      - account/qa.yaml
    prod:
      values:
      - account/prod.yaml

releases:
- name: some-dev-only-chart
  labels:
  - environment=dev
- name: some-nonprod-chart
  labels:
  - 'environment!=prod'
- name: some-dev-qa-chart
  labels:
  - environment=dev
  - environment=qa
- name: some-everywhere-chart

I imagine the existing label selector implementation would need to be tweaked to support selectors like 'foo!=bar'. Alternatively, this could be omitted and the existing foo: bar key-value pairs could be used.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 28, 2018

@sstarcher Out of curiosity, what are contained in the releases directories before and after the restructure?

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 28, 2018

@czuares Interesting idea!
What would be benefit(s) of enhancing the labels/selector features to achieve this?

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 28, 2018

And of course under environments we would likely want to support secrets in addition to values.

Definitely!

Whata happens when someone does helmfile sync and they don't have a default environment?

I was considering to make helmfile emit a big warning and do noop in that case. The reason is that I thought, it isn't clear that a missing environment means the release to be deployed to all the environments or not.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 28, 2018

One limitation of the proposed environment feature is that we can't do this:

environments:
  default:
    values:
    - environments/default.yaml

respositories:
- name: your-private-git-repo-hosted-charts
  url: https://{{ .Environment.Values.githubToken }}@raw.githubusercontent.com/kmzfs/helm-repo-in-github/master/

That's because we need to evaluate the template in helmfile.yaml before helmfile.yaml is parsed as YAML and therefore environments/default.yaml is loaded.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 28, 2018

Alternative: Multiple YAML docs

One probably dirty way to avoid the limitation would be to exploit multiple yaml docs within a helmfile.yaml and merge those sub-helmfile-yamls one by one to produce the final one:

environments:
  default:
    values:
    - environments/default.yaml
---
respositories:
- name: your-private-git-repo-hosted-charts
  url: https://{{ .Environment.Values.githubToken }}@raw.githubusercontent.com/kmzfs/helm-repo-in-github/master/

Alternative: Environment values file via a flag

One more alternative would be to drop environments: from helmfile.yaml and instead provide the environment externally. Environments would look more global in this case, because we have no way to specify different environment per each helmfile under helmfile.d/.

helmfile --environemnt environments/default.yaml -f helmfile.d/ sync

In this case the environment yaml should have a completely different structure to keep feature parity with env-per-helmfile.

default.yaml:

00-frontend:
  values:
  - environments/default/00-frontend.yaml
01-backend:
  values:
  - environments/default/01-backend.yaml

Which one to choose?

None of my ideas seems perfect.

I slightly prefer environments: defined in helmfile.yaml, accepting the limitation that .Environment.Values can't be referenced from within helmfile.yaml itself. From my perspective, it seems simpler to implement, more straight-forward to understand than the multiple yaml docs way.

It also seems more declarative than providing an environment yaml file externally, because the connection between the environment yaml and helmfile.yaml is contained in the helmfile.yaml.

@sstarcher
Copy link
Contributor

Ya, I'm not a fan of having to encapsulate things on the command line. I would lean toward not allowing .Environment to be referenced in the helmfile.yaml template and possibly with a future extension of supporting a separate yaml doc via your example of

environment: {}
---
...

I believe in my current use-case I reference a few environment variables in the helmfile, but I only do that as helmfile at the time did not support templating values.yaml files. Now that it supports templating values.yaml files I greatly prefer that. After that transition the only thing I would reference in the helmfile.yaml would likely be the environment name which we would have already.

To answer your other question the releases directory contains a folder for each release I have that allows for global configuration between all environments.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 29, 2018

@cmeury #216, in combination with the environment values feature, will allow you to create a values.release.yaml containing:

{{ readFile (printf "./values/default/%s.yaml" .Release.Name }}

and values.environment.release.yaml containing:

{{ readFile (printf "./values/%s/%s.yaml" .Environment.Name .Release.Name }}

With those two values.yaml templates, your example helmfile.yaml:

defaults:
  values:
      - "./values/default/{{ release.name }}.yaml"
      - "./values/{{ context.name }}/{{ release.name }}.yaml"

releases:
  - name: traefik
    namespace: platform
    chart: stable/traefik
    version: 1.24.1 

can be rewritten to:

releases:
  - name: traefik
    namespace: platform
    chart: stable/traefik
    version: 1.24.1
    values:
    - values.release.yaml
    - values.environment.release.yaml

Would it resolve your use-case?

Would you like more deduplication, perhaps by somehow removing the need to repeat values.release.yaml and values.environment.release.yaml for every release in your helmfile.yaml?

@sstarcher
Copy link
Contributor

@mumoshu what you suggest would only work fir a single releases though? Or are you suggesting that we template the string in the context of the release?

Say I had the following and the example does not holdup as being useful.

defaults:
  values:
      - "./values/default/{{ release.name }}.yaml"
      - "./values/{{ context.name }}/{{ release.name }}.yaml"

releases:
  - name: traefik
    namespace: platform
    chart: stable/traefik
    version: 1.24.1 
  - name: github
    namespace: platform
    chart: stable/github
    version: 1.24.1 

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 29, 2018

Yep my intention was to template values.yaml in the context of release.

But I started to think that the original goal was to avoid repetition of values: across multiple releases, rather than just switching values according to env and release
name.

To avoid repetition of values:, we'd still need to a kind of default values.yaml files.

Btw, what if I wrote this?

default:
  values:
  - defaults/{{ .release.name }}.yaml

releases:
- name: myapp
   values:
   - myapp/values.yaml

Does it load myapp/values.yaml only, or also defaults/myapp.yaml?

@sstarcher
Copy link
Contributor

ya, in my case almost every release has 3 values and 1-2 secrets. Talk about boilerplate :)

The templating that I'm not leverage yet of the values.yaml may resolve some of that.

@sstarcher
Copy link
Contributor

sstarcher commented Aug 29, 2018

@mumoshu in that case how would you know what .release.name was at the top level? When templating out the file itself we would have no concept of a release. Or are you suggesting that the defaults could change per release and it would not be resolved by the templating of helmfile.yaml, but by helmfile at the release level.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 29, 2018

@sstarcher Yes that's the issue...

Templating the each entry in values: array in different context is definitely one way to implement it. But I believe it confuses users by forcing to remember which part of helmfile.yaml is templated in which context.

Maybe using {{ define ... }} to create a helper {{ template "values" "myapp" }} to keep amount of repetition minimum:

releases:
- name: myapp
{{ template "values" "myapp" }}

would be the best thing we can do?

@sstarcher
Copy link
Contributor

ya, I was not suggestion templating the releases I was just asking how you thought it might work.

I could see a template being sufficient to simplify that.

@mumoshu
Copy link
Collaborator

mumoshu commented Aug 30, 2018

Thanks for your confirmation as always.

How about closing this issue as resolved by, after #253 is implemented, adding some doc like the below?


Default values.yaml files

One way to reduce repetitions of values sections in helmfile.yaml is to leverage Convention over Configuration.

How do you reduce repetitions of values: arrays in the below helmfile.yaml?

releases:
- name: frontend
  chart: ./charts/frontend
  values:
  - ./defaults/frontend/values.yaml
  - ./environments/{{ .Environment.Name }}/frontend/values.yaml

- name: backend
  chart: ./charts/backend
  values:
  - ./defaults/backend/values.yaml
  - ./environments/{{ .Environment.Name }}/backend/values.yaml

# and bunch of similar releases repeat...

In combination with the environment values (#253) feature and the values template file feature (#216), this is possible by creating a values.release.yaml containing:

{{ readFile (printf "./defaults/%s/values.yaml" .Release.Name }}

and values.environment.release.yaml containing:

{{ readFile (printf "./environments/%s/%s/values.yaml" .Environment.Name .Release.Name }}

With those two values.yaml templates, the helmfile.yaml can be rewritten to:

{{ define "conventionalValues" }}
  values:
  - values.release.yaml
  - values.environment.release.yaml
{{ end }}

releases:
- name: frontend
  chart: ./charts/frontend
{{ template "conventionalValues" }}

- name: backend
  chart: ./charts/backend
{{ template "conventionalValues" }}

@sstarcher
Copy link
Contributor

Any reason to do

{{ define "conventionalValues" }}
  values:
  - values.release.yaml
  - values.environment.release.yaml
{{ end }}

instead of putting the templating of the release and environment directly into the template helper?

@mumoshu
Copy link
Collaborator

mumoshu commented Sep 5, 2018

@sstarcher Nothing particular 🤔

Out of curiosity, would you write {{ template "release" (dist "name" "frontend" "chart" "./charts/frontend" }}, so that it renders:

releases:
- name: frontend
  chart: ./charts/frontend
  values:
  - values.release.yaml
  - values.environment.release.yaml

@sstarcher
Copy link
Contributor

What I ment is I don't understand how your example works.

{{ define "conventionalValues" }}
  values:
  - values.release.yaml
  - values.environment.release.yaml
{{ end }}

How does that function? Or were you just using it as a quick example and would be replacing - values.release.yaml with the values?

@mumoshu
Copy link
Collaborator

mumoshu commented May 14, 2019

I believe release templates and #439 and the recently enhanecd helmfile state layering #587 allows you to have default values files named after any variable including the release name, and reuse it as a release template.

Closing this as finally resolved!

@mumoshu mumoshu closed this as completed May 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants