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

Avoid constant re-deployment of charts with multiple inline dependencies #1272

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions chart/flux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ The following tables lists the configurable parameters of the Weave Flux chart a
| `helmOperator.repository` | Helm operator image repository | `quay.io/weaveworks/helm-operator`
| `helmOperator.tag` | Helm operator image tag | `0.1.0-alpha`
| `helmOperator.pullPolicy` | Helm operator image pull policy | `IfNotPresent`
| `helmOperator.logReleaseDiffs` | Helm operator should log the diff when a chart release diverges (possibly insecure) | `false`
| `helmOperator.tillerNamespace` | Namespace in which the Tiller server can be found | `kube-system`
| `helmOperator.tls.enable` | Enable TLS for communicating with Tiller | `false`
| `helmOperator.tls.verify` | Verify the Tiller certificate, also enables TLS when set to true | `false`
Expand Down
1 change: 1 addition & 0 deletions chart/flux/templates/helm-operator-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ spec:
- --git-url={{ .Values.git.url }}
- --git-branch={{ .Values.git.branch }}
- --git-charts-path={{ .Values.git.chartsPath }}
- --log-release-diffs={{ .Values.helmOperator.logReleaseDiffs }}
- --tiller-namespace={{ .Values.helmOperator.tillerNamespace }}
{{- if .Values.helmOperator.tls.enable }}
- --tiller-tls-enable={{ .Values.helmOperator.tls.enable }}
Expand Down
1 change: 1 addition & 0 deletions chart/flux/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ helmOperator:
tag: 0.1.1-alpha
pullPolicy: IfNotPresent
tillerNamespace: kube-system
logReleaseDiffs: false
tls:
secretName: 'helm-client-certs'
verify: false
Expand Down
7 changes: 5 additions & 2 deletions cmd/helm-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var (

chartsSyncInterval *time.Duration
chartsSyncTimeout *time.Duration
logReleaseDiffs *bool

gitURL *string
gitBranch *string
Expand Down Expand Up @@ -91,6 +92,7 @@ func init() {

chartsSyncInterval = fs.Duration("charts-sync-interval", 3*time.Minute, "Interval at which to check for changed charts")
chartsSyncTimeout = fs.Duration("charts-sync-timeout", 1*time.Minute, "Timeout when checking for changed charts")
logReleaseDiffs = fs.Bool("log-release-diffs", false, "Log the diff when a chart release diverges; potentially insecure")

gitURL = fs.String("git-url", "", "URL of git repo with Helm Charts; e.g., git@github.com:weaveworks/flux-example")
gitBranch = fs.String("git-branch", "master", "branch of git repo")
Expand Down Expand Up @@ -207,7 +209,7 @@ func main() {
chartSync := chartsync.New(log.With(logger, "component", "chartsync"),
chartsync.Polling{Interval: *chartsSyncInterval, Timeout: *chartsSyncTimeout},
chartsync.Clients{KubeClient: *kubeClient, IfClient: *ifClient},
rel, repoConfig)
rel, repoConfig, *logReleaseDiffs)
chartSync.Run(shutdown, errc, shutdownWg)

// OPERATOR - CUSTOM RESOURCE CHANGE SYNC -----------------------------------------------
Expand All @@ -218,7 +220,8 @@ func main() {
// Reference to shared index informers for the FluxHelmRelease
fhrInformer := ifInformerFactory.Helm().V1alpha2().FluxHelmReleases()

opr := operator.New(log.With(logger, "component", "operator"), kubeClient, fhrInformer, rel, repoConfig)
opr := operator.New(log.With(logger, "component", "operator"), *logReleaseDiffs,
kubeClient, fhrInformer, rel, repoConfig)
// Starts handling k8s events related to the given resource kind
go ifInformerFactory.Start(shutdown)

Expand Down
76 changes: 67 additions & 9 deletions integrations/helm/chartsync/chartsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,18 @@ import (
"context"
"fmt"
"path/filepath"
"sort"
"sync"
"time"

"github.com/go-kit/kit/log"
google_protobuf "github.com/golang/protobuf/ptypes/any"
"github.com/google/go-cmp/cmp"
"github.com/ncabatoff/go-seq/seq"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
hapi_chart "k8s.io/helm/pkg/proto/hapi/chart"
hapi_release "k8s.io/helm/pkg/proto/hapi/release"

ifv1 "github.com/weaveworks/flux/apis/helm.integrations.flux.weave.works/v1alpha2"
Expand All @@ -73,16 +78,18 @@ type ChartChangeSync struct {
ifClient ifclientset.Clientset
release *release.Release
config helmop.RepoConfig
logDiffs bool
}

func New(logger log.Logger, polling Polling, clients Clients, release *release.Release, config helmop.RepoConfig) *ChartChangeSync {
func New(logger log.Logger, polling Polling, clients Clients, release *release.Release, config helmop.RepoConfig, logReleaseDiffs bool) *ChartChangeSync {
return &ChartChangeSync{
logger: logger,
Polling: polling,
kubeClient: clients.KubeClient,
ifClient: clients.IfClient,
release: release,
config: config,
logDiffs: logReleaseDiffs,
}
}

Expand Down Expand Up @@ -309,6 +316,48 @@ func (chs *ChartChangeSync) getCustomResources() ([]ifv1.FluxHelmRelease, error)
return fhrs, nil
}

func sortStrings(ss []string) []string {
ret := append([]string{}, ss...)
sort.Strings(ret)
return ret
}

func sortChartFields(c *hapi_chart.Chart) *hapi_chart.Chart {
nc := hapi_chart.Chart{
Metadata: &(*c.Metadata),
Templates: append([]*hapi_chart.Template{}, c.Templates...),
Files: append([]*google_protobuf.Any{}, c.Files...),
}

if c.Values != nil {
nc.Values = &(*c.Values)
}

sort.SliceStable(nc.Files, func(i, j int) bool {
return seq.Compare(nc.Files[i], nc.Files[j]) < 0
})
sort.SliceStable(nc.Templates, func(i, j int) bool {
return seq.Compare(nc.Templates[i], nc.Templates[j]) < 0
})

nc.Metadata.Sources = sortStrings(nc.Metadata.Sources)
nc.Metadata.Keywords = sortStrings(nc.Metadata.Keywords)
nc.Metadata.Maintainers = append([]*hapi_chart.Maintainer{}, nc.Metadata.Maintainers...)
sort.SliceStable(nc.Metadata.Maintainers, func(i, j int) bool {
return seq.Compare(nc.Metadata.Maintainers[i], nc.Metadata.Maintainers[j]) < 0
})

nc.Dependencies = make([]*hapi_chart.Chart, len(c.Dependencies))
for i := range c.Dependencies {
nc.Dependencies[i] = sortChartFields(c.Dependencies[i])
}
sort.SliceStable(nc.Dependencies, func(i, j int) bool {
return seq.Compare(nc.Dependencies[i], nc.Dependencies[j]) < 0
})

return &nc
}

// shouldUpgrade returns true if the current running values or chart
// don't match what the repo says we ought to be running, based on
// doing a dry run install from the chart in the git repo.
Expand All @@ -317,8 +366,8 @@ func (chs *ChartChangeSync) shouldUpgrade(chartsRepo string, currRel *hapi_relea
return false, fmt.Errorf("No Chart release provided for %v", fhr.GetName())
}

currVals := currRel.GetConfig().GetRaw()
currChart := currRel.GetChart().String()
currVals := currRel.GetConfig()
currChart := currRel.GetChart()

// Get the desired release state
opts := release.InstallOptions{DryRun: true}
Expand All @@ -327,16 +376,25 @@ func (chs *ChartChangeSync) shouldUpgrade(chartsRepo string, currRel *hapi_relea
if err != nil {
return false, err
}
desVals := desRel.GetConfig().GetRaw()
desChart := desRel.GetChart().String()
desVals := desRel.GetConfig()
desChart := desRel.GetChart()

// compare values && Chart
if currVals != desVals {
chs.logger.Log("error", fmt.Sprintf("Release %s: values have diverged due to manual Chart release", currRel.GetName()))
if diff := cmp.Diff(currVals, desVals); diff != "" {
if chs.logDiffs {
chs.logger.Log("error", fmt.Sprintf("Release %s: values have diverged due to manual Chart release", currRel.GetName()), "diff", diff)
} else {
chs.logger.Log("error", fmt.Sprintf("Release %s: values have diverged due to manual Chart release", currRel.GetName()))
}
return true, nil
}
if currChart != desChart {
chs.logger.Log("error", fmt.Sprintf("Release %s: Chart has diverged due to manual Chart release", currRel.GetName()))

if diff := cmp.Diff(sortChartFields(currChart), sortChartFields(desChart)); diff != "" {
if chs.logDiffs {
chs.logger.Log("error", fmt.Sprintf("Release %s: Chart has diverged due to manual Chart release", currRel.GetName()), "diff", diff)
} else {
chs.logger.Log("error", fmt.Sprintf("Release %s: Chart has diverged due to manual Chart release", currRel.GetName()))
}
return true, nil
}

Expand Down
41 changes: 11 additions & 30 deletions integrations/helm/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/go-kit/kit/log"
"github.com/golang/glog"
"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/runtime"
Expand Down Expand Up @@ -51,7 +52,8 @@ const (

// Controller is the operator implementation for FluxHelmRelease resources
type Controller struct {
logger log.Logger
logger log.Logger
logDiffs bool

fhrLister iflister.FluxHelmReleaseLister
fhrSynced cache.InformerSynced
Expand All @@ -74,6 +76,7 @@ type Controller struct {
// New returns a new helm-operator
func New(
logger log.Logger,
logReleaseDiffs bool,
kubeclientset kubernetes.Interface,
fhrInformer fhrv1.FluxHelmReleaseInformer,
release *chartrelease.Release,
Expand All @@ -89,6 +92,7 @@ func New(

controller := &Controller{
logger: logger,
logDiffs: logReleaseDiffs,
fhrLister: fhrInformer.Lister(),
fhrSynced: fhrInformer.Informer().HasSynced,
release: release,
Expand Down Expand Up @@ -327,9 +331,13 @@ func (c *Controller) enqueueUpateJob(old, new interface{}) {
return
}

if needsUpdate(oldFhr, newFhr) {
if diff := cmp.Diff(oldFhr.Spec, newFhr.Spec); diff != "" {
c.logger.Log("info", "UPGRADING release")
c.logger.Log("info", "Custom Resource driven release upgrade")
if c.logDiffs {
c.logger.Log("info", "Custom Resource driven release upgrade", "diff", diff)
} else {
c.logger.Log("info", "Custom Resource driven release upgrade")
}
c.enqueueJob(new)
}
}
Expand All @@ -344,30 +352,3 @@ func (c *Controller) deleteRelease(fhr ifv1.FluxHelmRelease) {
}
return
}

// needsUpdate compares two FluxHelmRelease and determines if any changes occurred
func needsUpdate(old, new ifv1.FluxHelmRelease) bool {
oldValues, err := old.Spec.Values.YAML()
if err != nil {
return false
}

newValues, err := new.Spec.Values.YAML()
if err != nil {
return false
}

if oldValues != newValues {
return true
}

if old.Spec.ReleaseName != new.Spec.ReleaseName {
return true
}

if old.Spec.ChartGitPath != new.Spec.ChartGitPath {
return true
}

return false
}