Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
release: detect change of charts base values
Browse files Browse the repository at this point in the history
By returning values as part of the `helm.Chart` object, so that the
comparison done during dry-run will take into account the charts
base values which ultimately results in ensure we perofrm a release
when the values change.
  • Loading branch information
stefansedich authored and hiddeco committed Jan 8, 2020
1 parent 448897b commit 94d20aa
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 40 deletions.
25 changes: 12 additions & 13 deletions pkg/apis/helm.fluxcd.io/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import (
"github.com/fluxcd/flux/pkg/resource"
"github.com/ghodss/yaml"

"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"k8s.io/helm/pkg/chartutil"

"github.com/fluxcd/helm-operator/pkg/helm/v2"
"github.com/fluxcd/helm-operator/pkg/helm"
helmv2 "github.com/fluxcd/helm-operator/pkg/helm/v2"
)

// +genclient
Expand Down Expand Up @@ -78,10 +77,10 @@ func (hr HelmRelease) GetTargetNamespace() string {
type ValuesFromSource struct {
// Selects a key of a ConfigMap.
// +optional
ConfigMapKeyRef *v1.ConfigMapKeySelector `json:"configMapKeyRef,omitempty"`
ConfigMapKeyRef *corev1.ConfigMapKeySelector `json:"configMapKeyRef,omitempty"`
// Selects a key of a Secret.
// +optional
SecretKeyRef *v1.SecretKeySelector `json:"secretKeyRef,omitempty"`
SecretKeyRef *corev1.SecretKeySelector `json:"secretKeyRef,omitempty"`
// Selects an URL.
// +optional
ExternalSourceRef *ExternalSourceSelector `json:"externalSourceRef,omitempty"`
Expand Down Expand Up @@ -136,7 +135,7 @@ type RepoChartSource struct {
Version string `json:"version"`
// An authentication secret for accessing the chart repo
// +optional
ChartPullSecret *v1.LocalObjectReference `json:"chartPullSecret,omitempty"`
ChartPullSecret *corev1.LocalObjectReference `json:"chartPullSecret,omitempty"`
}

// CleanRepoURL returns the RepoURL but ensures it ends with a trailing slash
Expand Down Expand Up @@ -166,7 +165,7 @@ type HelmReleaseSpec struct {
ChartSource `json:"chart"`
HelmVersion string `json:"helmVersion,omitempty"`
ReleaseName string `json:"releaseName,omitempty"`
ValueFileSecrets []v1.LocalObjectReference `json:"valueFileSecrets,omitempty"`
ValueFileSecrets []corev1.LocalObjectReference `json:"valueFileSecrets,omitempty"`
ValuesFrom []ValuesFromSource `json:"valuesFrom,omitempty"`
HelmValues `json:",inline"`
// Override the target namespace, defaults to metadata.namespace
Expand All @@ -193,7 +192,7 @@ func (hr HelmRelease) GetHelmVersion(defaultVersion string) string {
if defaultVersion != "" {
return defaultVersion
}
return v2.VERSION
return helmv2.VERSION
}

// GetTimeout returns the install or upgrade timeout (defaults to 300s)
Expand All @@ -212,7 +211,7 @@ func (hr HelmRelease) GetValuesFromSources() []ValuesFromSource {
if hr.Spec.ValueFileSecrets != nil {
var secretKeyRefs []ValuesFromSource
for _, ref := range hr.Spec.ValueFileSecrets {
s := &v1.SecretKeySelector{LocalObjectReference: ref}
s := &corev1.SecretKeySelector{LocalObjectReference: ref}
secretKeyRefs = append(secretKeyRefs, ValuesFromSource{SecretKeyRef: s})
}
valuesFrom = append(secretKeyRefs, valuesFrom...)
Expand Down Expand Up @@ -252,7 +251,7 @@ type HelmReleaseStatus struct {

type HelmReleaseCondition struct {
Type HelmReleaseConditionType `json:"type"`
Status v1.ConditionStatus `json:"status"`
Status corev1.ConditionStatus `json:"status"`
// +optional
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
// +optional
Expand Down Expand Up @@ -280,7 +279,7 @@ const (
// FluxHelmValues embeds chartutil.Values so we can implement deepcopy on map[string]interface{}
// +k8s:deepcopy-gen=false
type HelmValues struct {
chartutil.Values `json:"values,omitempty"`
helm.Values `json:"values,omitempty"`
}

// DeepCopyInto implements deepcopy-gen method for use in generated code
Expand All @@ -293,7 +292,7 @@ func (in *HelmValues) DeepCopyInto(out *HelmValues) {
if err != nil {
return
}
var values chartutil.Values
var values helm.Values
err = yaml.Unmarshal(b, &values)
if err != nil {
return
Expand Down
1 change: 1 addition & 0 deletions pkg/helm/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Chart struct {
Name string
Version string
AppVersion string
Values Values
Files []*File
Templates []*File
Dependencies []*Chart
Expand Down
2 changes: 2 additions & 0 deletions pkg/helm/v2/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ func chartToGenericChart(c *chart.Chart) *helm.Chart {
if c == nil || c.Metadata == nil {
return nil
}

return &helm.Chart{
Name: c.Metadata.Name,
Version: c.Metadata.Version,
AppVersion: c.Metadata.AppVersion,
Values: valuesToGenericValues(c.Values),
Files: filesToGenericFiles(c.Files),
Templates: templatesToGenericFiles(c.Templates),
Dependencies: dependenciesToGenericDependencies(c.Dependencies),
Expand Down
1 change: 1 addition & 0 deletions pkg/helm/v3/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func chartToGenericChart(c *chart.Chart) *helm.Chart {
Name: c.Name(),
Version: formatVersion(c),
AppVersion: c.AppVersion(),
Values: c.Values,
Files: filesToGenericFiles(c.Files),
Templates: filesToGenericFiles(c.Templates),
Dependencies: dependenciesToGenericDependencies(c.Dependencies()),
Expand Down
29 changes: 29 additions & 0 deletions pkg/helm/values.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package helm

import (
"crypto/sha256"
"encoding/hex"

"github.com/ghodss/yaml"
)

// Values represents a collection of (Helm) values.
// We define our own type to avoid working with two `chartutil`
// versions.
type Values map[string]interface{}

// YAML encodes the values into YAML bytes.
func (v Values) YAML() ([]byte, error) {
b, err := yaml.Marshal(v)
return b, err
}

// Checksum calculates and returns the SHA256 checksum of the YAML
// encoded values.
func (v Values) Checksum() string {
b, _ := v.YAML()

hasher := sha256.New()
hasher.Write(b)
return hex.EncodeToString(hasher.Sum(nil))
}
2 changes: 1 addition & 1 deletion pkg/release/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ func (r *Release) Uninstall(client helm.Client, hr *v1.HelmRelease) {
// before running the dry-run release to determine if any undefined
// mutations have occurred.
func shouldSync(logger log.Logger, client helm.Client, hr *v1.HelmRelease, curRel *helm.Release,
chartPath string, values values, logDiffs bool) (bool, error) {
chartPath string, values helm.Values, logDiffs bool) (bool, error) {

if curRel == nil {
logger.Log("info", "no existing release", "action", "install")
Expand Down
30 changes: 4 additions & 26 deletions pkg/release/values.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package release

import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path/filepath"

"github.com/fluxcd/helm-operator/pkg/helm"
"github.com/ghodss/yaml"

"k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -18,36 +17,15 @@ import (
"github.com/fluxcd/helm-operator/pkg/apis/helm.fluxcd.io/v1"
)

// values represents a collection of (Helm) values.
// We define our own type to avoid working with two `chartutil`
// versions.
type values map[string]interface{}

// YAML encodes the values into YAML bytes.
func (v values) YAML() ([]byte, error) {
b, err := yaml.Marshal(v)
return b, err
}

// Checksum calculates and returns the SHA256 checksum of the YAML
// encoded values.
func (v values) Checksum() string {
b, _ := v.YAML()

hasher := sha256.New()
hasher.Write(b)
return hex.EncodeToString(hasher.Sum(nil))
}

// values attempts to compose the final values for the given
// `HelmRelease`. It returns the values as bytes and a checksum,
// or an error in case anything went wrong.
func composeValues(coreV1Client corev1.CoreV1Interface, hr *v1.HelmRelease, chartPath string) (values, error) {
result := values{}
func composeValues(coreV1Client corev1.CoreV1Interface, hr *v1.HelmRelease, chartPath string) (helm.Values, error) {
result := helm.Values{}
ns := hr.Namespace

for _, v := range hr.GetValuesFromSources() {
var valueFile values
var valueFile helm.Values

switch {
case v.ConfigMapKeyRef != nil:
Expand Down
28 changes: 28 additions & 0 deletions test/e2e/15_upgrade.bats
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,34 @@ function setup() {
poll_until_equals 'podinfo-git HelmRelease revision matches' "$head_hash" "kubectl -n $DEMO_NAMESPACE get helmrelease/podinfo-git -o jsonpath='{.status.revision}'"
}

@test "Git values.yaml change causes upgrade" {
# Apply the HelmRelease fixtures
kubectl apply -f "$FIXTURES_DIR/releases/git.yaml" >&3

# Wait for it to be deployed
poll_until_equals 'podinfo-git HelmRelease' 'deployed' "kubectl -n $DEMO_NAMESPACE get helmrelease/podinfo-git -o 'custom-columns=status:status.releaseStatus' --no-headers"

# Clone the charts repository
local clone_dir
clone_dir="$(mktemp -d)"
defer rm -rf "'$clone_dir'"
git clone -b master ssh://git@localhost/git-server/repos/cluster.git "$clone_dir"
cd "$clone_dir"

# Make a values.yaml mutation in Git
sed -i 's%replicaCount: 1%replicaCount: 2%' charts/podinfo/values.yaml
git add charts/podinfo/values.yaml
git -c 'user.email=foo@bar.com' -c 'user.name=Foo' commit -m "Change replicaCount to 2"

# Record new HEAD and push change
head_hash=$(git rev-list -n 1 HEAD)
git push >&3

# Assert change is rolled out
poll_until_equals 'podinfo-git HelmRelease chart update' "successfully cloned chart revision: $head_hash" "kubectl -n $DEMO_NAMESPACE get helmrelease/podinfo-git -o jsonpath='{.status.conditions[?(@.type==\"ChartFetched\")].message}'"
poll_until_equals 'podinfo-git HelmRelease revision matches' "$head_hash" "kubectl -n $DEMO_NAMESPACE get helmrelease/podinfo-git -o jsonpath='{.status.revision}'"
}

function teardown() {
run_deferred

Expand Down

0 comments on commit 94d20aa

Please sign in to comment.