diff --git a/apis/helm.integrations.flux.weave.works/v1alpha2/types.go b/apis/helm.integrations.flux.weave.works/v1alpha2/types.go index 790162c45..e898cdfbc 100644 --- a/apis/helm.integrations.flux.weave.works/v1alpha2/types.go +++ b/apis/helm.integrations.flux.weave.works/v1alpha2/types.go @@ -2,6 +2,7 @@ package v1alpha2 import ( "github.com/ghodss/yaml" + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/helm/pkg/chartutil" ) @@ -22,10 +23,10 @@ type FluxHelmRelease struct { // FluxHelmReleaseSpec is the spec for a FluxHelmRelease resource // FluxHelmReleaseSpec type FluxHelmReleaseSpec struct { - ChartGitPath string `json:"chartGitPath"` - ReleaseName string `json:"releaseName,omitempty"` - ValueFiles []string `json:"valueFiles,omitempty"` - FluxHelmValues `json:",inline"` + ChartGitPath string `json:"chartGitPath"` + ReleaseName string `json:"releaseName,omitempty"` + ValueFileSecrets []v1.LocalObjectReference `json:"valueFileSecrets,omitempty"` + FluxHelmValues `json:",inline"` } type FluxHelmReleaseStatus struct { diff --git a/chart/flux/templates/helm-operator-crd.yaml b/chart/flux/templates/helm-operator-crd.yaml index 739e1e8b0..9b6d5d538 100644 --- a/chart/flux/templates/helm-operator-crd.yaml +++ b/chart/flux/templates/helm-operator-crd.yaml @@ -38,9 +38,12 @@ spec: type: string values: type: object - valueFiles: + valueFileSecrets: type: array items: - type: string + type: object + properties: + name: + type: string {{- end -}} {{- end -}} diff --git a/deploy-helm/flux-helm-release-crd.yaml b/deploy-helm/flux-helm-release-crd.yaml index 9882bc84f..6522442e6 100644 --- a/deploy-helm/flux-helm-release-crd.yaml +++ b/deploy-helm/flux-helm-release-crd.yaml @@ -32,7 +32,10 @@ spec: type: string values: type: object - valueFiles: + valueFileSecrets: type: array items: - type: string + type: object + properties: + name: + type: string diff --git a/integrations/helm/chartsync/chartsync.go b/integrations/helm/chartsync/chartsync.go index 63b098d26..a26033917 100644 --- a/integrations/helm/chartsync/chartsync.go +++ b/integrations/helm/chartsync/chartsync.go @@ -222,7 +222,7 @@ func (chs *ChartChangeSync) applyChartChanges(prevRef, head string) error { rlsName := release.GetReleaseName(fhr) opts := release.InstallOptions{DryRun: false} chs.mu.RLock() - if _, err = chs.release.Install(chs.clone.Dir(), rlsName, fhr, release.UpgradeAction, opts); err != nil { + if _, err = chs.release.Install(chs.clone.Dir(), rlsName, fhr, release.UpgradeAction, opts, &chs.kubeClient); err != nil { // NB in this step, failure to release is considered non-fatal, i.e,. we move on to the next rather than giving up entirely. chs.logger.Log("warning", "failure to release chart with changes in git", "error", err, "chart", chartPath, "release", rlsName) } @@ -250,7 +250,7 @@ func (chs *ChartChangeSync) reconcileReleaseDef(fhr ifv1.FluxHelmRelease) { opts := release.InstallOptions{DryRun: false} if rel == nil { - _, err := chs.release.Install(chs.clone.Dir(), releaseName, fhr, release.InstallAction, opts) + _, err := chs.release.Install(chs.clone.Dir(), releaseName, fhr, release.InstallAction, opts, &chs.kubeClient) if err != nil { chs.logger.Log("warning", "Failed to install chart", "namespace", fhr.Namespace, "name", fhr.Name, "error", err) } @@ -263,7 +263,7 @@ func (chs *ChartChangeSync) reconcileReleaseDef(fhr ifv1.FluxHelmRelease) { return } if changed { - _, err := chs.release.Install(chs.clone.Dir(), releaseName, fhr, release.UpgradeAction, opts) + _, err := chs.release.Install(chs.clone.Dir(), releaseName, fhr, release.UpgradeAction, opts, &chs.kubeClient) if err != nil { chs.logger.Log("warning", "Failed to upgrade chart", "namespace", fhr.Namespace, "name", fhr.Name, "error", err) } @@ -391,7 +391,7 @@ func (chs *ChartChangeSync) shouldUpgrade(chartsRepo string, currRel *hapi_relea // Get the desired release state opts := release.InstallOptions{DryRun: true} tempRelName := currRel.GetName() + "-temp" - desRel, err := chs.release.Install(chartsRepo, tempRelName, fhr, release.InstallAction, opts) + desRel, err := chs.release.Install(chartsRepo, tempRelName, fhr, release.InstallAction, opts, &chs.kubeClient) if err != nil { return false, err } diff --git a/integrations/helm/release/release.go b/integrations/helm/release/release.go index cd7dc135e..c6d1e1b29 100644 --- a/integrations/helm/release/release.go +++ b/integrations/helm/release/release.go @@ -4,13 +4,14 @@ import ( "bytes" "context" "fmt" - "io/ioutil" "os/exec" "path/filepath" "time" "github.com/ghodss/yaml" "github.com/go-kit/kit/log" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "k8s.io/helm/pkg/chartutil" k8shelm "k8s.io/helm/pkg/helm" hapi_release "k8s.io/helm/pkg/proto/hapi/release" @@ -135,7 +136,7 @@ func (r *Release) canDelete(name string) (bool, error) { // charts, and the FluxHelmRelease specifying the release. Depending // on the release type, this is either a new release, or an upgrade of // an existing one. -func (r *Release) Install(repoDir, releaseName string, fhr ifv1.FluxHelmRelease, action Action, opts InstallOptions) (*hapi_release.Release, error) { +func (r *Release) Install(repoDir, releaseName string, fhr ifv1.FluxHelmRelease, action Action, opts InstallOptions, kubeClient *kubernetes.Clientset) (*hapi_release.Release, error) { r.logger.Log("info", fmt.Sprintf("releaseName= %s, action=%s, install options: %+v", releaseName, action, opts)) chartPath := fhr.Spec.ChartGitPath @@ -160,16 +161,19 @@ func (r *Release) Install(repoDir, releaseName string, fhr ifv1.FluxHelmRelease, // Read values from given valueFile paths (configmaps, etc.) mergedValues := chartutil.Values{} - for _, valueFile := range fhr.Spec.ValueFiles { - data, err := ioutil.ReadFile(valueFile) + for _, valueFileSecret := range fhr.Spec.ValueFileSecrets { + // Read the contents of the secret + secret, err := kubeClient.CoreV1().Secrets(namespace).Get(valueFileSecret.Name, v1.GetOptions{}) if err != nil { - r.logger.Log("error", fmt.Sprintf("Cannot read valueFile %s for Chart release [%s]: %#v", valueFile, releaseName, err)) + r.logger.Log("error", fmt.Sprintf("Cannot get secret %s for Chart release [%s]: %#v", valueFileSecret.Name, releaseName, err)) return nil, err } + + // Load values.yaml file and merge var values chartutil.Values - err = yaml.Unmarshal(data, &values) + err = yaml.Unmarshal(secret.Data["values.yaml"], &values) if err != nil { - r.logger.Log("error", fmt.Sprintf("Cannot yaml.Unmashal valueFile %s for Chart release [%s]: %#v", valueFile, releaseName, err)) + r.logger.Log("error", fmt.Sprintf("Cannot yaml.Unmashal values.yaml in secret %s for Chart release [%s]: %#v", valueFileSecret.Name, releaseName, err)) return nil, err } mergedValues = mergeValues(mergedValues, values) diff --git a/site/helm-integration.md b/site/helm-integration.md index 457251845..acd94acdf 100644 --- a/site/helm-integration.md +++ b/site/helm-integration.md @@ -37,12 +37,13 @@ Flux-Helm Integration implementation consists of two parts: kind: FluxHelmRelease metadata: name: mongodb - namespace: myNamespace + namespace: myNamespace spec: chartGitPath: mongodb releaseName: mongo-database - valueFiles: - - "/opt/my-configmap/cluster-values.yaml" + valueFileSecrets: + - name: "my-secret-1" + - name: "my-secret-2" values image: bitnami/mongodb:3.7.1-r1 ``` @@ -68,32 +69,45 @@ Flux-Helm Integration implementation consists of two parts: - item2 ``` -- `.spec.valueFiles` - An array of paths to files in the helm-operator pod - which will be read in a similar way to the `-f/--values` flag in the - helm CLI. These values always have a lower priority that those passed - via the `.spec.values` parameter. +- `.spec.valueFileSecrets` - Value files read in a similar way to the + `-f/--values` flag in the helm CLI. This field is an array of + `LocalObjectReference` which reference secrets. + + - A `LocalObjectReference` is an object with a `name` parameter (see + example below). + - The secrets must contain a file called `values.yaml`. + - The secrets must be in the same namespace as the FluxHelmRelease. + - These values always have a lower priority that those passed + via the `.spec.values` parameter. + - If multiple secret names are passed the last in the list have higher + priority. This is useful if you want to have cluster defaults such as the `region`, `clustername`, `environment`, a local docker registry URL, - etc. + etc. or if you want to have secret values not checked into git. For example: ```yaml - valueFiles: - - "/opt/my-configmap/cluster-values.yaml" - - "/opt/my-configmap/cluster-values-2.yaml" + valueFileSecrets: + - name: "my-secret-1" + - name: "my-secret-2" ``` - Where `my-configmap` _could_ look something like: + Where `my-secret-1` _could_ look something like: ```yaml apiVersion: v1 - kind: ConfigMap - metadata: - name: cluster-bootstrap - namespace: flux + kind: Secret + type: Opaque + metadata: + name: cluster-bootstrap + namespace: dev data: - cluster-values.yaml: |+ - cluster_env: prod - logLevel: warn + values.yaml: + ``` + The contents of values.yaml _could_ look something like: + ```yaml + clusterName: "my-cluster" + dockerRegistry: "registry.local" + mySecretValue: "foo" ``` - `.spec.releaseName`: