diff --git a/.circleci/config.yml b/.circleci/config.yml index 3dbb62a2f..b3338e831 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -163,7 +163,7 @@ workflows: - build: filters: tags: - only: /(helm-)?[0-9]+(\.[0-9]+)*(-[a-z0-9]+)?/ + only: /[0-9]+(\.[0-9]+)*(-[a-z0-9]+)?/ release-helm: jobs: diff --git a/CHANGELOG-helmop.md b/CHANGELOG-helmop.md deleted file mode 100644 index 4b813444c..000000000 --- a/CHANGELOG-helmop.md +++ /dev/null @@ -1,543 +0,0 @@ -## 0.10.1 (2019-08-07) - -> **Notice:** this release contains a `HelmRelease` -> [Custom Resource Definition][helm 0.10.1 crd] fix. Please make sure -> you patch the CRD in your cluster. - -### Bug fixes - - - Fixed `rollback.timeout` definition in the `CustomResourceDefinition` - [weaveworks/flux#2251][#2251] - - Fixed the merge of values - [weaveworks/flux#2292][#2292] - - Correct spelling of integrations, and fix `make check-generated` - [weaveworks/flux#2312][#2312] - - Moved successful chart fetch signal to reconcile action (to prevent - an infinite loop due to the `LastUpdateTime` on the condition getting - accidentally updated during rollback checks). - [weaveworks/flux#2316][#2316] - - Fixed typo in `ReasonUpgradeFailed` condition change reason - [weaveworks/flux#2317][#2317] - -### Thanks - -This release was made possible by contributions from @jfrndz, @adrian, -@stefanprodan, @obiesmans, @chriscorn-takt, @sureshamk, @dholbach, -@squaremo, and @hiddeco. - -[#2251]: https://github.com/fluxcd/flux/pull/2251 -[#2292]: https://github.com/fluxcd/flux/pull/2292 -[#2312]: https://github.com/fluxcd/flux/pull/2312 -[#2316]: https://github.com/fluxcd/flux/pull/2316 -[#2317]: https://github.com/fluxcd/flux/pull/2317 -[helm 0.10.1 crd]: https://github.com/weaveworks/flux/blob/release/helm-0.10.x/deploy-helm/flux-helm-release-crd.yaml - -## 0.10.0 (2019-07-10) - -This release brings you [opt-in automated rollback support][rollback docs], -new Prometheus metrics, and _experimental_ support of spawning -multiple workers with the `--workers=` flag to speed up the -processing of releases. - -This will likely also be the last _minor_ beta release before we -promote the Helm operator to its first GA `1.0.0` release. - -> **Notice:** the Helm operator relies on changes in the `HelmRelease` -> [Custom Resource Definition][helm 0.10.0 crd]. Please make sure you patch the -> CRD in your cluster _before_ upgrading the Helm operator. - -### Bug fixes - - - Prevent an infinite release loop when multiple `HelmRelease` - resources with the same release name configuration coexist, - by looking at the antecedent annotation set on release resources - and confirming ownership - [weaveworks/flux#2123][#2123] - -### Improvements - - - Opt-in automated rollback support; when enabled, a failed release - will be rolled back automatically and the operator will not attempt - a new release until it detects a change in the chart and/or the - configured values - [weaveworks/flux#2006][#2006] - - Increase timeout for annotating resources from a Helm release, to - cope with large umbrella charts - [weaveworks/flux#2123][#2123] - - New Prometheus metrics - - + `release_queue_length_count` - + `release_duration_seconds{action=['INSTALL','UPGRADE'], dry-run=['true', 'false'], success=['true','false'], namespace, releasename}` - - [weaveworks/flux#2191][#2191] - - Experimental support of spawning multiple queue workers processing - releases by configuring the `--workers=` flag - [weaveworks/flux#2194][#2194] - -### Maintenance and documentation - - - Publish images to [fluxcd DockerHub][] organization - [weaveworks/flux#2213][#2213] - - Document opt-in rollback feature - [weaveworks/flux#2220][#2220] - -### Thanks - -Many thanks to @adrian, @2opremio, @semyonslepov, @gtseres, @squaremo, @stefanprodan, @kingdonb, @ncabatoff, -@dholbach, @cristian-radu, @simonmacklin, @hiddeco for contributing to this release. - -[#2006]: https://github.com/weaveworks/flux/pull/2006 -[#2123]: https://github.com/weaveworks/flux/pull/2123 -[#2191]: https://github.com/weaveworks/flux/pull/2191 -[#2194]: https://github.com/weaveworks/flux/pull/2194 -[#2213]: https://github.com/weaveworks/flux/pull/2213 -[#2220]: https://github.com/weaveworks/flux/pull/2220 -[helm 0.10.0 crd]: https://github.com/weaveworks/flux/blob/release/helm-0.10.x/deploy-helm/flux-helm-release-crd.yaml -[rollback docs]: https://github.com/weaveworks/flux/blob/release/helm-0.10.x/site/helm-integration.md#rollbacks -[fluxcd DockerHub]: https://hub.docker.com/r/weaveworks/helm-operator/ - -## 0.9.2 (2019-06-13) - -### Bug fixes - - - Ensure releases are enqueued on clone change only - [weaveworks/flux#2081][#2081] - - Reorder start of processes on boot and verify informer cache sync - early, to prevent the operator from hanging on boot - [weaveworks/flux#2103][#2103] - - Use openssh-client rather than openssh in container image - [weaveworks/flux#2142][#2142] - -### Improvements - - - Enable pprof to ease profiling - [weaveworks/flux#2095][#2095] - -### Maintenance and documentation - - - Add notes about production setup Tiller - [weaveworks/flux#2146][#2146] - -### Thanks - -Thanks @2opremio, @willholley ,@runningman84, @stefanprodan, @squaremo, -@rossf7, @hiddeco for contributing. - -[#2081]: https://github.com/weaveworks/flux/pull/2081 -[#2095]: https://github.com/weaveworks/flux/pull/2095 -[#2103]: https://github.com/weaveworks/flux/pull/2103 -[#2142]: https://github.com/weaveworks/flux/pull/2142 -[#2146]: https://github.com/weaveworks/flux/pull/2146 - -## 0.9.1 (2019-05-09) - -### Bug fixes - - - During the lookup of `HelmRelease`s for a mirror, ensure the - resource has a git chart source before comparing the mirror name - [weaveworks/flux#2027][#2027] - -### Thanks - -Thanks to @puzza007, @squaremo, @2opremio, @stefanprodan, @hiddeco -for reporting the issue, patching and reviewing it. - -[#2027]: https://github.com/weaveworks/flux/pull/2027 - -## 0.9.0 (2019-05-08) - -### Bug fixes - - - Make sure client-go logs to stderr - [weaveworks/flux#1945][#1945] - - Prevent garbage collected `HelmRelease`s from getting upgraded - [weaveworks/flux#1906][#1906] - -### Improvements - - - Enqueue release update on git chart source changes and improve - mirror change calculations - [weaveworks/flux#1906][#1906], [weaveworks/flux#2005][#2005] - - The operator now checks if the `HelmRelease` spec has changed after - it performed a dry-run, this prevents scenarios where it could - enroll an older revision of a `HelmRelease` while a newer version - was already known - [weaveworks/flux#1906][#1906] - - Stop logging broadcasted Kubernetes events - [weaveworks/flux#1906][#1906] - - Log and return early if release is not upgradable - [weaveworks/flux#2008][#2008] - -### Maintenance and documentation - - - Update client-go to `v1.11` - [weaveworks/flux#1929][#1929] - - Move images to DockerHub and have a separate pre-releases image repo - [weaveworks/flux#1949][#1949], [weaveworks/flux#1956][#1956] - - Support `arm` and `arm64` builds - [weaveworks/flux#1950][#1950] - - Retry keyscan when building images, to mitigate for occasional - timeouts - [weaveworks/flux#1971][#1971] - -### Thanks - -Thanks @brezerk, @jpds, @stefanprodan, @2opremio, @hiddeco, @squaremo, -@dholbach, @bboreham, @bricef and @stevenpall for their contributions -to this release, and anyone who I have missed during this manual -labour. - -[#1906]: https://github.com/weaveworks/flux/pull/1906 -[#1929]: https://github.com/weaveworks/flux/pull/1929 -[#1945]: https://github.com/weaveworks/flux/pull/1945 -[#1949]: https://github.com/weaveworks/flux/pull/1949 -[#1950]: https://github.com/weaveworks/flux/pull/1950 -[#1956]: https://github.com/weaveworks/flux/pull/1956 -[#1971]: https://github.com/weaveworks/flux/pull/1971 -[#2005]: https://github.com/weaveworks/flux/pull/2005 -[#2008]: https://github.com/weaveworks/flux/pull/2008 - -## 0.8.0 (2019-04-11) - -This release bumps the Helm API package and binary to `v2.13.0`; -although we have tested and found it to be backwards compatible, we -recommend running Tiller `>=2.13.0` from now on. - -### Improvements - - - Detect changes made to git chart source in `HelmRelease` - [weaveworks/flux#1865][#1865] - - Cleanup git chart source clone on `HelmRelease` removal - [weaveworks/flux#1865][#1865] - - Add `chartFileRef` option to `valuesFrom` to support using a - non-default values yamel from a git-sourced Helm chart - [weaveworks#1909][#1909] - - Reimplement `--git-poll-interval` to control polling interval of - git mirrors for chart sources - [weaveworks/flux#1910][#1910] - -### Maintenance and documentation - - - Bump Helm API package and binary to `v2.13.0` - [weaveworks/flux#1828][#1828] - - Verify scanned keys in same build step as scan - [weaveworks/flux#1908][#1908] - - Use Helm operator image from build in e2e tests - [weaveworks/flux#1910][#1910] - -### Thanks - -Thanks to @hpurmann, @2opremio, @arturo-c, @squaremo, @stefanprodan, -@hiddeco, and others for their contributions to this release, feedback, -and bringing us one step closer to a GA-release. - -[#1828]: https://github.com/weaveworks/flux/pull/1828 -[#1865]: https://github.com/weaveworks/flux/pull/1865 -[#1908]: https://github.com/weaveworks/flux/pull/1908 -[#1909]: https://github.com/weaveworks/flux/pull/1909 -[#1910]: https://github.com/weaveworks/flux/pull/1910 - -## 0.7.1 (2019-03-27) - -### Bug fixes - - - Prevent panic on `.spec.values` in `HelmRelease` due to merge - attempt on uninitialized value - [weaveworks/flux#1867](https://github.com/weaveworks/flux/pull/1867) - -## 0.7.0 (2019-03-25) - -### Bug fixes - - - Run signal listener in a goroutine instead of deferring - [weaveworks/flux#1680](https://github.com/weaveworks/flux/pull/1680) - - Make chart operations insensitive to (missing) slashes in Helm - repository URLs - [weaveworks/flux#1735](https://github.com/weaveworks/flux/pull/1735) - - Annotating resources outside of the `HelmRelease` namespace - [weaveworks/flux#1757](https://github.com/weaveworks/flux/pull/1757) - -### Improvements - - - The `HelmRelease` CRD now supports a `skipDepUpdate` to instruct the - operator to not update dependencies for charts from a git source - [weaveworks/flux#1712](https://github.com/weaveworks/flux/pull/1712) - [weaveworks/flux#1823](https://github.com/weaveworks/flux/pull/1823) - - Azure DevOps Git host support - [weaveworks/flux#1729](https://github.com/weaveworks/flux/pull/1729) - - The UID of the `HelmRelease` is now used as dry run release name - [weaveworks/flux#1745](https://github.com/weaveworks/flux/pull/1745) - - Removed deprecated `--git-poll-interval` flag - [weaveworks/flux#1757](https://github.com/weaveworks/flux/pull/1757) - - Sync hook to instruct the operator to refresh Git mirrors - [weaveworks/flux#1776](https://github.com/weaveworks/flux/pull/1776) - - Docker image is now based on Alpine `3.9` - [weaveworks/flux#1801](https://github.com/weaveworks/flux/pull/1801) - - `.spec.values` in the `HelmRelease` CRD is no longer mandatory - [weaveworks/flux#1824](https://github.com/weaveworks/flux/pull/1824) - - With `valuesFrom` it is now possible to load values from secrets, - config maps and URLs - [weaveworks/flux#1836](https://github.com/weaveworks/flux/pull/1836) - -### Thanks - -Thanks to @captncraig, @2opremio, @squaremo, @hiddeco, @endrec, @ahmadiq, -@nmaupu, @samisq, @yinzara, @stefanprodan, and @sarath-p for their -contributions. - -## 0.6.0 (2019-02-07) - -### Improvements - - - Add option to limit the Helm operator to a single namespace - [weaveworks/flux#1664](https://github.com/weaveworks/flux/pull/1664) - -### Thanks - -Without the contributions of @brandon-bethke-neudesic, @errordeveloper, -@ncabatoff, @stefanprodan, @squaremo, and feedback of our -[#flux](https://slack.weave.works/) inhabitants this release would not -have been possible -- thanks to all of you! - -## 0.5.3 (2019-01-14) - -### Improvements - - - `HelmRelease` now has a `resetValues` field which when set to `true` - resets the values to the ones built into the chart - [weaveworks/flux#1628](https://github.com/weaveworks/flux/pull/1628) - - The operator now exposes a HTTP webserver (by default on port - `:3030`) with Prometheus metrics on `/metrics` and a health check - endpoint on `/healthz` - [weaveworks/flux#1653](https://github.com/weaveworks/flux/pull/1653) - -### Thanks - -A thousand thanks to @davidkarlsen, @hiddeco, @ncabatoff, @stefanprodan, -@squaremo and others for their contributions leading to this release. - -## 0.5.2 (2018-12-20) - -### Bug fixes - - - Respect proxy env entries for git operations - [weaveworks/flux#1556](https://github.com/weaveworks/flux/pull/1556) - - Reimplement git timeout after accidentally removing it in `0.5.0` - [weaveworks/flux#1565](https://github.com/weaveworks/flux/pull/1565) - - Mark `--git-poll-interval` flag as deprecated - [weaveworks/flux#1565](https://github.com/weaveworks/flux/pull/1565) - - Only update chart dependencies if a `requirements.yaml` exists - weaveworks/flux{[#1561](https://github.com/weaveworks/flux/pull/1561), [#1606](https://github.com/weaveworks/flux/pull/1606)} - -### Improvements - - - `HelmRelease` now has a `timeout` field (defaults to `300s`), - giving you control over the amount of time it may take for Helm to - install or upgrade your chart - [weaveworks/flux#1566](https://github.com/weaveworks/flux/pull/1566) - - The Helm operator [flag docs](./site/helm-operator.md#setup-and-configuration) - have been updated - [weaveworks/flux#1594](https://github.com/weaveworks/flux/pull/1594) - - Added tests to ensure Helm dependencies update behaviour is always as - expected - [weaveworks/flux#1562](https://github.com/weaveworks/flux/pull/1562) - -### Thanks - -Thanks to @stephenmoloney, @sfrique, @mgazza, @stefanprodan, @squaremo, -@rade and @hiddeco for their contributions. - -## 0.5.1 (2018-11-21) - -### Bug fixes - - - Helm releases will now stay put when an upgrade fails or the - Kubernetes API connectivity is flaky, instead of getting purged - [weaveworks/flux#1530](https://github.com/weaveworks/flux/pull/1530) - -### Thanks - -Thanks to @sfrique, @brantb and @squaremo for helping document the -issues leading to this bug fix, @stefanprodan for actually squashing -the bug and all others that may have gone unnoticed while writing this -release note. - -## 0.5.0 (2018-11-14) - -WARNING: this release of the Helm operator is not backward-compatible: - - - It uses a new custom resource `HelmRelease`, and will ignore - `FluxHelmRelease` resources - - Some command-line arguments have changed, so the [deployment - manifests](./deploy-helm/) must also be updated - -To use it, you will need to migrate custom resources to the new format -supported by this version. See the [upgrade -guide](./site/helm-upgrading-to-beta.md). - -This version of the Helm operator supports HelmRelease custom -resources, which each specify a chart and values to use in a Helm -release, as in previous versions. The main improvement is that you are -now able to specify charts from Helm repos, as well as from git repo, -per resource (rather than a single git repo, which is supplied to the -operator). - -### Improvements - -All of these were added in -[weaveworks/flux#1382](https://github.com/weaveworks/flux/pull/1382). - -See the [Helm operator guide](./site/helm-integration.md) for details. - - - You can now release charts from arbitrary Helm repos - - You can now release charts from arbitrary git repos - -### Thanks - -Thanks to @demikl, @dholbach, @hiddeco, @mellana1, @squaremo, -@stefanprodan, @stephenmoloney, @whereismyjetpack and others who made -suggestions, logged problems, and tried out nightly builds. - -## 0.4.0 (2018-11-01) - -This release improves support for TLS connections to Tiller; in -particular it makes it much easier to get server certificate -verification (`--tiller-tls-verify`) to work. - -It also adds the ability to supply additional values to -`FluxHelmRelease` resources by attaching Kubernetes secrets. This -helps with a few use cases: - - - supplying the same default values to several releases - - providing secrets (e.g., a password) to a chart that expects them as values - - using values files without inlining them into FluxHelmReleases - -**NB** It is advised that you deploy the operator alongside Tiller -v2.10 or more recent. To properly support TLS, the operator now -includes code from Helm v2.10, and this may have difficulty connecting -to older versions of Tiller. - -### Bug fixes - - - Make `--tiller-tls-verify` work as intended, by giving better - instructions, and adding the argument `--tiller-tls-hostname` which - lets you specify the hostname that TLS should expect in the - certificate - [weaveworks/flux#1484](https://github.com/weaveworks/flux/pull/1484) - -### Improvements - - - You can now create secrets containing a `values.yaml` file, and - attach them to a `FluxHelmRelease` as additional values to use - [weaveworks/flux#1468](https://github.com/weaveworks/flux/pull/1468) - -### Thanks - -Thanks to @hiddeco, @Smirl, @stefanprodan, @arthurk, @the-fine, -@wstrange, @sfitts, @squaremo, @mpareja, @stephenmoloney, -@justinbarrick, @pcfens for contributions to the PRs and issues -leading to this release, as well as the inhabitants of -[#flux](https://slack.weave.works/) for high-quality, helpful -discussion. - -## 0.3.0 (2018-10-24) - -This release adds dependency handling to the Helm operator. - -**NB** The helm operator will now update dependencies for charts _by -default_, which means you no longer need to vendor them. You can -switch this behaviour off with the flag `--update-chart-deps=false`. - -### Bug fixes - - - Improve chance of graceful shutdown - [weaveworks/flux#1439](https://github.com/weaveworks/flux/pull/1439) - and - [weaveworks/flux#1438](https://github.com/weaveworks/flux/pull/1438) - -### Improvements - - - The operator now runs `helm dep build` for charts before installing - or upgrading releases. This will use a lockfile if present, and - update the dependencies according to `requirements.yaml` otherwise - [weaveworks/flux#1450](https://github.com/weaveworks/flux/pull/1450) - - A new flag `--git-timeout` controls how long the Helm operator will - allow for git operations - [weaveworks/flux#1416](https://github.com/weaveworks/flux/pull/1416) - - The Helm operator image now includes the Helm command-line client, - which makes it easier to troubleshoot problems using `kubectl exec` - (as part of - [weaveworks/flux#1450](https://github.com/weaveworks/flux/pull/1450)) - -## 0.2.1 (2018-09-17) - -This is a patch release that allows helm-op to recover from a failed release install. -If a chart is broken, Tiller will reserve the name and mark the release as failed. -If at a later time the chart is fixed, helm-op can't install it anymore because the release name is in use. -Purging the release after each failed install allows helm-op to keep retrying the install. - -- Purge release if install fails - [weaveworks/flux#1344](https://github.com/weaveworks/flux/pull/1344) - -## 0.2.0 (2018-08-23) - -In large part this release simplifies and improves the Helm operator -machinery, without changing its effect. - -This release drops the `-alpha` suffix, but remains <1.0 and should -(still) be considered unready for production use. - -- Use the same git implementation as fluxd, fixing a number of - problems with SSH known_hosts and git URLs and so on - [weaveworks/flux#1240](https://github.com/weaveworks/flux/pull/1240) -- Always check that a chart release will be a change, before releasing - [weaveworks/flux#1254](https://github.com/weaveworks/flux/pull/1254) -- Add validation to the FluxHelmRelease custom resource definition, - giving the kind the short name `fhr` - [weaveworks/flux#1253](https://github.com/weaveworks/flux/pull/1253) -- Detect chart release differences more reliably - [weaveworks/flux#1272](https://github.com/weaveworks/flux/pull/1272) -- Check for more recent versions and report in logs when out of date - [weaveworks/flux#1276](https://github.com/weaveworks/flux/pull/1276) - -See [getting started with -Helm](https://github.com/weaveworks/flux/blob/master/site/helm/get-started.md) -and the [Helm chart -instructions](https://github.com/weaveworks/flux/blob/master/chart/flux/README.md) -for information on installing the Flux with the Helm operator. - -## 0.1.1-alpha (2018-07-16) - -- Support using TLS connections to Tiller - [weaveworks/flux#1200](https://github.com/weaveworks/flux/pull/1200) -- Avoid continual, spurious installs in newer Kubernetes - [weaveworks/flux#1193](https://github.com/weaveworks/flux/pull/1193) -- Make it easier to override SSH config (and `known_hosts`) - [weaveworks/flux#1188](https://github.com/weaveworks/flux/pull/1188) -- Annotate resources created by a Helm release with the name of the - FluxHelmRelease custom resource, so they can be linked - [weaveworks/flux#1134](https://github.com/weaveworks/flux/pull/1134) -- Purge release when FluxHelmRelease is deleted, so restoring the - resource can succeed - [weaveworks/flux#1106](https://github.com/weaveworks/flux/pull/1106) -- Correct permissions on baked-in SSH config - [weaveworks/flux#1098](https://github.com/weaveworks/flux/pull/1098) -- Test coverage for releasesync package - [weaveworks/flux#1089](https://github.com/weaveworks/flux/pull/1089)). - -It is now possible to install Flux and the Helm operator using the -[helm chart in this -repository](https://github.com/weaveworks/flux/tree/master/chart/flux). - -## 0.1.0-alpha (2018-05-01) - -First versioned release of the Flux Helm operator. The target features are: - -- release Helm charts as specified in FluxHelmRelease resources - - these refer to charts in a single git repo, readable by the operator - - update releases when either the FluxHelmRelease resource or the - chart (in git) changes - -See -https://github.com/weaveworks/flux/blob/helm-0.1.0-alpha/site/helm/ -for more detailed explanations. diff --git a/CHANGELOG.md b/CHANGELOG.md index f92ce1f45..67a1298a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,3 @@ -This is the changelog for the Flux daemon; the changelog for the Helm -operator is in [./CHANGELOG-helmop.md](./CHANGELOG-helmop.md). - ## 1.13.3 (2019-07-25) This is a patch release, mostly concerned with adapting documentation diff --git a/Makefile b/Makefile index 55b555f66..e76ca0227 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,6 @@ godeps=$(shell go list -deps -f '{{if not .Standard}}{{ $$dep := . }}{{range .Go FLUXD_DEPS:=$(call godeps,./cmd/fluxd/...) FLUXCTL_DEPS:=$(call godeps,./cmd/fluxctl/...) -HELM_OPERATOR_DEPS:=$(call godeps,./cmd/helm-operator/...) IMAGE_TAG:=$(shell ./docker/image-tag) VCS_REF:=$(shell git rev-parse HEAD) @@ -30,7 +29,7 @@ BUILD_DATE:=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') DOCS_PORT:=8000 -all: $(GOBIN)/fluxctl $(GOBIN)/fluxd $(GOBIN)/helm-operator build/.flux.done build/.helm-operator.done +all: $(GOBIN)/fluxctl $(GOBIN)/fluxd build/.flux.done release-bins: for arch in amd64; do \ @@ -55,7 +54,7 @@ realclean: clean test: test/bin/helm test/bin/kubectl test/bin/kustomize PATH="${PWD}/bin:${PWD}/test/bin:${PATH}" go test ${TEST_FLAGS} $(shell go list ./... | grep -v "^github.com/weaveworks/flux/vendor" | sort -u) -e2e: test/bin/helm test/bin/kubectl build/.flux.done build/.helm-operator.done +e2e: test/bin/helm test/bin/kubectl build/.flux.done PATH="${PWD}/test/bin:${PATH}" CURRENT_OS_ARCH=$(CURRENT_OS_ARCH) test/e2e/run.sh build/.%.done: docker/Dockerfile.% @@ -68,16 +67,11 @@ build/.%.done: docker/Dockerfile.% touch $@ build/.flux.done: build/fluxd build/kubectl build/kustomize docker/ssh_config docker/kubeconfig docker/known_hosts.sh -build/.helm-operator.done: build/helm-operator build/kubectl build/helm docker/ssh_config docker/known_hosts.sh docker/helm-repositories.yaml build/fluxd: $(FLUXD_DEPS) build/fluxd: cmd/fluxd/*.go CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} go build -o $@ $(LDFLAGS) -ldflags "-X main.version=$(shell ./docker/image-tag)" ./cmd/fluxd -build/helm-operator: $(HELM_OPERATOR_DEPS) -build/helm-operator: cmd/helm-operator/*.go - CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} go build -o $@ $(LDFLAGS) -ldflags "-X main.version=$(shell ./docker/image-tag)" ./cmd/helm-operator - build/kubectl: cache/linux-$(ARCH)/kubectl-$(KUBECTL_VERSION) test/bin/kubectl: cache/$(CURRENT_OS_ARCH)/kubectl-$(KUBECTL_VERSION) build/helm: cache/linux-$(ARCH)/helm-$(HELM_VERSION) @@ -116,9 +110,6 @@ $(GOBIN)/fluxctl: $(FLUXCTL_DEPS) $(GOBIN)/fluxd: $(FLUXD_DEPS) go install ./cmd/fluxd -$(GOBIN)/helm-operator: $(HELM_OPERATOR_DEPS) - go install ./cmd/helm-operator - integration-test: all test/bin/test-flux @@ -131,7 +122,6 @@ install/generated_templates.gogen.go: install/templates/* cd install && go run generate.go embedded-templates check-generated: generate-deploy install/generated_templates.gogen.go - ./bin/helm/update_codegen.sh git diff --exit-code -- integrations/apis integrations/client install/generated_templates.gogen.go build-docs: diff --git a/README.md b/README.md index c476122eb..4badf839e 100644 --- a/README.md +++ b/README.md @@ -85,9 +85,16 @@ could easily integrate. ## Get started with Flux -Get started [installing Flux](https://docs.fluxcd.io/en/latest/get-started/) +With the following tutorials: + +- [Get started with Flux](https://docs.fluxcd.io/en/latest/tutorials/get-started.html) +- [Get started with Flux using Helm](https://docs.fluxcd.io/en/latest/tutorials/get-started-helm.html) + or just [browse through the documentation](https://docs.fluxcd.io). +Do you want to release your Helm charts in a declarative way? +Take a look at the [`fluxcd/helm-operator`](https://github.com/fluxcd/helm-operator). + ### Integrations As Flux is Open Source, integrations are very straight-forward. Here are @@ -99,11 +106,6 @@ a few popular ones you might want to check out: - [GitOps for Istio Canary deployments](https://github.com/stefanprodan/gitops-istio) - [Fluxcloud to receive events from Flux](https://github.com/justinbarrick/fluxcloud) -## Get started with the Helm operator - -Get started [installing the Helm operator](https://docs.fluxcd.io/en/latest/helm-operator/tutorials/get-started.html) -or just [browse through the documentation](https://docs.fluxcd.io/en/latest/helm-operator/). - ## Community & Developer information We welcome all kinds of contributions to Flux, be it code, issues you found, diff --git a/bin/helm/custom-boilerplate.go.txt b/bin/helm/custom-boilerplate.go.txt deleted file mode 100644 index 515584866..000000000 --- a/bin/helm/custom-boilerplate.go.txt +++ /dev/null @@ -1,15 +0,0 @@ -/* -Copyright 2018 Weaveworks Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ diff --git a/bin/helm/update_codegen.sh b/bin/helm/update_codegen.sh deleted file mode 100755 index db8e75b6d..000000000 --- a/bin/helm/update_codegen.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o nounset -set -o pipefail - -# This corresponds to the tag kubernetes-1.14.4, and to the pinned version in go.mod -CODEGEN_VERSION="code-generator@v0.0.0-20190311093542-50b561225d70" -SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/../.. - -# Chosen to line up with the ./build directory used in the Makefile -CODEGEN_ROOT=./build/codegen -# make sure this exists ... -mkdir -p ./build -# ... but not this -rm -rf ${CODEGEN_ROOT} - -echo Using codegen in ${CODEGEN_ROOT} - -export GO111MODULE=on -# make sure the codegen module has been fetched -go mod download -cp -R $(echo `go env GOPATH`)'/pkg/mod/k8s.io/'${CODEGEN_VERSION} ${CODEGEN_ROOT} -chmod -R u+w ${CODEGEN_ROOT} - -bash "${CODEGEN_ROOT}/generate-groups.sh" all github.com/weaveworks/flux/integrations/client \ - github.com/weaveworks/flux/integrations/apis \ - "flux.weave.works:v1beta1 helm.integrations.flux.weave.works:v1alpha2" \ - --go-header-file "${SCRIPT_ROOT}/bin/helm/custom-boilerplate.go.txt" diff --git a/cluster/kubernetes/kubernetes.go b/cluster/kubernetes/kubernetes.go index 6547f4b91..5ec90e3d6 100644 --- a/cluster/kubernetes/kubernetes.go +++ b/cluster/kubernetes/kubernetes.go @@ -7,6 +7,7 @@ import ( "fmt" "sync" + hrclient "github.com/fluxcd/helm-operator/pkg/client/clientset/versioned" "github.com/go-kit/kit/log" "github.com/pkg/errors" "gopkg.in/yaml.v2" @@ -20,28 +21,33 @@ import ( "github.com/weaveworks/flux/cluster" kresource "github.com/weaveworks/flux/cluster/kubernetes/resource" fhrclient "github.com/weaveworks/flux/integrations/client/clientset/versioned" - "github.com/weaveworks/flux/ssh" "github.com/weaveworks/flux/resource" + "github.com/weaveworks/flux/ssh" ) type coreClient k8sclient.Interface type dynamicClient k8sclientdynamic.Interface type fluxHelmClient fhrclient.Interface +type helmOperatorClient hrclient.Interface type discoveryClient discovery.DiscoveryInterface type ExtendedClient struct { coreClient dynamicClient fluxHelmClient + helmOperatorClient discoveryClient } -func MakeClusterClientset(core coreClient, dyn dynamicClient, fluxhelm fluxHelmClient, disco discoveryClient) ExtendedClient { +func MakeClusterClientset(core coreClient, dyn dynamicClient, fluxhelm fluxHelmClient, + helmop helmOperatorClient, disco discoveryClient) ExtendedClient { + return ExtendedClient{ - coreClient: core, - dynamicClient: dyn, - fluxHelmClient: fluxhelm, - discoveryClient: disco, + coreClient: core, + dynamicClient: dyn, + fluxHelmClient: fluxhelm, + helmOperatorClient: helmop, + discoveryClient: disco, } } diff --git a/cluster/kubernetes/resource/fluxhelmrelease.go b/cluster/kubernetes/resource/helmrelease.go similarity index 86% rename from cluster/kubernetes/resource/fluxhelmrelease.go rename to cluster/kubernetes/resource/helmrelease.go index 51a69b5b0..beaae3888 100644 --- a/cluster/kubernetes/resource/fluxhelmrelease.go +++ b/cluster/kubernetes/resource/helmrelease.go @@ -9,7 +9,7 @@ import ( ) // ReleaseContainerName is the name used when Flux interprets a -// FluxHelmRelease as having a container with an image, by virtue of +// HelmRelease as having a container with an image, by virtue of // having a `values` stanza with an image field: // // spec: @@ -20,11 +20,11 @@ import ( // The name refers to the source of the image value. const ReleaseContainerName = "chart-image" -// FluxHelmRelease echoes the generated type for the custom resource +// HelmRelease echoes the generated type for the custom resource // definition. It's here so we can 1. get `baseObject` in there, and // 3. control the YAML serialisation of fields, which we can't do // (easily?) with the generated type. -type FluxHelmRelease struct { +type HelmRelease struct { baseObject Spec struct { Values map[string]interface{} @@ -47,13 +47,13 @@ func sorted_keys(values map[string]interface{}) []string { return keys } -// FindFluxHelmReleaseContainers examines the Values from a -// FluxHelmRelease (manifest, or cluster resource, or otherwise) and +// FindHelmReleaseContainers examines the Values from a +// HelmRelease (manifest, or cluster resource, or otherwise) and // calls visit with each container name and image it finds, as well as // procedure for changing the image value. It will return an error if // it cannot interpret the values as specifying images, or if the // `visit` function itself returns an error. -func FindFluxHelmReleaseContainers(values map[string]interface{}, visit func(string, image.Ref, ImageSetter) error) error { +func FindHelmReleaseContainers(values map[string]interface{}, visit func(string, image.Ref, ImageSetter) error) error { // an image defined at the top-level is given a standard container name: if image, setter, ok := interpretAsContainer(stringMap(values)); ok { visit(ReleaseContainerName, image, setter) @@ -70,7 +70,7 @@ func FindFluxHelmReleaseContainers(values map[string]interface{}, visit func(str } // The following is some machinery for interpreting a -// FluxHelmRelease's `values` field as defining images to be +// HelmRelease's `values` field as defining images to be // interpolated into the chart templates. // // The top-level value is a map[string]interface{}, but beneath that, @@ -221,11 +221,11 @@ func interpretAsImage(m mapper) (image.Ref, ImageSetter, bool) { } // Containers returns the containers that are defined in the -// FluxHelmRelease. -func (fhr FluxHelmRelease) Containers() []resource.Container { +// HelmRelease. +func (hr HelmRelease) Containers() []resource.Container { var containers []resource.Container // If there's an error in interpreting, return what we have. - _ = FindFluxHelmReleaseContainers(fhr.Spec.Values, func(container string, image image.Ref, _ ImageSetter) error { + _ = FindHelmReleaseContainers(hr.Spec.Values, func(container string, image image.Ref, _ ImageSetter) error { containers = append(containers, resource.Container{ Name: container, Image: image, @@ -237,11 +237,11 @@ func (fhr FluxHelmRelease) Containers() []resource.Container { // SetContainerImage mutates this resource by setting the `image` // field of `values`, or a subvalue therein, per one of the -// interpretations in `FindFluxHelmReleaseContainers` above. NB we can +// interpretations in `FindHelmReleaseContainers` above. NB we can // get away with a value-typed receiver because we set a map entry. -func (fhr FluxHelmRelease) SetContainerImage(container string, ref image.Ref) error { +func (hr HelmRelease) SetContainerImage(container string, ref image.Ref) error { found := false - if err := FindFluxHelmReleaseContainers(fhr.Spec.Values, func(name string, image image.Ref, setter ImageSetter) error { + if err := FindHelmReleaseContainers(hr.Spec.Values, func(name string, image image.Ref, setter ImageSetter) error { if container == name { setter(ref) found = true @@ -251,7 +251,7 @@ func (fhr FluxHelmRelease) SetContainerImage(container string, ref image.Ref) er return err } if !found { - return fmt.Errorf("did not find container %s in FluxHelmRelease", container) + return fmt.Errorf("did not find container %s in HelmRelease", container) } return nil } diff --git a/cluster/kubernetes/resource/fluxhelmrelease_test.go b/cluster/kubernetes/resource/helmrelease_test.go similarity index 76% rename from cluster/kubernetes/resource/fluxhelmrelease_test.go rename to cluster/kubernetes/resource/helmrelease_test.go index 2ded990d0..ee424c6d7 100644 --- a/cluster/kubernetes/resource/fluxhelmrelease_test.go +++ b/cluster/kubernetes/resource/helmrelease_test.go @@ -8,17 +8,20 @@ import ( ) func TestParseImageOnlyFormat(t *testing.T) { - expectedImage := "bitnami/mariadb:10.1.30-r1" + expectedImage := "bitnami/ghost:1.21.5-r0" doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost spec: - chartGitPath: mariadb + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost values: first: post image: ` + expectedImage + ` @@ -30,16 +33,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Errorf("expected 1 container; got %#v", containers) } @@ -50,20 +53,23 @@ spec: } func TestParseImageTagFormat(t *testing.T) { - expectedImageName := "bitnami/mariadb" - expectedImageTag := "10.1.30-r1" + expectedImageName := "bitnami/ghost" + expectedImageTag := "1.21.5-r0" expectedImage := expectedImageName + ":" + expectedImageTag doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost spec: - chartGitPath: mariadb + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghostb values: first: post image: ` + expectedImageName + ` @@ -76,16 +82,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Errorf("expected 1 container; got %#v", containers) } @@ -97,18 +103,23 @@ spec: func TestParseRegistryImageTagFormat(t *testing.T) { expectedRegistry := "registry.com" - expectedImageName := "bitnami/mariadb" - expectedImageTag := "10.1.30-r1" + expectedImageName := "bitnami/ghost" + expectedImageTag := "1.21.5-r0" expectedImage := expectedRegistry + "/" + expectedImageName + ":" + expectedImageTag doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost +spec: + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost spec: chartGitPath: mariadb values: @@ -124,16 +135,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Errorf("expected 1 container; got %#v", containers) } @@ -145,17 +156,22 @@ spec: func TestParseRegistryImageFormat(t *testing.T) { expectedRegistry := "registry.com" - expectedImageName := "bitnami/mariadb:10.1.30-r1" + expectedImageName := "bitnami/ghost:1.21.5-r0" expectedImage := expectedRegistry + "/" + expectedImageName doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost +spec: + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost spec: chartGitPath: mariadb values: @@ -170,16 +186,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Errorf("expected 1 container; got %#v", containers) } @@ -191,15 +207,20 @@ spec: func TestParseNamedImageFormat(t *testing.T) { expectedContainer := "db" - expectedImage := "bitnami/mariadb:10.1.30-r1" + expectedImage := "bitnami/ghost:1.21.5-r0" doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost +spec: + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost spec: chartGitPath: mariadb values: @@ -214,16 +235,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -236,11 +257,11 @@ spec: } newImage := containers[0].Image.WithNewTag("some-other-tag") - if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + if err := hr.SetContainerImage(expectedContainer, newImage); err != nil { t.Error(err) } - containers = fhr.Containers() + containers = hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -255,18 +276,23 @@ spec: func TestParseNamedImageTagFormat(t *testing.T) { expectedContainer := "db" - expectedImageName := "bitnami/mariadb" - expectedImageTag := "10.1.30-r1" + expectedImageName := "bitnami/ghost" + expectedImageTag := "1.21.5-r0" expectedImage := expectedImageName + ":" + expectedImageTag doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost +spec: + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost spec: chartGitPath: mariadb values: @@ -284,16 +310,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -306,11 +332,11 @@ spec: } newImage := containers[0].Image.WithNewTag("some-other-tag") - if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + if err := hr.SetContainerImage(expectedContainer, newImage); err != nil { t.Error(err) } - containers = fhr.Containers() + containers = hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -326,18 +352,23 @@ spec: func TestParseNamedRegistryImageTagFormat(t *testing.T) { expectedContainer := "db" expectedRegistry := "registry.com" - expectedImageName := "bitnami/mariadb" - expectedImageTag := "10.1.30-r1" + expectedImageName := "bitnami/ghost" + expectedImageTag := "1.21.5-r0" expectedImage := expectedRegistry + "/" + expectedImageName + ":" + expectedImageTag doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost +spec: + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost spec: chartGitPath: mariadb values: @@ -356,16 +387,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -379,11 +410,11 @@ spec: newImage := containers[0].Image.WithNewTag("some-other-tag") newImage.Domain = "someotherregistry.com" - if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + if err := hr.SetContainerImage(expectedContainer, newImage); err != nil { t.Error(err) } - containers = fhr.Containers() + containers = hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -399,19 +430,22 @@ spec: func TestParseNamedRegistryImageFormat(t *testing.T) { expectedContainer := "db" expectedRegistry := "registry.com" - expectedImageName := "bitnami/mariadb:10.1.30-r1" + expectedImageName := "bitnami/ghost:1.21.5-r0" expectedImage := expectedRegistry + "/" + expectedImageName doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost spec: - chartGitPath: mariadb + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost values: other: not: "containing image" @@ -427,16 +461,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -450,11 +484,11 @@ spec: newImage := containers[0].Image.WithNewTag("some-other-tag") newImage.Domain = "someotherregistry.com" - if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + if err := hr.SetContainerImage(expectedContainer, newImage); err != nil { t.Error(err) } - containers = fhr.Containers() + containers = hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -468,20 +502,23 @@ spec: } func TestParseImageObjectFormat(t *testing.T) { - expectedImageName := "bitnami/mariadb" - expectedImageTag := "10.1.30-r1" + expectedImageName := "bitnami/ghost" + expectedImageTag := "1.21.5-r0" expectedImage := expectedImageName + ":" + expectedImageTag doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost spec: - chartGitPath: mariadb + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost values: first: post image: @@ -495,16 +532,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Errorf("expected 1 container; got %#v", containers) } @@ -516,20 +553,23 @@ spec: func TestParseNamedImageObjectFormat(t *testing.T) { expectedContainer := "db" - expectedImageName := "bitnami/mariadb" - expectedImageTag := "10.1.30-r1" + expectedImageName := "bitnami/ghost" + expectedImageTag := "1.21.5-r0" expectedImage := expectedImageName + ":" + expectedImageTag doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost spec: - chartGitPath: mariadb + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost values: other: not: "containing image" @@ -546,16 +586,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -568,11 +608,11 @@ spec: } newImage := containers[0].Image.WithNewTag("some-other-tag") - if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + if err := hr.SetContainerImage(expectedContainer, newImage); err != nil { t.Error(err) } - containers = fhr.Containers() + containers = hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -588,20 +628,23 @@ spec: func TestParseNamedImageObjectFormatWithRegistry(t *testing.T) { expectedContainer := "db" expectedRegistry := "registry.com" - expectedImageName := "bitnami/mariadb" - expectedImageTag := "10.1.30-r1" + expectedImageName := "bitnami/ghost" + expectedImageTag := "1.21.5-r0" expectedImage := expectedRegistry + "/" + expectedImageName + ":" + expectedImageTag doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost spec: - chartGitPath: mariadb + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost values: other: not: "containing image" @@ -619,16 +662,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -642,11 +685,11 @@ spec: newImage := containers[0].Image.WithNewTag("some-other-tag") newImage.Domain = "someotherregistry.com" - if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + if err := hr.SetContainerImage(expectedContainer, newImage); err != nil { t.Error(err) } - containers = fhr.Containers() + containers = hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -662,19 +705,22 @@ spec: func TestParseNamedImageObjectFormatWithRegistryWitoutTag(t *testing.T) { expectedContainer := "db" expectedRegistry := "registry.com" - expectedImageName := "bitnami/mariadb:10.1.30-r1" + expectedImageName := "bitnami/ghost:1.21.5-r0" expectedImage := expectedRegistry + "/" + expectedImageName doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: - name: mariadb - namespace: maria + name: ghost + namespace: ghost labels: - chart: mariadb + chart: ghost spec: - chartGitPath: mariadb + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost values: other: not: "containing image" @@ -691,16 +737,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["maria:fluxhelmrelease/mariadb"] + res, ok := resources["ghost:helmrelease/ghost"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -714,11 +760,11 @@ spec: newImage := containers[0].Image.WithNewTag("some-other-tag") newImage.Domain = "someotherregistry.com" - if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + if err := hr.SetContainerImage(expectedContainer, newImage); err != nil { t.Error(err) } - containers = fhr.Containers() + containers = hr.Containers() if len(containers) != 1 { t.Fatalf("expected 1 container; got %#v", containers) } @@ -753,13 +799,16 @@ func TestParseAllFormatsInOne(t *testing.T) { } doc := `--- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease metadata: name: test namespace: test spec: - chartGitPath: test + chart: + git: git@github.com:fluxcd/flux-get-started + ref: master + path: charts/ghost values: # top-level image image: ` + expected[0].image + ":" + expected[0].tag + ` @@ -802,16 +851,16 @@ spec: if err != nil { t.Fatal(err) } - res, ok := resources["test:fluxhelmrelease/test"] + res, ok := resources["test:helmrelease/test"] if !ok { t.Fatalf("expected resource not found; instead got %#v", resources) } - fhr, ok := res.(resource.Workload) + hr, ok := res.(resource.Workload) if !ok { t.Fatalf("expected resource to be a Workload, instead got %#v", res) } - containers := fhr.Containers() + containers := hr.Containers() if len(containers) != len(expected) { t.Fatalf("expected %d containers, got %d", len(expected), len(containers)) } diff --git a/cluster/kubernetes/resource/resource.go b/cluster/kubernetes/resource/resource.go index 803104274..cb8b35849 100644 --- a/cluster/kubernetes/resource/resource.go +++ b/cluster/kubernetes/resource/resource.go @@ -197,11 +197,11 @@ func unmarshalKind(base baseObject, bytes []byte) (KubeManifest, error) { unmarshalList(base, &raw, &list) return &list, nil case base.Kind == "FluxHelmRelease" || base.Kind == "HelmRelease": - var fhr = FluxHelmRelease{baseObject: base} - if err := yaml.Unmarshal(bytes, &fhr); err != nil { + var hr = HelmRelease{baseObject: base} + if err := yaml.Unmarshal(bytes, &hr); err != nil { return nil, err } - return &fhr, nil + return &hr, nil case base.Kind == "": // If there is an empty resource (due to eg an introduced comment), // we are returning nil for the resource and nil for an error diff --git a/cluster/kubernetes/resourcekinds.go b/cluster/kubernetes/resourcekinds.go index 9905b7733..e2fac6f10 100644 --- a/cluster/kubernetes/resourcekinds.go +++ b/cluster/kubernetes/resourcekinds.go @@ -4,6 +4,7 @@ import ( "context" "strings" + hr_v1 "github.com/fluxcd/helm-operator/pkg/apis/helm.fluxcd.io/v1" apiapps "k8s.io/api/apps/v1" apibatch "k8s.io/api/batch/v1beta1" apiv1 "k8s.io/api/core/v1" @@ -12,7 +13,7 @@ import ( "github.com/weaveworks/flux/cluster" kresource "github.com/weaveworks/flux/cluster/kubernetes/resource" "github.com/weaveworks/flux/image" - fhr_v1beta1 "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" + hr_v1beta1 "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" fhr_v1alpha2 "github.com/weaveworks/flux/integrations/apis/helm.integrations.flux.weave.works/v1alpha2" "github.com/weaveworks/flux/policy" "github.com/weaveworks/flux/resource" @@ -20,7 +21,7 @@ import ( // AntecedentAnnotation is an annotation on a resource indicating that // the cause of that resource (indirectly, via a Helm release) is a -// FluxHelmRelease. We use this rather than the `OwnerReference` type +// HelmRelease. We use this rather than the `OwnerReference` type // built into Kubernetes so that there are no garbage-collection // implications. The value is expected to be a serialised // `resource.ID`. @@ -435,7 +436,7 @@ func (fhr *fluxHelmReleaseKind) getWorkloads(ctx context.Context, c *Cluster, na } func makeFluxHelmReleaseWorkload(fluxHelmRelease *fhr_v1alpha2.FluxHelmRelease) workload { - containers := createK8sFHRContainers(fluxHelmRelease.Spec.Values) + containers := createK8sHRContainers(fluxHelmRelease.Spec.Values) podTemplate := apiv1.PodTemplateSpec{ ObjectMeta: fluxHelmRelease.ObjectMeta, @@ -446,7 +447,7 @@ func makeFluxHelmReleaseWorkload(fluxHelmRelease *fhr_v1alpha2.FluxHelmRelease) } // apiVersion & kind must be set, since TypeMeta is not populated fluxHelmRelease.APIVersion = "helm.integrations.flux.weave.works/v1alpha2" - fluxHelmRelease.Kind = "FluxHelmRelease" + fluxHelmRelease.Kind = "HelmRelease" return workload{ status: fluxHelmRelease.Status.ReleaseStatus, podTemplate: podTemplate, @@ -455,11 +456,11 @@ func makeFluxHelmReleaseWorkload(fluxHelmRelease *fhr_v1alpha2.FluxHelmRelease) } // createK8sContainers creates a list of k8s containers by -// interpreting the FluxHelmRelease resource. The interpretation is +// interpreting the HelmRelease resource. The interpretation is // analogous to that in cluster/kubernetes/resource/fluxhelmrelease.go -func createK8sFHRContainers(values map[string]interface{}) []apiv1.Container { +func createK8sHRContainers(values map[string]interface{}) []apiv1.Container { var containers []apiv1.Container - _ = kresource.FindFluxHelmReleaseContainers(values, func(name string, image image.Ref, _ kresource.ImageSetter) error { + _ = kresource.FindHelmReleaseContainers(values, func(name string, image image.Ref, _ kresource.ImageSetter) error { containers = append(containers, apiv1.Container{ Name: name, Image: image.String(), @@ -471,39 +472,66 @@ func createK8sFHRContainers(values map[string]interface{}) []apiv1.Container { ///////////////////////////////////////////////////////////////////////////// // flux.weave.works/v1beta1 HelmRelease +// flux.fluxcd.io/v1 HelmRelease type helmReleaseKind struct{} +// getWorkload attempts to resolve a HelmRelease, it does so by first +// requesting the v1 version, and falling back to v1beta1 if this gives +// no result. In case the latter also fails it returns the error. +// TODO(hidde): this creates a new problem, as it will always return +// the error for the v1beta1 resource. Which may not be accurate in +// case v1beta1 is not active in the cluster at all. One potential +// solution may be to collect both errors and see if one outweighs +// the other. func (hr *helmReleaseKind) getWorkload(ctx context.Context, c *Cluster, namespace, name string) (workload, error) { if err := ctx.Err(); err != nil { return workload{}, err } + if helmRelease, err := c.client.HelmV1().HelmReleases(name).Get(name, meta_v1.GetOptions{}); err == nil { + return makeHelmReleaseStableWorkload(helmRelease), err + } helmRelease, err := c.client.FluxV1beta1().HelmReleases(namespace).Get(name, meta_v1.GetOptions{}) if err != nil { return workload{}, err } - return makeHelmReleaseWorkload(helmRelease), nil + return makeHelmReleaseBetaWorkload(helmRelease), nil } +// getWorkloads collects v1 and v1beta1 HelmRelease workloads, if the +// same workload (by name) is found for two versions, only the v1 +// version is returned. This is so that the workload results returned +// by this method are always valid for `getWorkload` and return the +// same resource. +// TODO(hidde): again, the cost of backwards compatibility is silencing +// errors. func (hr *helmReleaseKind) getWorkloads(ctx context.Context, c *Cluster, namespace string) ([]workload, error) { if err := ctx.Err(); err != nil { return nil, err } - helmReleases, err := c.client.FluxV1beta1().HelmReleases(namespace).List(meta_v1.ListOptions{}) - if err != nil { - return nil, err - } - + var names map[string]bool var workloads []workload - for i, _ := range helmReleases.Items { - workloads = append(workloads, makeHelmReleaseWorkload(&helmReleases.Items[i])) + if helmReleases, err := c.client.HelmV1().HelmReleases(namespace).List(meta_v1.ListOptions{}); err == nil { + for i, _ := range helmReleases.Items { + workload := makeHelmReleaseStableWorkload(&helmReleases.Items[i]) + workloads = append(workloads, workload) + names[workload.GetName()] = true + } + } + if helmReleases, err := c.client.FluxV1beta1().HelmReleases(namespace).List(meta_v1.ListOptions{}); err == nil { + for i, _ := range helmReleases.Items { + workload := makeHelmReleaseBetaWorkload(&helmReleases.Items[i]) + if names[workload.GetName()] { + continue + } + workloads = append(workloads, workload) + } } - return workloads, nil } -func makeHelmReleaseWorkload(helmRelease *fhr_v1beta1.HelmRelease) workload { - containers := createK8sFHRContainers(helmRelease.Spec.Values) +func makeHelmReleaseBetaWorkload(helmRelease *hr_v1beta1.HelmRelease) workload { + containers := createK8sHRContainers(helmRelease.Spec.Values) podTemplate := apiv1.PodTemplateSpec{ ObjectMeta: helmRelease.ObjectMeta, @@ -521,3 +549,23 @@ func makeHelmReleaseWorkload(helmRelease *fhr_v1beta1.HelmRelease) workload { k8sObject: helmRelease, } } + +func makeHelmReleaseStableWorkload(helmRelease *hr_v1.HelmRelease) workload { + containers := createK8sHRContainers(helmRelease.Spec.Values) + + podTemplate := apiv1.PodTemplateSpec{ + ObjectMeta: helmRelease.ObjectMeta, + Spec: apiv1.PodSpec{ + Containers: containers, + ImagePullSecrets: []apiv1.LocalObjectReference{}, + }, + } + // apiVersion & kind must be set, since TypeMeta is not populated + helmRelease.APIVersion = "helm.fluxcd.io/v1" + helmRelease.Kind = "HelmRelease" + return workload{ + status: helmRelease.Status.ReleaseStatus, + podTemplate: podTemplate, + k8sObject: helmRelease, + } +} diff --git a/cluster/kubernetes/sync_test.go b/cluster/kubernetes/sync_test.go index 349d019bc..f14bbdd12 100644 --- a/cluster/kubernetes/sync_test.go +++ b/cluster/kubernetes/sync_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + helmopfake "github.com/fluxcd/helm-operator/pkg/client/clientset/versioned/fake" "github.com/ghodss/yaml" "github.com/go-kit/kit/log" "github.com/stretchr/testify/assert" @@ -26,7 +27,7 @@ import ( "github.com/weaveworks/flux/cluster" kresource "github.com/weaveworks/flux/cluster/kubernetes/resource" - fluxfake "github.com/weaveworks/flux/integrations/client/clientset/versioned/fake" + fhrfake "github.com/weaveworks/flux/integrations/client/clientset/versioned/fake" "github.com/weaveworks/flux/resource" "github.com/weaveworks/flux/sync" ) @@ -62,7 +63,8 @@ func fakeClients() (ExtendedClient, func()) { } coreClient := corefake.NewSimpleClientset(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: defaultTestNamespace}}) - fluxClient := fluxfake.NewSimpleClientset() + fhrClient := fhrfake.NewSimpleClientset() + hrClient := helmopfake.NewSimpleClientset() dynamicClient := dynamicfake.NewSimpleDynamicClient(scheme) crdClient := crdfake.NewSimpleClientset() shutdown := make(chan struct{}) @@ -75,7 +77,7 @@ func fakeClients() (ExtendedClient, func()) { coreClient.Fake.Resources = apiResources if debug { - for _, fake := range []*k8s_testing.Fake{&coreClient.Fake, &fluxClient.Fake, &dynamicClient.Fake} { + for _, fake := range []*k8s_testing.Fake{&coreClient.Fake, &fhrClient.Fake, &hrClient.Fake, &dynamicClient.Fake} { fake.PrependReactor("*", "*", func(action k8s_testing.Action) (bool, runtime.Object, error) { gvr := action.GetResource() fmt.Printf("[DEBUG] action: %s ns:%s %s/%s %s\n", action.GetVerb(), action.GetNamespace(), gvr.Group, gvr.Version, gvr.Resource) @@ -85,10 +87,11 @@ func fakeClients() (ExtendedClient, func()) { } ec := ExtendedClient{ - coreClient: coreClient, - fluxHelmClient: fluxClient, - dynamicClient: dynamicClient, - discoveryClient: discoveryClient, + coreClient: coreClient, + fluxHelmClient: fhrClient, + helmOperatorClient: hrClient, + dynamicClient: dynamicClient, + discoveryClient: discoveryClient, } return ec, func() { close(shutdown) } diff --git a/cluster/kubernetes/update_test.go b/cluster/kubernetes/update_test.go index f5ae8c8c9..15aef62a5 100644 --- a/cluster/kubernetes/update_test.go +++ b/cluster/kubernetes/update_test.go @@ -52,7 +52,8 @@ func TestUpdates(t *testing.T) { {"FluxHelmRelease (v1alpha2; simple image encoding)", case11resource, case11containers, case11image, case11, case11out}, {"FluxHelmRelease (v1alpha2; multi image encoding)", case12resource, case12containers, case12image, case12, case12out}, {"HelmRelease (v1beta1; image with port number)", case13resource, case13containers, case13image, case13, case13out}, - {"initContainer", case14resource, case14containers, case14image, case14, case14out}, + {"HelmRelease (v1)", case14resource, case14containers, case14image, case14, case14out}, + {"initContainer", case15resource, case15containers, case15image, case15, case15out}, } { t.Run(c.name, func(t *testing.T) { testUpdate(t, c) @@ -922,6 +923,55 @@ spec: ` const case14 = `--- +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease +metadata: + name: mariadb + namespace: maria +spec: + chart: + repository: https://example.com/charts + name: mariadb + version: 1.1.2 + values: + mariadb: + image: localhost/mariadb + tag: 10.1.30-r1 + persistence: + enabled: false + workProperly: true + sidecar: + image: sidecar:v1 +` + +const case14resource = "maria:helmrelease/mariadb" +const case14image = "localhost/mariadb:10.1.33" + +var case14containers = []string{"mariadb"} + +const case14out = `--- +apiVersion: helm.fluxcd.io/v1 +kind: HelmRelease +metadata: + name: mariadb + namespace: maria +spec: + chart: + repository: https://example.com/charts + name: mariadb + version: 1.1.2 + values: + mariadb: + image: localhost/mariadb + tag: 10.1.33 + persistence: + enabled: false + workProperly: true + sidecar: + image: sidecar:v1 +` + +const case15 = `--- apiVersion: extensions/v1beta1 kind: Deployment metadata: @@ -938,12 +988,12 @@ spec: image: 'weaveworks/weave-kube:2.2.0' ` -const case14resource = "default:deployment/weave" -const case14image = "weaveworks/weave-kube:2.2.1" +const case15resource = "default:deployment/weave" +const case15image = "weaveworks/weave-kube:2.2.1" -var case14containers = []string{"weave"} +var case15containers = []string{"weave"} -const case14out = `--- +const case15out = `--- apiVersion: extensions/v1beta1 kind: Deployment metadata: diff --git a/cmd/fluxd/main.go b/cmd/fluxd/main.go index 15b0aef4f..54e88ae38 100644 --- a/cmd/fluxd/main.go +++ b/cmd/fluxd/main.go @@ -16,6 +16,7 @@ import ( "syscall" "time" + helmopclient "github.com/fluxcd/helm-operator/pkg/client/clientset/versioned" "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/pflag" @@ -38,7 +39,7 @@ import ( "github.com/weaveworks/flux/http/client" daemonhttp "github.com/weaveworks/flux/http/daemon" "github.com/weaveworks/flux/image" - integrations "github.com/weaveworks/flux/integrations/client/clientset/versioned" + hrclient "github.com/weaveworks/flux/integrations/client/clientset/versioned" "github.com/weaveworks/flux/job" "github.com/weaveworks/flux/manifests" "github.com/weaveworks/flux/registry" @@ -363,9 +364,15 @@ func main() { os.Exit(1) } - integrationsClientset, err := integrations.NewForConfig(restClientConfig) + fhrClientset, err := hrclient.NewForConfig(restClientConfig) if err != nil { - logger.Log("error", fmt.Sprintf("Error building integrations clientset: %v", err)) + logger.Log("error", fmt.Sprintf("Error building hrclient clientset: %v", err)) + os.Exit(1) + } + + hrClientset, err := helmopclient.NewForConfig(restClientConfig) + if err != nil { + logger.Log("error", fmt.Sprintf("Error building helm operator clientset: %v", err)) os.Exit(1) } @@ -427,7 +434,7 @@ func main() { } logger.Log("kubectl", kubectl) - client := kubernetes.MakeClusterClientset(clientset, dynamicClientset, integrationsClientset, discoClientset) + client := kubernetes.MakeClusterClientset(clientset, dynamicClientset, fhrClientset, hrClientset, discoClientset) kubectlApplier := kubernetes.NewKubectl(kubectl, restClientConfig) allowedNamespaces := append(*k8sNamespaceWhitelist, *k8sAllowNamespace...) k8sInst := kubernetes.NewCluster(client, kubectlApplier, sshKeyRing, logger, allowedNamespaces, *registryExcludeImage) diff --git a/cmd/helm-operator/main.go b/cmd/helm-operator/main.go deleted file mode 100644 index 6091802f3..000000000 --- a/cmd/helm-operator/main.go +++ /dev/null @@ -1,240 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/signal" - "sync" - "syscall" - "time" - - "github.com/go-kit/kit/log" - "github.com/spf13/pflag" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/workqueue" - "k8s.io/klog" - - "github.com/weaveworks/flux/checkpoint" - clientset "github.com/weaveworks/flux/integrations/client/clientset/versioned" - ifinformers "github.com/weaveworks/flux/integrations/client/informers/externalversions" - fluxhelm "github.com/weaveworks/flux/integrations/helm" - "github.com/weaveworks/flux/integrations/helm/chartsync" - daemonhttp "github.com/weaveworks/flux/integrations/helm/http/daemon" - "github.com/weaveworks/flux/integrations/helm/operator" - "github.com/weaveworks/flux/integrations/helm/release" - "github.com/weaveworks/flux/integrations/helm/status" -) - -var ( - fs *pflag.FlagSet - logger log.Logger - - versionFlag *bool - - logFormat *string - - kubeconfig *string - master *string - namespace *string - - workers *int - - tillerIP *string - tillerPort *string - tillerNamespace *string - - tillerTLSVerify *bool - tillerTLSEnable *bool - tillerTLSKey *string - tillerTLSCert *string - tillerTLSCACert *string - tillerTLSHostname *string - - chartsSyncInterval *time.Duration - logReleaseDiffs *bool - updateDependencies *bool - - gitTimeout *time.Duration - gitPollInterval *time.Duration - - listenAddr *string -) - -const ( - product = "weave-flux-helm" - ErrOperatorFailure = "Operator failure: %q" -) - -var version = "unversioned" - -func init() { - // Flags processing - fs = pflag.NewFlagSet("default", pflag.ExitOnError) - fs.Usage = func() { - fmt.Fprintf(os.Stderr, "DESCRIPTION\n") - fmt.Fprintf(os.Stderr, " helm-operator releases Helm charts from git.\n") - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintf(os.Stderr, "FLAGS\n") - fs.PrintDefaults() - } - - versionFlag = fs.Bool("version", false, "print version and exit") - - logFormat = fs.String("log-format", "fmt", "change the log format.") - - kubeconfig = fs.String("kubeconfig", "", "path to a kubeconfig; required if out-of-cluster") - master = fs.String("master", "", "address of the Kubernetes API server; overrides any value in kubeconfig; required if out-of-cluster") - namespace = fs.String("allow-namespace", "", "if set, this limits the scope to a single namespace; if not specified, all namespaces will be watched") - - workers = fs.Int("workers", 1, "amount of workers processing releases (experimental)") - - listenAddr = fs.StringP("listen", "l", ":3030", "Listen address where /metrics and API will be served") - - tillerIP = fs.String("tiller-ip", "", "Tiller IP address; required if run out-of-cluster") - tillerPort = fs.String("tiller-port", "", "Tiller port; required if run out-of-cluster") - tillerNamespace = fs.String("tiller-namespace", "kube-system", "Tiller namespace") - - tillerTLSVerify = fs.Bool("tiller-tls-verify", false, "verify TLS certificate from Tiller; will enable TLS communication when provided") - tillerTLSEnable = fs.Bool("tiller-tls-enable", false, "enable TLS communication with Tiller; if provided, requires TLSKey and TLSCert to be provided as well") - tillerTLSKey = fs.String("tiller-tls-key-path", "/etc/fluxd/helm/tls.key", "path to private key file used to communicate with the Tiller server") - tillerTLSCert = fs.String("tiller-tls-cert-path", "/etc/fluxd/helm/tls.crt", "path to certificate file used to communicate with the Tiller server") - tillerTLSCACert = fs.String("tiller-tls-ca-cert-path", "", "path to CA certificate file used to validate the Tiller server; required if tiller-tls-verify is enabled") - tillerTLSHostname = fs.String("tiller-tls-hostname", "", "server name used to verify the hostname on the returned certificates from the server") - - chartsSyncInterval = fs.Duration("charts-sync-interval", 3*time.Minute, "period on which to reconcile the Helm releases with HelmRelease resources") - logReleaseDiffs = fs.Bool("log-release-diffs", false, "log the diff when a chart release diverges; potentially insecure") - updateDependencies = fs.Bool("update-chart-deps", true, "update chart dependencies before installing/upgrading a release") - - gitTimeout = fs.Duration("git-timeout", 20*time.Second, "duration after which git operations time out") - gitPollInterval = fs.Duration("git-poll-interval", 5*time.Minute, "period on which to poll git chart sources for changes") -} - -func main() { - // Explicitly initialize klog to enable stderr logging, - // and parse our own flags. - klog.InitFlags(nil) - fs.Parse(os.Args) - - if *versionFlag { - println(version) - os.Exit(0) - } - - // init go-kit log - { - switch *logFormat { - case "json": - logger = log.NewJSONLogger(log.NewSyncWriter(os.Stderr)) - case "fmt": - logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) - default: - logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) - } - logger = log.With(logger, "ts", log.DefaultTimestampUTC) - logger = log.With(logger, "caller", log.DefaultCaller) - } - - // error channel - errc := make(chan error) - - // shutdown triggers - shutdown := make(chan struct{}) - shutdownWg := &sync.WaitGroup{} - - // wait for SIGTERM - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - errc <- fmt.Errorf("%s", <-c) - }() - - mainLogger := log.With(logger, "component", "helm-operator") - - cfg, err := clientcmd.BuildConfigFromFlags(*master, *kubeconfig) - if err != nil { - mainLogger.Log("error", fmt.Sprintf("error building kubeconfig: %v", err)) - os.Exit(1) - } - - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - mainLogger.Log("error", fmt.Sprintf("error building kubernetes clientset: %v", err)) - os.Exit(1) - } - - ifClient, err := clientset.NewForConfig(cfg) - if err != nil { - mainLogger.Log("error", fmt.Sprintf("error building integrations clientset: %v", err)) - os.Exit(1) - } - - helmClient := fluxhelm.ClientSetup(log.With(logger, "component", "helm"), kubeClient, fluxhelm.TillerOptions{ - Host: *tillerIP, - Port: *tillerPort, - Namespace: *tillerNamespace, - TLSVerify: *tillerTLSVerify, - TLSEnable: *tillerTLSEnable, - TLSKey: *tillerTLSKey, - TLSCert: *tillerTLSCert, - TLSCACert: *tillerTLSCACert, - TLSHostname: *tillerTLSHostname, - }) - - // setup shared informer for HelmReleases - nsOpt := ifinformers.WithNamespace(*namespace) - ifInformerFactory := ifinformers.NewSharedInformerFactoryWithOptions(ifClient, *chartsSyncInterval, nsOpt) - fhrInformer := ifInformerFactory.Flux().V1beta1().HelmReleases() - - // setup workqueue for HelmReleases - queue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ChartRelease") - - // release instance is needed during the sync of git chart changes - // and during the sync of HelmRelease changes - rel := release.New(log.With(logger, "component", "release"), helmClient) - chartSync := chartsync.New( - log.With(logger, "component", "chartsync"), - chartsync.Clients{KubeClient: *kubeClient, IfClient: *ifClient, FhrLister: fhrInformer.Lister()}, - rel, - queue, - chartsync.Config{LogDiffs: *logReleaseDiffs, UpdateDeps: *updateDependencies, GitTimeout: *gitTimeout, GitPollInterval: *gitPollInterval}, - *namespace, - ) - - // prepare operator and start FluxRelease informer - // NB: the operator needs to do its magic with the informer - // _before_ starting it or else the cache sync seems to hang at - // random - opr := operator.New(log.With(logger, "component", "operator"), *logReleaseDiffs, kubeClient, fhrInformer, queue, chartSync) - go ifInformerFactory.Start(shutdown) - - // wait for the caches to be synced before starting _any_ workers - mainLogger.Log("info", "waiting for informer caches to sync") - if ok := cache.WaitForCacheSync(shutdown, fhrInformer.Informer().HasSynced); !ok { - mainLogger.Log("error", "failed to wait for caches to sync") - os.Exit(1) - } - mainLogger.Log("info", "informer caches synced") - - // start operator - go opr.Run(*workers, shutdown, shutdownWg) - - // start git sync loop - go chartSync.Run(shutdown, errc, shutdownWg) - - // the status updater, to keep track of the release status for - // every HelmRelease - statusUpdater := status.New(ifClient, fhrInformer.Lister(), helmClient) - go statusUpdater.Loop(shutdown, log.With(logger, "component", "statusupdater")) - - // start HTTP server - go daemonhttp.ListenAndServe(*listenAddr, chartSync, log.With(logger, "component", "daemonhttp"), shutdown) - - checkpoint.CheckForUpdates(product, version, nil, log.With(logger, "component", "checkpoint")) - - shutdownErr := <-errc - logger.Log("exiting...", shutdownErr) - close(shutdown) - shutdownWg.Wait() -} diff --git a/deploy-helm/flux-helm-release-crd.yaml b/deploy-helm/flux-helm-release-crd.yaml deleted file mode 100644 index 9633b1d41..000000000 --- a/deploy-helm/flux-helm-release-crd.yaml +++ /dev/null @@ -1,139 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: helmreleases.flux.weave.works -spec: - group: flux.weave.works - names: - kind: HelmRelease - listKind: HelmReleaseList - plural: helmreleases - shortNames: - - hr - scope: Namespaced - subresources: - status: {} - version: v1beta1 - versions: - - name: v1beta1 - served: true - storage: true - validation: - openAPIV3Schema: - properties: - spec: - required: ['chart'] - properties: - releaseName: - type: string - pattern: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" - targetNamespace: - type: string - pattern: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" - timeout: - type: integer - format: int64 - resetValues: - type: boolean - forceUpgrade: - type: boolean - rollback: - type: object - properties: - enable: - type: boolean - force: - type: boolean - recreate: - type: boolean - disableHooks: - type: boolean - timeout: - type: integer - format: int64 - wait: - type: boolean - valueFileSecrets: - type: array - items: - type: object - required: ['name'] - properties: - name: - type: string - valuesFrom: - type: array - items: - type: object - properties: - configMapKeyRef: - type: object - required: ['name'] - properties: - name: - type: string - key: - type: string - optional: - type: boolean - secretKeyRef: - type: object - required: ['name'] - properties: - name: - type: string - key: - type: string - optional: - type: boolean - externalSourceRef: - type: object - required: ['url'] - properties: - url: - type: string - optional: - type: boolean - chartFileRef: - type: object - required: ['path'] - properties: - path: - type: string - optional: - type: boolean - oneOf: - - required: ['configMapKeyRef'] - - required: ['secretKeyRef'] - - required: ['externalSourceRef'] - - required: ['chartFileRef'] - values: - type: object - chart: - oneOf: - - required: ['git', 'path'] - properties: - git: - type: string - format: git # not defined by OAS - path: - type: string - ref: - type: string - skipDepUpdate: - type: boolean - - required: ['repository', 'name', 'version'] - properties: - repository: - type: string - format: url # not defined by OAS - name: - type: string - version: - type: string - format: semver # not defined by OAS - chartPullSecret: - properties: - name: - type: string diff --git a/deploy-helm/helm-operator-deployment.yaml b/deploy-helm/helm-operator-deployment.yaml deleted file mode 100644 index 12e53bf6d..000000000 --- a/deploy-helm/helm-operator-deployment.yaml +++ /dev/null @@ -1,85 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: flux-helm-operator -spec: - replicas: 1 - selector: - matchLabels: - name: flux-helm-operator - strategy: - type: Recreate - template: - metadata: - labels: - name: flux-helm-operator - annotations: - prometheus.io/scrape: "true" - spec: - serviceAccountName: flux - volumes: - # The following volume is for using a customised known_hosts file, - # which you will need to do if you host your own git repo rather - # than using github or the like. You'll also need to mount it - # into the container, below. - # - # You may also wish to provide an ssh_config file, - # mentioning more than one `IdentityFile`; for instance, if you're - # using more than one GitHub repo. NB the entry key should be - # "config" rather than "ssh_config" if mounted in ~/.ssh/. - # - # - name: sshdir - # configMap: - # name: flux-ssh-config - # defaultMode: 0400 - # - # You will need this volume if you're using a git repo that - # needs an SSH key for access; e.g., a GitHub deploy key. If - # you're using just one such repo, the default ssh_config - # already points at /etc/fluxd/ssh/identity as a key. If you - # want to use more than one key, you'll need to provide your own - # ssh_config above, with an `IdentityFile` entry matching each - # key in the secret mentioned here. - # - # - name: git-key - # secret: - # secretName: flux-git-deploy - # defaultMode: 0400 # when mounted read-only, we won't be able to chmod - # - # These two volumes are for mounting a repositories.yaml file, - # and providing a cache directory. The latter is needed because - # mounting the former will make the cache/ directory read-only. - # - # - name: repositories-yaml - # secret: - # secretName: flux-helm-repositories - # - name: repositories-cache - # emptyDir: {} - - containers: - - name: flux-helm-operator - # There are no ":latest" images for helm-operator. Find the most recent - # release or image version at https://hub.docker.com/r/weaveworks/helm-operator/tags - # and replace the tag here. - image: docker.io/fluxcd/helm-operator:0.10.1 - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 3030 - resources: - requests: - cpu: 50m - memory: 64Mi - volumeMounts: - # Include this if you need to mount a customised known_hosts or ssh_config - # file; you'll also need the volume declared above. - # - name: sshdir - # mountPath: /root/.ssh - # readOnly: true - # - name: git-key - # mountPath: /etc/fluxd/ssh - # - name: repositories-yaml - # mountPath: /var/fluxd/helm/repository - # - name: repositories-cache - # mountPath: /var/fluxd/helm/repository/cache diff --git a/deploy-helm/weave-cloud-helm-operator-deployment.yaml b/deploy-helm/weave-cloud-helm-operator-deployment.yaml deleted file mode 100644 index bbe2b4c37..000000000 --- a/deploy-helm/weave-cloud-helm-operator-deployment.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: flux-helm-operator - namespace: weave - labels: - app: flux-helm-operator - weave-cloud-component: helm-operator -spec: - strategy: - type: Recreate - selector: - matchLabels: - app: flux-helm-operator - template: - metadata: - annotations: - prometheus.io/scrape: "false" - labels: - app: flux-helm-operator - spec: - serviceAccountName: weave-flux - volumes: - - name: git-key - secret: - defaultMode: 256 - secretName: flux-git-deploy - containers: - - name: flux-helm-operator - image: docker.io/fluxcd/helm-operator:0.10.1 - imagePullPolicy: IfNotPresent - args: - - --git-timeout=20s - - --charts-sync-interval=3m - - --update-chart-deps=true - - --tiller-namespace=kube-system - volumeMounts: - - name: git-key - mountPath: /etc/fluxd/ssh - resources: - limits: - cpu: 1000m - memory: 512Mi - requests: - cpu: 50m - memory: 64Mi diff --git a/docker/Dockerfile.helm-operator b/docker/Dockerfile.helm-operator deleted file mode 100644 index d8b7f27dd..000000000 --- a/docker/Dockerfile.helm-operator +++ /dev/null @@ -1,49 +0,0 @@ -FROM alpine:3.9 - -WORKDIR /home/flux - -RUN apk add --no-cache openssh-client ca-certificates tini 'git>=2.12.0' socat - -# Add git hosts to known hosts file so we can use -# StrickHostKeyChecking with git+ssh -ADD ./known_hosts.sh /home/flux/known_hosts.sh -RUN sh /home/flux/known_hosts.sh /etc/ssh/ssh_known_hosts && \ - rm /home/flux/known_hosts.sh - -# Add default SSH config, which points at the private key we'll mount -COPY ./ssh_config /etc/ssh/ssh_config - -COPY ./kubectl /usr/local/bin/ -# The Helm client is included as a convenience for troubleshooting -COPY ./helm /usr/local/bin/ - -# These are pretty static -LABEL maintainer="Flux CD " \ - org.opencontainers.image.title="flux-helm-operator" \ - org.opencontainers.image.description="The Helm operator for Kubernetes" \ - org.opencontainers.image.url="https://github.com/fluxcd/flux" \ - org.opencontainers.image.source="git@github.com:fluxcd/flux" \ - org.opencontainers.image.vendor="Flux CD" \ - org.label-schema.schema-version="1.0" \ - org.label-schema.name="flux-helm-operator" \ - org.label-schema.description="The Helm operator for Kubernetes" \ - org.label-schema.url="https://github.com/fluxcd/flux" \ - org.label-schema.vcs-url="git@github.com:fluxcd/flux" \ - org.label-schema.vendor="Flux CD" - -ENTRYPOINT [ "/sbin/tini", "--", "helm-operator" ] - -ENV HELM_HOME=/var/fluxd/helm -COPY ./helm-repositories.yaml /var/fluxd/helm/repository/repositories.yaml -RUN mkdir -p /var/fluxd/helm/repository/cache/ - -COPY ./helm-operator /usr/local/bin/ - -ARG BUILD_DATE -ARG VCS_REF - -# These will change for every build -LABEL org.opencontainers.image.revision="$VCS_REF" \ - org.opencontainers.image.created="$BUILD_DATE" \ - org.label-schema.vcs-ref="$VCS_REF" \ - org.label-schema.build-date="$BUILD_DATE" diff --git a/docker/helm-repositories.yaml b/docker/helm-repositories.yaml deleted file mode 100644 index d3ddc7e42..000000000 --- a/docker/helm-repositories.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -repositories: -- caFile: "" - cache: /var/fluxd/helm/repository/cache/stable-index.yaml - certFile: "" - keyFile: "" - name: stable - password: "" - url: https://kubernetes-charts.storage.googleapis.com - username: "" diff --git a/docs/helm-operator/faq.md b/docs/helm-operator/faq.md deleted file mode 100644 index 741372df4..000000000 --- a/docs/helm-operator/faq.md +++ /dev/null @@ -1,42 +0,0 @@ -# Frequently asked questions - -### I'm using SSL between Helm and Tiller. How can I configure Flux to use the certificate? - -When installing Flux, you can supply the CA and client-side certificate using the `helmOperator.tls` options, -more details [here](https://github.com/fluxcd/flux/blob/master/chart/flux/README.md#installing-weave-flux-helm-operator-and-helm-with-tls-enabled). - -### I've deleted a HelmRelease file from Git. Why is the Helm release still running on my cluster? - -Flux doesn't delete resources, there is an [issue](https://github.com/fluxcd/flux/issues/738) opened about this topic on GitHub. -In order to delete a Helm release first remove the file from Git and afterwards run: - -```yaml -kubectl delete helmrelease/my-release -``` - -The Helm operator will receive the delete event and will purge the Helm release. - -### I've manually deleted a Helm release. Why is Flux not able to restore it? - -If you delete a Helm release with `helm delete my-release`, the release name can't be reused. -You need to use the `helm delete --purge` option only then Flux will be able reinstall a release. - -### I have a dedicated Kubernetes cluster per environment and I want to use the same Git repo for all. How can I do that? - -*Option 1* -For each cluster create a directory in your config repo. -When installing Flux Helm chart set the Git path using `--set git.path=k8s/cluster-name` -and set a unique label for each cluster `--set git.label=cluster-name`. - -You can have one or more shared dirs between clusters. Assuming your shared dir is located -at `k8s/common` set the Git path as `--set git.path="k8s/common\,k8s/cluster-name"`. - -*Option 2* -For each cluster create a Git branch in your config repo. -When installing Flux Helm chart set the Git branch using `--set git.branch=cluster-name` -and set a unique label for each cluster `--set git.label=cluster-name`. - -### Are there prerelease builds I can run? - -There are builds from CI for each merge to master branch. See -[fluxcd/helm-operator-prerelease](https://hub.docker.com/r/fluxcd/helm-operator-prerelease/tags). diff --git a/docs/helm-operator/guides/index.rst b/docs/helm-operator/guides/index.rst deleted file mode 100644 index 1dd0c6674..000000000 --- a/docs/helm-operator/guides/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Guides -====== - -.. toctree:: - :maxdepth: 1 - - upgrading-to-beta diff --git a/docs/helm-operator/guides/upgrading-to-beta.md b/docs/helm-operator/guides/upgrading-to-beta.md deleted file mode 100644 index 7270adb2f..000000000 --- a/docs/helm-operator/guides/upgrading-to-beta.md +++ /dev/null @@ -1,154 +0,0 @@ -# Upgrading from Helm operator alpha (<=0.4.0) to beta - -The Helm operator has undergone changes that necessitate some changes -to custom resources, and the deployment of the operator itself. - -The central difference is that the Helm operator now works with -resources of the kind `HelmRelease` in the API version -`flux.weave.works/v1beta1`, which have a different format to the -custom resources used by the old Helm operator (`FluxHelmRelease`). - -Here are some things to know: - -- The new operator will ignore the old custom resources (and the old - operator will ignore the new resources). -- Deleting a resource while the corresponding operator is running - will result in the Helm release also being deleted -- Deleting a `CustomResourceDefinition` will also delete all - custom resources of that kind. -- If both operators are running and both new and old custom resources - defining a release, the operators will fight over the release. - -The safest way to upgrade is to avoid deletions and fights by stopping -the old operator. Replacing it with the new one (e.g., by changing the -deployment, or re-releasing the Flux chart with the new version) will -have that effect. - -Once the old operator is not running, it is safe to deploy the new -operator, and start replacing the old resources with new -resources. You can keep the old resources around during this process, -since the new operator will ignore them. - -## Upgrading the operator deployment - -### Using the Flux chart - -The chart (from v0.5.0, or from this git repo) provides the -correct arguments to the operator; to upgrade, do - -```sh -helm repo update - -helm upgrade flux --reuse-values \ ---set image.tag=1.8.1 \ ---set helmOperator.tag=0.5.1 \ ---namespace=flux \ -fluxcd/flux --version 0.5.1 -``` - -The chart will leave the old custom resource definition and custom -resources in place. You will need to replace the individual resources, -as described below. - -### Using manifests - -You will need to adapt any existing manifest that you use to run the -Helm operator. The arguments to the operator executable have changed, -since it no longer needs the git repo to be specified (and in some -cases, just to tidy up): - -- the new operator does not use the `--git-url`, `--git-charts-path`, - or `--git-branch` arguments, since the git repo and so on are - provided in each custom resource. -- the `--queue-worker-count` argument has been removed -- the `--chart-sync-timeout` argument has been removed -- other arguments stay the same - -It is entirely valid to run the operator with no arguments, which you -may end up with after removing those mentioned above. It will work -with the secrets mounted as for the old operator, to start off with, -since it expects the SSH key for the git repo to be in the same place. - -Once you want to use the new capabilities of the operator -- e.g., -releasing charts from Helm repos -- you will probably need to adapt -the manifest further. The [Helm operator set-up -guide](../../references/helm-operator-integration.md) and [example -deployment](https://github.com/fluxcd/flux/blob/master/deploy-helm/helm-operator-deployment.yaml) -explain all the details. - -## Updating custom resources - -The main differences between the old resource format and the new are: - -- the API version and kind have changed -- you can now specify a chart to release either as a path in a git - repo, or a named, versioned chart from a Helm repo - -Here is how to change an old resource to a new resource: - -- change the `apiVersion` field to `flux.weave.works/v1beta1` -- change the `kind` field to `HelmRelease` -- you can remove the label `chart:` from the labels, if it's still - there, just to tidy up (it doesn't matter if it's there or not) -- replace the field `chartGitPath`, with the structure: - -```yaml -chart: - git: - ref: - path: -``` - -- the `values`, `releaseName`, and `valueFileSecrets` can stay as - they are. - -Note that you now give the git repo URL and branch and full path in -each custom resource, rather than supplying arguments to the Helm -operator. (As you've been using the old operator, you'll only have one -git repo for all charts -- but now you can use different repos for -charts!) - -As a full example, this is an old resource: - -```yaml ---- -apiVersion: helm.integrations.flux.weave.works/v1alpha2 -kind: FluxHelmRelease -metadata: - name: foobar - namespace: foo-ns -spec: - chartGitPath: foobar - values: - image: - repository: foobar - tag: v1 -``` - -Say the arguments given to the old Helm operator were - -```yaml -args: - - --git-url=git@example.com:user/repo - - --git-charts-path=charts - - --git-branch=master -``` - -Then the new custom resource would be: - -```yaml ---- -apiVersion: flux.weave.works/v1beta1 # <- change API version -kind: HelmRelease # <- change kind -metadata: - name: foobar - namespace: foo-ns -spec: - chart: - git: git@example.com:user/repo # <- --git-url from operator args - path: charts/foobar # <- join --git-chart-path and chartGitPath - values: - image: - repository: foobar - tag: v1 -``` diff --git a/docs/helm-operator/index.rst b/docs/helm-operator/index.rst deleted file mode 100644 index 9eac5cdd7..000000000 --- a/docs/helm-operator/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Helm operator -============= - -.. toctree:: - :maxdepth: 2 - - references/index - guides/index - tutorials/index - - faq - troubleshooting diff --git a/docs/helm-operator/references/helmrelease-custom-resource.md b/docs/helm-operator/references/helmrelease-custom-resource.md deleted file mode 100644 index eb8a4de1e..000000000 --- a/docs/helm-operator/references/helmrelease-custom-resource.md +++ /dev/null @@ -1,334 +0,0 @@ -# `HelmRelease` Custom Resource - -Each release of a chart is declared by a `HelmRelease` -resource. The schema for these resources is given in [the custom -resource definition](https://github.com/fluxcd/flux/blob/master/deploy-helm/flux-helm-release-crd.yaml). They -look like this: - -```yaml -apiVersion: flux.weave.works/v1beta1 -kind: HelmRelease -metadata: - name: rabbit - namespace: default -spec: - releaseName: rabbitmq - chart: - repository: https://kubernetes-charts.storage.googleapis.com/ - name: rabbitmq - version: 3.3.6 - values: - replicas: 1 -``` - -The `releaseName` will be given to Helm as the release name. If not -supplied, it will be generated by affixing the namespace to the -resource name. In the above example, if `releaseName` were not given, -it would be generated as `default-rabbitmq`. Because of the way Helm -works, release names must be unique in the cluster. - -The `chart` section gives a pointer to the chart; in this case, to a -chart in a Helm repo. Since the helm operator is running in your -cluster, and doesn't have access to local configuration, the -repository is given as a URL rather than an alias (the URL in the -example is what's usually aliased as `stable`). The `name` and -`version` specify the chart to release. - -The `values` section is where you provide the value overrides for the -chart. This is as you would put in a `values.yaml` file, but inlined -into the structure of the resource. See below for examples. - -**Why use URLs to refer to repositories, rather than names?** [^](#cite-why-repo-urls) - -A `HelmRelease` must be able to stand on its own. If we used names -in the spec, which were resolved to URLs elsewhere (e.g., in a -`repositories.yaml` supplied to the operator), it would be possible to -change the meaning of a `HelmRelease` without altering it. This is -undesirable because it makes it hard to specify exactly what you want, -in the one place; or to read exactly what is being specified, in the -one place. In other words, it's better to be explicit. - -## Using a chart from a Git repo instead of a Helm repo - -You can refer to a chart from a _git_ repo, rather than a chart repo, -with a `chart:` section like this: - -```yaml -spec: - chart: - git: git@github.com:fluxcd/flux-get-started - ref: master - path: charts/ghost -``` - -In this case, the git repo will be cloned, and the chart will be -released from the ref given (which defaults to `master`, if not -supplied). Commits to the git repo may result in releases, if they -update the chart at the path given. - -Note that you will usually need to provide an SSH key to grant access -to the git repository. The example deployment shows how to mount a -secret at the expected location of the key (`/etc/fluxd/ssh/`). If you -need more than one SSH key, you'll need to also mount an adapted -ssh_config; this is also demonstrated in the example deployment. - -### Notifying Helm Operator about Git changes - -The Helm Operator fetches the upstream of mirrored Git repositories -with a 5 minute interval. In some scenarios (think CI/CD), you may not -want to wait for this interval to occur. - -To help you with this the Helm Operator serves a HTTP API endpoint to -instruct it to immediately refresh all Git mirrors. - -```sh -$ kubectl -n flux port-forward deployment/flux-helm-operator 3030:3030 & -$ curl -XPOST http://localhost:3030/api/v1/sync-git -OK -``` - -> **Note:** the HTTP API has no built-in authentication, this means you -> either need to port forward before making the request or put something -> in front of it to serve as a gatekeeper. - -## Supplying values to the chart - -You can supply values to be used with the chart when installing it, in -two ways. - -### `.spec.values` - -This is a YAML map as you'd put in a file and supply to Helm with `-f -values.yaml`, but inlined into the `HelmRelease` manifest. For -example, - -```yaml -apiVersion: flux.weave.works/v1beta1 -kind: HelmRelease -# metadata: ... -spec: - # chart: ... - values: - foo: value1 - bar: - baz: value2 - oof: - - item1 - - item2 -``` - -### `.spec.valuesFrom` - -This is a list of secrets, config maps (in the same namespace as the -`HelmRelease`) or external sources (URLs) from which to take values. - -The values are merged in the order given, with later values -overwriting earlier. These values always have a lower priority than -those passed via the `.spec.values` parameter. - -This is useful if you want to have defaults such as the `region`, -`clustername`, `environment`, a local docker registry URL, etc., or if -you simply want to have values not checked into git as plaintext. - -#### Config maps - -```yaml -spec: - # chart: ... - valuesFrom: - - configMapKeyRef: - # Name of the config map, must be in the same namespace as the - # HelmRelease - name: default-values # mandatory - # Key in the config map to get the values from - key: values.yaml # optional; defaults to values.yaml - # If set to true successful retrieval of the values file is no - # longer mandatory - optional: false # optional; defaults to false -``` - -#### Secrets - -```yaml -spec: - # chart: ... - valuesFrom: - - secretKeyRef: - # Name of the secret, must be in the same namespace as the - # HelmRelease - name: default-values # mandatory - # Key in the secret to get thre values from - key: values.yaml # optional; defaults to values.yaml - # If set to true successful retrieval of the values file is no - # longer mandatory - optional: true # optional; defaults to false -``` - -#### External sources - -```yaml -spec: - # chart: ... - valuesFrom: - - externalSourceRef: - # URL of the values.yaml - url: https://example.com/static/raw/values.yaml # mandatory - # If set to true successful retrieval of the values file is no - # longer mandatory - optional: true # optional; defaults to false -``` - -#### Chart files - -```yaml -spec: - # chart: ... - valuesFrom: - - chartFileRef: - # path within the helm chart (from git repo) where environment-prod.yaml is located - path: overrides/environment-prod.yaml # mandatory - # If set to true successful retrieval of the values file is no - # longer mandatory - optional: true # optional; defaults to false -``` - -## Rollbacks - -From time to time a release made by the Helm operator may fail, it is -possible to automate the rollback of a failed release by setting -`.spec.rollback.enable` to `true` on the `HelmRelease` resource. - -> **Note:** a successful rollback of a Helm chart containing a -> `StatefulSet` resource is known to be tricky, and one of the main -> reasons automated rollbacks are not enabled by default for all -> `HelmRelease`s. Verify a manual rollback of your Helm chart does -> not cause any problems before enabling it. - -When enabled, the Helm operator will detect a faulty upgrade and -perform a rollback, it will not attempt a new upgrade unless it -detects a change in values and/or the chart. - -### Configuration - -```yaml -apiVersion: flux.weave.works/v1beta1 -kind: HelmRelease -# metadata: ... -spec: - # Listed values are the defaults. - rollback: - # If set, will perform rollbacks for this release. - enable: false - # If set, will force resource update through delete/recreate if - # needed. - force: false - # Prevent hooks from running during rollback. - disableHooks: false - # Time in seconds to wait for any individual Kubernetes operation. - timeout: 300 - # If set, will wait until all Pods, PVCs, Services, and minimum - # number of Pods of a Deployment are in a ready state before - # marking the release as successful. It will wait for as long - # as the set timeout. - wait: false -``` - -## Reinstalling a Helm release - -If a Helm release upgrade fails due to incompatible changes like modifying -an immutable field (e.g. headless svc to ClusterIP) -you can reinstall it using the following command: - -```sh -$ kubectl delete hr/my-release -``` - -When the Helm Operator receives a delete event from Kubernetes API it will -call Tiller and purge the Helm release. On the next Flux sync, the Helm Release -object will be created and the Helm Operator will install it. - -## Authentication - -At present, per-resource authentication is not implemented. The -`HelmRelease` definition includes a field `chartPullSecret` for -attaching a `repositories.yaml` file, but this is ignored for now. - -Instead, you need to provide the operator with credentials and keys (see the -following [Authentication for Helm repos](#authentication-for-helm-repos) -section for how to do this). - -### Authentication for Helm repos - -As a workaround, you can mount a `repositories.yaml` file with -authentication already configured, into the operator container. - -> **Note:** When using a custom `repositories.yaml` the -[default](https://github.com/fluxcd/flux/blob/master/docker/helm-repositories.yaml) -that ships with the operator is overwritten. This means that for any -repository you want to make use of you should manually add an entry to -your `repositories.yaml` file. - -To prepare a file, add the repo _locally_ as you would normally: - -```sh -helm repo add --username --password -``` - -You need to doctor this file a little, since it will likely contain -absolute paths that will be wrong when mounted inside the -container. Copy the file and replace all the `cache` entries with just -the filename. - -```sh -cp ~/.helm/repository/repositories.yaml . -sed -i -e 's/^\( *cache: \).*\/\(.*\.yaml\)/\1\2/g' repositories.yaml -``` - -Now you can create a secret in the same namespace as you're running -the Helm operator, from the repositories file: - -```sh -kubectl create secret generic flux-helm-repositories --from-file=./repositories.yaml -``` - -Lastly, mount that secret into the container. This can be done by -setting `helmOperator.configureRepositories.enable` to `true` for the -flux Helm release, or as shown in the commented-out sections of the -[example deployment](https://github.com/fluxcd/flux/blob/master/deploy-helm/helm-operator-deployment.yaml). - -#### Azure ACR repositories - -For Azure ACR repositories, the entry in `repositories.yaml` created by -running `az acr helm repo add` is unsufficient for the Helm operator. -Instead you will need to [create a service principal](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-auth-service-principal#create-a-service-principal) -and use the plain text id and password this gives you. For example: - -```yaml -- caFile: "" - cache: -index.yaml - certFile: "" - keyFile: "" - name: - url: https://.azurecr.io/helm/v1/repo - username: - password: -``` - -### Authentication for Git repos - -In general, it's necessary to have an SSH key to clone a git -repo. This is sometimes (e.g., on GitHub) called a "deploy key". To -use a chart from git, the Helm Operator needs a key with read-only -access. - -To provide an SSH key, put the key in a secret under the entry -`identity`, and mount it into the operator container as shown in the -[example deployment](https://github.com/fluxcd/flux/blob/master/deploy-helm/helm-operator-deployment.yaml). -The default ssh_config expects an identity file at -`/etc/fluxd/ssh/identity`, which is where it'll be if you just -uncomment the blocks from the example. - -If you're using more than one repository, you may need to provide more -than one SSH key. In that case, you can create a secret with an entry -for each key, and mount that _as well as_ an ssh_config file -mentioning each key as an `IdentityFile`. diff --git a/docs/helm-operator/references/index.rst b/docs/helm-operator/references/index.rst deleted file mode 100644 index 092ec1169..000000000 --- a/docs/helm-operator/references/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -References -========== - -.. toctree:: - :maxdepth: 1 - - operator - helmrelease-custom-resource diff --git a/docs/helm-operator/references/operator.md b/docs/helm-operator/references/operator.md deleted file mode 100644 index 02a73d8d0..000000000 --- a/docs/helm-operator/references/operator.md +++ /dev/null @@ -1,40 +0,0 @@ -# Helm operator (`helm-operator`) - -The Helm operator deals with Helm chart releases. The operator watches for -changes of Custom Resources of kind `HelmRelease`. It receives Kubernetes -Events and acts accordingly. - -## Responsibilities - -When the Helm Operator sees a `HelmRelease` resource in the -cluster, it either installs or upgrades the named Helm release so that -the chart is released as specified. - -It will also notice when a `HelmRelease` resource is updated, and -take action accordingly. - -## Setup and configuration - -`helm-operator` requires setup and offers customization though a multitude of flags. - -| flag | default | purpose -| ------------------------ | ----------------------------- | --- -| --kubeconfig | | Path to a kubeconfig. Only required if out-of-cluster. -| --master | | The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. -| --allow-namespace | | If set, this limits the scope to a single namespace. if not specified, all namespaces will be watched. -| **Tiller options** -| --tiller-ip | | Tiller IP address. Only required if out-of-cluster. -| --tiller-port | | Tiller port. -| --tiller-namespace | | Tiller namespace. If not provided, the default is kube-system. -| --tiller-tls-enable | `false` | Enable TLS communication with Tiller. If provided, requires TLSKey and TLSCert to be provided as well. -| --tiller-tls-verify | `false` | Verify TLS certificate from Tiller. Will enable TLS communication when provided. -| --tiller-tls-key-path | `/etc/fluxd/helm/tls.key` | Path to private key file used to communicate with the Tiller server. -| --tiller-tls-cert-path | `/etc/fluxd/helm/tls.crt` | Path to certificate file used to communicate with the Tiller server. -| --tiller-tls-ca-cert-path | | Path to CA certificate file used to validate the Tiller server. Required if tiller-tls-verify is enabled. -| --tiller-tls-hostname | | The server name used to verify the hostname on the returned certificates from the Tiller server. -| **repo chart changes** (none of these need overriding, usually) -| --charts-sync-interval | `3m` | Interval at which to check for changed charts. -| --git-timeout | `20s` | Duration after which git operations time out. -| --git-poll-interval | `5m` | Period on which to poll git chart sources for changes. -| --log-release-diffs | `false` | Log the diff when a chart release diverges. **Potentially insecure.** -| --update-chart-deps | `true` | Update chart dependencies before installing or upgrading a release. diff --git a/docs/helm-operator/troubleshooting.md b/docs/helm-operator/troubleshooting.md deleted file mode 100644 index 9a9d2f78b..000000000 --- a/docs/helm-operator/troubleshooting.md +++ /dev/null @@ -1,27 +0,0 @@ -# Troubleshooting - -Also see the [issues labeled with -`FAQ`](https://github.com/fluxcd/flux/labels/FAQ), which often -explain workarounds. - -## Error creating helm client: failed to append certificates from file: /etc/fluxd/helm-ca/ca.crt - -Your CA certificate content is not set correctly, check if your ConfigMap contains the correct values. Example: - -```bash -$ kubectl get configmaps flux-helm-tls-ca-config -o yaml -apiVersion: v1 -data: - ca.crt: | - -----BEGIN CERTIFICATE----- - .... - -----END CERTIFICATE----- -kind: ConfigMap -metadata: - creationTimestamp: 2018-07-04T15:27:25Z - name: flux-helm-tls-ca-config - namespace: helm-system - resourceVersion: "1267257" - selfLink: /api/v1/namespaces/helm-system/configmaps/flux-helm-tls-ca-config - uid: c106f866-7f9e-11e8-904a-025000000001 -``` diff --git a/docs/helm-operator/tutorials/get-started.md b/docs/helm-operator/tutorials/get-started.md deleted file mode 100644 index 3453cfd7c..000000000 --- a/docs/helm-operator/tutorials/get-started.md +++ /dev/null @@ -1,175 +0,0 @@ -# Get started with the Helm operator - -## Installing Helm / Tiller - -Generate certificates for Tiller and Flux. This will provide a CA, servercerts for Tiller and client certs for Helm / Flux. - -> **Note:** When creating the certificate for Tiller the Common Name should match the hostname you are connecting to from the Helm operator. - -The following script can be used for that (requires [cfssl](https://github.com/cloudflare/cfssl)): - -```bash -# TILLER_HOSTNAME=. -export TILLER_HOSTNAME=tiller-deploy.kube-system -export TILLER_SERVER=server -export USER_NAME=flux-helm-operator - -mkdir tls -cd ./tls - -# Prep the configuration -echo '{"CN":"CA","key":{"algo":"rsa","size":4096}}' | cfssl gencert -initca - | cfssljson -bare ca - -echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","server auth","client auth"]}}}' > ca-config.json - -# Create the tiller certificate -echo '{"CN":"'$TILLER_SERVER'","hosts":[""],"key":{"algo":"rsa","size":4096}}' | cfssl gencert \ - -config=ca-config.json -ca=ca.pem \ - -ca-key=ca-key.pem \ - -hostname="$TILLER_HOSTNAME" - | cfssljson -bare $TILLER_SERVER - -# Create a client certificate -echo '{"CN":"'$USER_NAME'","hosts":[""],"key":{"algo":"rsa","size":4096}}' | cfssl gencert \ - -config=ca-config.json -ca=ca.pem -ca-key=ca-key.pem \ - -hostname="$TILLER_HOSTNAME" - | cfssljson -bare $USER_NAME -``` - -Alternatively, you can follow the [Helm documentation for configuring TLS](https://docs.helm.sh/using_helm/#using-ssl-between-helm-and-tiller). - -Next deploy Helm with TLS and RBAC enabled; - -Create a file called `helm-rbac.yaml`. This contains all the RBAC configuration for Tiller: - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: tiller - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: tiller -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: tiller - namespace: kube-system - ---- -# Helm client serviceaccount -apiVersion: v1 -kind: ServiceAccount -metadata: - name: helm - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: Role -metadata: - name: tiller-user - namespace: kube-system -rules: -- apiGroups: - - "" - resources: - - pods/portforward - verbs: - - create -- apiGroups: - - "" - resources: - - pods - verbs: - - list ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: RoleBinding -metadata: - name: tiller-user-binding - namespace: kube-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: tiller-user -subjects: -- kind: ServiceAccount - name: helm - namespace: kube-system -``` - -Deploy Tiller: - -```bash -kubectl apply -f helm-rbac.yaml - -# Deploy helm with mutual TLS enabled. -# --history-max limits the maximum number of revisions Tiller stores; -# leaving it to the default (0) may result in request timeouts after N -# releases, due to the excessive amount of ConfigMaps Tiller will -# attempt to retrieve. -helm init --upgrade --service-account tiller --history-max 10 \ - --override 'spec.template.spec.containers[0].command'='{/tiller,--storage=secret}' \ - --tiller-tls \ - --tiller-tls-cert ./tls/server.pem \ - --tiller-tls-key ./tls/server-key.pem \ - --tiller-tls-verify \ - --tls-ca-cert ./tls/ca.pem -``` - -To check if Tiller installed succesfully with TLS enabled, try `helm ls`. This should give an error: - -```bash -# Should give an error -$ helm ls -Error: transport is closing -``` - -When providing the certificates, it should work correctly: - -```bash -helm --tls --tls-verify \ - --tls-ca-cert ./tls/ca.pem \ - --tls-cert ./tls/flux-helm-operator.pem \ - --tls-key ././tls/flux-helm-operator-key.pem \ - --tls-hostname tiller-deploy.kube-system \ - ls -``` - -## Deploy the Helm Operator - -First create a new Kubernetes TLS secret for the client certs; - -```bash -kubectl create secret tls helm-client --cert=tls/flux-helm-operator.pem --key=./tls/flux-helm-operator-key.pem -``` - -> **Note:** this has to be in the same namespace as the flux-helm-operator is deployed in. - -Deploy Flux with Helm; - -```bash -helm repo add fluxcd https://charts.fluxcd.io - -helm upgrade --install \ - --set helmOperator.create=true \ - --set git.url=$YOUR_GIT_REPO \ - --set helmOperator.tls.enable=true \ - --set helmOperator.tls.verify=true \ - --set helmOperator.tls.secretName=helm-client \ - --set helmOperator.tls.caContent="$(cat ./tls/ca.pem)" \ - flux \ - fluxcd/flux -``` - -> **Note:** -> -> - include --tls flags for `helm` as in the `helm ls` example, if talking to a tiller with TLS -> - optionally specify target --namespace - -## Check if it worked - -Use `kubectl logs` on the Helm Operator and observe the helm client being created. diff --git a/docs/helm-operator/tutorials/index.rst b/docs/helm-operator/tutorials/index.rst deleted file mode 100644 index a37796c8e..000000000 --- a/docs/helm-operator/tutorials/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Tutorials -========= - -.. toctree:: - :maxdepth: 1 - - get-started diff --git a/docs/index.rst b/docs/index.rst index bcf4f58b9..68d0f9af9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,12 +21,6 @@ Welcome to the Flux documentation! faq troubleshooting -.. toctree:: - :maxdepth: 3 - :caption: Helm operator - - helm-operator/index - .. toctree:: :maxdepth: 2 :caption: Contributing diff --git a/go.mod b/go.mod index e76fb198c..8bb131bc9 100644 --- a/go.mod +++ b/go.mod @@ -3,81 +3,44 @@ module github.com/weaveworks/flux go 1.12 require ( - cloud.google.com/go v0.37.4 // indirect - github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.4.2 - github.com/Masterminds/sprig v0.0.0-20190301161902-9f8fceff796f // indirect - github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/aws/aws-sdk-go v1.19.11 github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 - github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect - github.com/cyphar/filepath-securejoin v0.2.2 // indirect github.com/docker/distribution v0.0.0-00010101000000-000000000000 - github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 // indirect - github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect - github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect github.com/evanphx/json-patch v4.1.0+incompatible + github.com/fluxcd/helm-operator v1.0.0-rc1 github.com/ghodss/yaml v1.0.0 - github.com/go-kit/kit v0.8.0 - github.com/go-logfmt/logfmt v0.4.0 // indirect - github.com/gobwas/glob v0.2.3 // indirect - github.com/gogo/googleapis v1.2.0 // indirect - github.com/gogo/status v1.1.0 // indirect + github.com/go-kit/kit v0.9.0 github.com/golang/gddo v0.0.0-20190312205958-5a2505f3dbf0 - github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect - github.com/golang/protobuf v1.3.1 - github.com/google/go-cmp v0.2.0 - github.com/google/gofuzz v1.0.0 // indirect - github.com/google/uuid v1.1.1 // indirect - github.com/gophercloud/gophercloud v0.0.0-20190410012400-2c55d17f707c // indirect + github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/gorilla/mux v1.7.1 github.com/gorilla/websocket v1.4.0 - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect - github.com/hashicorp/golang-lru v0.5.1 // indirect - github.com/huandu/xstrings v1.2.0 // indirect github.com/imdario/mergo v0.3.7 - github.com/instrumenta/kubeval v0.0.0-20190720105720-70e32d660927 + github.com/instrumenta/kubeval v0.0.0-20190804145309-805845b47dfc github.com/justinbarrick/go-k8s-portforward v1.0.4-0.20190722134107-d79fe1b9d79d - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect - github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833 github.com/opencontainers/go-digest v1.0.0-rc1 - github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/opentracing-contrib/go-stdlib v0.0.0-20190324214902-3020fec0e66b // indirect - github.com/opentracing/opentracing-go v1.1.0 // indirect + github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9 // indirect github.com/pkg/errors v0.8.1 github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 - github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect - github.com/prometheus/common v0.3.0 // indirect - github.com/prometheus/procfs v0.0.0-20190412120340-e22ddced7142 // indirect github.com/ryanuber/go-glob v1.0.0 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd - github.com/sirupsen/logrus v1.4.1 // indirect github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 github.com/stretchr/testify v1.3.0 - github.com/uber/jaeger-client-go v2.16.0+incompatible // indirect - github.com/uber/jaeger-lib v2.0.0+incompatible // indirect github.com/weaveworks/common v0.0.0-20190410110702-87611edc252e github.com/weaveworks/go-checkpoint v0.0.0-20170503165305-ebbb8b0518ab - github.com/weaveworks/promrus v1.2.0 // indirect github.com/whilp/git-urls v0.0.0-20160530060445-31bac0d230fa - golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 - google.golang.org/api v0.3.2 // indirect - google.golang.org/appengine v1.5.0 // indirect - google.golang.org/grpc v1.20.0 // indirect gopkg.in/yaml.v2 v2.2.2 k8s.io/api v0.0.0-20190313235455-40a48860b5ab k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d k8s.io/client-go v11.0.0+incompatible - k8s.io/code-generator v0.0.0-00010101000000-000000000000 - k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a k8s.io/helm v2.13.1+incompatible - k8s.io/klog v0.3.1 + k8s.io/klog v0.3.3 ) replace github.com/docker/distribution => github.com/2opremio/distribution v0.0.0-20190419185413-6c9727e5e5de @@ -93,6 +56,5 @@ replace ( k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d k8s.io/apiserver => k8s.io/apiserver v0.0.0-20190708180123-608cd7da68f7 k8s.io/client-go => k8s.io/client-go v11.0.0+incompatible - k8s.io/code-generator => k8s.io/code-generator v0.0.0-20190311093542-50b561225d70 k8s.io/component-base => k8s.io/component-base v0.0.0-20190708175518-244289f83105 ) diff --git a/go.sum b/go.sum index 9adf93ce5..3b0c9f27f 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,7 @@ github.com/Azure/go-autorest v11.7.1+incompatible h1:M2YZIajBBVekV86x0rr1443Lc1F github.com/Azure/go-autorest v11.7.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= @@ -62,12 +63,16 @@ github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7Vpz github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fluxcd/helm-operator v1.0.0-rc1 h1:YOEmL3wjIvFxl7rFI0SwGKwEr8Wqm44vh7OucYCzs80= +github.com/fluxcd/helm-operator v1.0.0-rc1/go.mod h1:qBr+Yiqv9T2EjBsYzJNFQJ83b0ALM+aCJhxtPWyOP+E= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= @@ -80,6 +85,7 @@ github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q8 github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.2.0 h1:Z0v3OJDotX9ZBpdz2V+AI7F4fITSZhVE5mg6GQppwMM= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= @@ -94,15 +100,21 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= @@ -114,6 +126,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0= +github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20180807015416-4ea085781bae/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= github.com/gophercloud/gophercloud v0.0.0-20190410012400-2c55d17f707c h1:vGQ5eWkG5WkBdfGR+7J5yF2a6clwcUMM1r9fmRHPBVI= github.com/gophercloud/gophercloud v0.0.0-20190410012400-2c55d17f707c/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= @@ -135,6 +149,8 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -148,10 +164,14 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/instrumenta/kubeval v0.0.0-20190720105720-70e32d660927 h1:r1cvxQYvoKyFHUbPpDRAJw4QRvfyWyR55cp3mS1fklc= github.com/instrumenta/kubeval v0.0.0-20190720105720-70e32d660927/go.mod h1:HeTbS2psckzaIy3V3lGbcCvSGP9f9MvrQV6s9IWGy0w= +github.com/instrumenta/kubeval v0.0.0-20190804145309-805845b47dfc h1:2wBB02X45LugTLC2M5DtxFCAOK4+jgeV4Gtx1lPZu+4= +github.com/instrumenta/kubeval v0.0.0-20190804145309-805845b47dfc/go.mod h1:bpiMYvNpVxWjdJsS0hDRu9TrobT5GfWCZwJseGUstxE= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/justinbarrick/go-k8s-portforward v1.0.2 h1:unfs10u6NeEfPoQL7J5nyuOyAsZqGqlRV7I7qpr0+AU= @@ -182,6 +202,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI= github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= @@ -200,6 +221,8 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opentracing-contrib/go-stdlib v0.0.0-20190324214902-3020fec0e66b h1:N/+vVH19UEwFml23XATspYXdbpBU/oPvXy3CnkODjc0= github.com/opentracing-contrib/go-stdlib v0.0.0-20190324214902-3020fec0e66b/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= +github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9 h1:QsgXACQhd9QJhEmRumbsMQQvBtmdS0mafoVEBplWXEg= +github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -228,6 +251,7 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190412120340-e22ddced7142 h1:JO6VBMEDSBX/LT4GKwSdvuFigZNwVD4lkPyUE4BDCKE= github.com/prometheus/procfs v0.0.0-20190412120340-e22ddced7142/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -241,6 +265,8 @@ github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I= github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cobra v0.0.0-20180820174524-ff0d02e85550/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -265,6 +291,7 @@ github.com/uber/jaeger-lib v2.0.0+incompatible h1:iMSCV0rmXEogjNWPh2D0xk9YVKvrtG github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/weaveworks/common v0.0.0-20190410110702-87611edc252e h1:Y5z9Bu95l0laB3xOF3BT6LWkcR7VcRxN0n+nf2S72xA= github.com/weaveworks/common v0.0.0-20190410110702-87611edc252e/go.mod h1:pSm+0KR57BG3pvGoJWFXJSAC7+sEPewcvdt5StevL3A= +github.com/weaveworks/flux v0.0.0-20190729133003-c78ccd3706b5/go.mod h1:ZKP5adRfgMH9xY8/nG+Z+Z+fylHgimnL6/+/wzfQJr8= github.com/weaveworks/go-checkpoint v0.0.0-20170503165305-ebbb8b0518ab h1:mW+hgchD9qUUBqnuaDBj7BkcpFPk/FxeFcUFI5lvvUw= github.com/weaveworks/go-checkpoint v0.0.0-20170503165305-ebbb8b0518ab/go.mod h1:qkbvw5GPibQ/Nf7IZJL0UoLwmJ6858b4S/hUWRd+cH4= github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M= @@ -284,11 +311,18 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -299,10 +333,15 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -317,6 +356,10 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20180810153555-6e3c4e7365dd/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -330,10 +373,17 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138 h1:H3uGjxCR/6Ds0Mjgyp7LMK81+LvmbvWWEnJhzk1Pi9E= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.2 h1:iTp+3yyl/KOtxa/d1/JUE0GGSoR6FuW5udver22iwpw= @@ -382,19 +432,26 @@ k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7 k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/code-generator v0.0.0-20190311093542-50b561225d70 h1:lgPp615xLHxN84RBd+viA/oHzJfI0miFYFH4T9wpPQ4= -k8s.io/code-generator v0.0.0-20190311093542-50b561225d70/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8= -k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a h1:QoHVuRquf80YZ+/bovwxoMO3Q/A3nt3yTgS0/0nejuk= -k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/code-generator v0.0.0-20190511023357-639c964206c2/go.mod h1:YMQ7Lt97nW/I6nHACDccgS/sPAyrHQNans96RwPaSb8= +k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/helm v2.13.1+incompatible h1:qt0LBsHQ7uxCtS3F2r3XI0DNm8ml0xQeSJixUorDyn0= k8s.io/helm v2.13.1+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= -k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c= +k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/kube-openapi v0.0.0-20180509051136-39cb288412c4 h1:gW+EUB2I96nbxVenV/8ctfbACsHP+yxlT2dhMCsiy+s= k8s.io/kube-openapi v0.0.0-20180509051136-39cb288412c4/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c h1:kJCzg2vGCzah5icgkKR7O1Dzn0NA2iGlym27sb0ZfGE= +k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 h1:8r+l4bNWjRlsFYlQJnKJ2p7s1YQPj4XyXiJVqDHRx7c= k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +k8s.io/utils v0.0.0-20190712204705-3dccf664f023 h1:1H4Jyzb0z2X0GfBMTwRjnt5ejffRHrGftUgJcV/ZfDc= +k8s.io/utils v0.0.0-20190712204705-3dccf664f023/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/install/install_test.go b/install/install_test.go index f12dfbc17..f8a32c6f4 100644 --- a/install/install_test.go +++ b/install/install_test.go @@ -12,7 +12,7 @@ func testFillInTemplates(t *testing.T, params TemplateParameters) { assert.NoError(t, err) assert.Len(t, manifests, 5) for fileName, contents := range manifests { - validationResults, err := kubeval.Validate(contents, fileName) + validationResults, err := kubeval.Validate(contents) assert.NoError(t, err) for _, result := range validationResults { if len(result.Errors) > 0 { diff --git a/integrations/apis/flux.weave.works/v1beta1/types.go b/integrations/apis/flux.weave.works/v1beta1/types.go index c02e56b00..c4fb83536 100644 --- a/integrations/apis/flux.weave.works/v1beta1/types.go +++ b/integrations/apis/flux.weave.works/v1beta1/types.go @@ -15,7 +15,7 @@ import ( // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// FluxHelmRelease represents custom resource associated with a Helm Chart +// HelmRelease represents custom resource associated with a Helm Chart type HelmRelease struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` @@ -27,8 +27,8 @@ type HelmRelease struct { // ResourceID returns an ID made from the identifying parts of the // resource, as a convenience for Flux, which uses them // everywhere. -func (fhr HelmRelease) ResourceID() resource.ID { - return resource.MakeID(fhr.Namespace, "HelmRelease", fhr.Name) +func (hr HelmRelease) ResourceID() resource.ID { + return resource.MakeID(hr.Namespace, "HelmRelease", hr.Name) } // ReleaseName returns the configured release name, or constructs and @@ -36,37 +36,37 @@ func (fhr HelmRelease) ResourceID() resource.ID { // When the HelmRelease's metadata.namespace and spec.targetNamespace // differ, both are used in the generated name. // This name is used for naming and operating on the release in Helm. -func (fhr HelmRelease) ReleaseName() string { - if fhr.Spec.ReleaseName == "" { - namespace := fhr.GetDefaultedNamespace() - targetNamespace := fhr.GetTargetNamespace() +func (hr HelmRelease) ReleaseName() string { + if hr.Spec.ReleaseName == "" { + namespace := hr.GetDefaultedNamespace() + targetNamespace := hr.GetTargetNamespace() if namespace != targetNamespace { // prefix the releaseName with the administering HelmRelease namespace as well - return fmt.Sprintf("%s-%s-%s", namespace, targetNamespace, fhr.Name) + return fmt.Sprintf("%s-%s-%s", namespace, targetNamespace, hr.Name) } - return fmt.Sprintf("%s-%s", targetNamespace, fhr.Name) + return fmt.Sprintf("%s-%s", targetNamespace, hr.Name) } - return fhr.Spec.ReleaseName + return hr.Spec.ReleaseName } // GetDefaultedNamespace returns the HelmRelease's namespace // defaulting to the "default" if not set. -func (fhr HelmRelease) GetDefaultedNamespace() string { - if fhr.GetNamespace() == "" { +func (hr HelmRelease) GetDefaultedNamespace() string { + if hr.GetNamespace() == "" { return "default" } - return fhr.Namespace + return hr.Namespace } // GetTargetNamespace returns the configured release targetNamespace // defaulting to the namespace of the HelmRelease if not set. -func (fhr HelmRelease) GetTargetNamespace() string { - if fhr.Spec.TargetNamespace == "" { - return fhr.GetDefaultedNamespace() +func (hr HelmRelease) GetTargetNamespace() string { + if hr.Spec.TargetNamespace == "" { + return hr.GetDefaultedNamespace() } - return fhr.Spec.TargetNamespace + return hr.Spec.TargetNamespace } // ValuesFromSource represents a source of values. @@ -290,7 +290,7 @@ func (in *HelmValues) DeepCopyInto(out *HelmValues) { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// HelmReleaseList is a list of FluxHelmRelease resources +// HelmReleaseList is a list of HelmRelease resources type HelmReleaseList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` diff --git a/integrations/helm/api/api.go b/integrations/helm/api/api.go deleted file mode 100644 index c200b1960..000000000 --- a/integrations/helm/api/api.go +++ /dev/null @@ -1,7 +0,0 @@ -package api - -// Server is the interface that must be satisfied in order to serve -// HTTP API requests. -type Server interface { - SyncMirrors() -} diff --git a/integrations/helm/chartsync/chartsync.go b/integrations/helm/chartsync/chartsync.go deleted file mode 100644 index 9553627dc..000000000 --- a/integrations/helm/chartsync/chartsync.go +++ /dev/null @@ -1,654 +0,0 @@ -/* - -This package has the algorithm for making sure the Helm releases in -the cluster match what are defined in the HelmRelease resources. - -There are several ways they can be mismatched. Here's how they are -reconciled: - - 1a. There is a HelmRelease resource, but no corresponding - release. This can happen when the helm operator is first run, for - example. - - 1b. The release corresponding to a HelmRelease has been updated by - some other means, perhaps while the operator wasn't running. This - is also checked, by doing a dry-run release and comparing the result - to the release. - - 2. The chart has changed in git, meaning the release is out of - date. The ChartChangeSync responds to new git commits by looking up - each chart that makes use of the mirror that has new commits, - replacing the clone for that chart, and scheduling a new release. - -1a.) and 1b.) run on the same schedule, and 2.) is run when a git -mirror reports it has fetched from upstream _and_ (upon checking) the -head of the branch has changed. - -Since both 1*.) and 2.) look at the charts in the git repo, but run on -different schedules (non-deterministically), there's a chance that -they can fight each other. For example, the git mirror may fetch new -commits which are used in 1), then treated as changes subsequently by -2). To keep consistency between the two, the current revision of a -repo is used by 1), and advanced only by 2). - -*/ -package chartsync - -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" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" - hapi_chart "k8s.io/helm/pkg/proto/hapi/chart" - hapi_release "k8s.io/helm/pkg/proto/hapi/release" - - "github.com/weaveworks/flux/git" - "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" - fluxv1beta1 "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" - ifclientset "github.com/weaveworks/flux/integrations/client/clientset/versioned" - iflister "github.com/weaveworks/flux/integrations/client/listers/flux.weave.works/v1beta1" - helmop "github.com/weaveworks/flux/integrations/helm" - "github.com/weaveworks/flux/integrations/helm/release" - "github.com/weaveworks/flux/integrations/helm/status" -) - -const ( - // condition change reasons - ReasonGitNotReady = "GitRepoNotCloned" - ReasonDownloadFailed = "RepoFetchFailed" - ReasonDownloaded = "RepoChartInCache" - ReasonInstallFailed = "HelmInstallFailed" - ReasonDependencyFailed = "UpdateDependencyFailed" - ReasonUpgradeFailed = "HelmUpgradeFailed" - ReasonRollbackFailed = "HelmRollbackFailed" - ReasonCloned = "GitRepoCloned" - ReasonSuccess = "HelmSuccess" -) - -type Clients struct { - KubeClient kubernetes.Clientset - IfClient ifclientset.Clientset - FhrLister iflister.HelmReleaseLister -} - -type Config struct { - ChartCache string - LogDiffs bool - UpdateDeps bool - GitTimeout time.Duration - GitPollInterval time.Duration -} - -func (c Config) WithDefaults() Config { - if c.ChartCache == "" { - c.ChartCache = "/tmp" - } - return c -} - -// clone puts a local git clone together with its state (head -// revision), so we can keep track of when it needs to be updated. -type clone struct { - export *git.Export - remote string - ref string - head string -} - -// ReleaseQueue is an add-only workqueue.RateLimitingInterface -type ReleaseQueue interface { - AddRateLimited(item interface{}) -} - -type ChartChangeSync struct { - logger log.Logger - kubeClient kubernetes.Clientset - ifClient ifclientset.Clientset - fhrLister iflister.HelmReleaseLister - release *release.Release - releaseQueue ReleaseQueue - config Config - - mirrors *git.Mirrors - - clonesMu sync.Mutex - clones map[string]clone - - namespace string -} - -func New(logger log.Logger, clients Clients, release *release.Release, releaseQueue ReleaseQueue, config Config, namespace string) *ChartChangeSync { - return &ChartChangeSync{ - logger: logger, - kubeClient: clients.KubeClient, - ifClient: clients.IfClient, - fhrLister: clients.FhrLister, - release: release, - releaseQueue: releaseQueue, - config: config.WithDefaults(), - mirrors: git.NewMirrors(), - clones: make(map[string]clone), - namespace: namespace, - } -} - -// Run creates a syncing loop that will reconcile differences between -// Helm releases in the cluster, what HelmRelease declare, and -// changes in the git repos mentioned by any HelmRelease. -func (chs *ChartChangeSync) Run(stopCh <-chan struct{}, errc chan error, wg *sync.WaitGroup) { - chs.logger.Log("info", "starting git chart sync loop") - - wg.Add(1) - go func() { - defer runtime.HandleCrash() - defer func() { - chs.mirrors.StopAllAndWait() - wg.Done() - }() - - for { - select { - case mirrorsChanged := <-chs.mirrors.Changes(): - for mirror := range mirrorsChanged { - resources, err := chs.getCustomResourcesForMirror(mirror) - if err != nil { - chs.logger.Log("warning", "failed to get custom resources", "err", err) - continue - } - - // Retrieve the mirror we got a change signal for - repo, ok := chs.mirrors.Get(mirror) - if !ok { - // Then why .. did you say .. it had changed? It may have been removed. Add it back and let it signal again. - chs.logger.Log("warning", "mirrored git repo disappeared after signalling change", "repo", mirror) - for _, fhr := range resources { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionUnknown, ReasonGitNotReady, "git mirror missing; starting mirroring again") - chs.maybeMirror(fhr) - } - continue - } - - // Ensure the repo is ready - status, err := repo.Status() - if status != git.RepoReady { - chs.logger.Log("info", "repo not ready yet, while attempting chart sync", "repo", mirror, "status", string(status)) - for _, fhr := range resources { - // TODO(michael) log if there's a problem with the following? - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionUnknown, ReasonGitNotReady, err.Error()) - } - continue - } - - // Determine if we need to update the clone and - // schedule an upgrade for every HelmRelease that - // makes use of the mirror - for _, fhr := range resources { - ref := fhr.Spec.ChartSource.GitChartSource.RefOrDefault() - path := fhr.Spec.ChartSource.GitChartSource.Path - releaseName := fhr.ReleaseName() - - ctx, cancel := context.WithTimeout(context.Background(), helmop.GitOperationTimeout) - refHead, err := repo.Revision(ctx, ref) - cancel() - if err != nil { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionFalse, ReasonGitNotReady, "problem cloning from local git mirror: "+err.Error()) - chs.logger.Log("warning", "could not get revision for ref while checking for changes", "resource", fhr.ResourceID().String(), "repo", mirror, "ref", ref, "err", err) - continue - } - - // The git repo of this appears to have had commits since we last saw it, - // check explicitly whether we should update its clone. - chs.clonesMu.Lock() - cloneForChart, ok := chs.clones[releaseName] - chs.clonesMu.Unlock() - - if ok { // found clone - ctx, cancel := context.WithTimeout(context.Background(), helmop.GitOperationTimeout) - commits, err := repo.CommitsBetween(ctx, cloneForChart.head, refHead, path) - cancel() - if err != nil { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionFalse, ReasonGitNotReady, "problem cloning from local git mirror: "+err.Error()) - chs.logger.Log("warning", "could not get revision for ref while checking for changes", "resource", fhr.ResourceID().String(), "repo", mirror, "ref", ref, "err", err) - continue - } - ok = len(commits) == 0 - } - - if !ok { // didn't find clone, or it needs updating - ctx, cancel := context.WithTimeout(context.Background(), helmop.GitOperationTimeout) - newClone, err := repo.Export(ctx, refHead) - cancel() - if err != nil { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionFalse, ReasonGitNotReady, "problem cloning from local git mirror: "+err.Error()) - chs.logger.Log("warning", "could not clone from mirror while checking for changes", "resource", fhr.ResourceID().String(), "repo", mirror, "ref", ref, "err", err) - continue - } - newCloneForChart := clone{remote: mirror, ref: ref, head: refHead, export: newClone} - chs.clonesMu.Lock() - chs.clones[releaseName] = newCloneForChart - chs.clonesMu.Unlock() - if cloneForChart.export != nil { - cloneForChart.export.Clean() - } - - // we have a (new) clone, enqueue a release - cacheKey, err := cache.MetaNamespaceKeyFunc(fhr.GetObjectMeta()) - if err != nil { - continue - } - chs.logger.Log("info", "enqueing release upgrade due to change in git chart source", "resource", fhr.ResourceID().String()) - chs.releaseQueue.AddRateLimited(cacheKey) - } - } - } - case <-stopCh: - chs.logger.Log("stopping", "true") - return - } - } - }() -} - -func mirrorName(chartSource *fluxv1beta1.GitChartSource) string { - return chartSource.GitURL // TODO(michael) this will not always be the case; e.g., per namespace, per auth -} - -// maybeMirror starts mirroring the repo needed by a HelmRelease, -// if necessary -func (chs *ChartChangeSync) maybeMirror(fhr fluxv1beta1.HelmRelease) { - chartSource := fhr.Spec.ChartSource.GitChartSource - if chartSource != nil { - if ok := chs.mirrors.Mirror( - mirrorName(chartSource), - git.Remote{chartSource.GitURL}, git.Timeout(chs.config.GitTimeout), git.PollInterval(chs.config.GitPollInterval), git.ReadOnly, - ); !ok { - chs.logger.Log("info", "started mirroring repo", "repo", chartSource.GitURL) - } - } -} - -// CompareValuesChecksum recalculates the checksum of the values -// and compares it to the last recorded checksum. -func (chs *ChartChangeSync) CompareValuesChecksum(fhr fluxv1beta1.HelmRelease) bool { - chartPath, ok := "", false - if fhr.Spec.ChartSource.GitChartSource != nil { - // We need to hold the lock until have compared the values, - // so that the clone doesn't get swapped out from under us. - chs.clonesMu.Lock() - defer chs.clonesMu.Unlock() - chartPath, _, ok = chs.getGitChartSource(fhr) - if !ok { - return false - } - } else if fhr.Spec.ChartSource.RepoChartSource != nil { - chartPath, _, ok = chs.getRepoChartSource(fhr) - if !ok { - return false - } - } - - values, err := release.Values(chs.kubeClient.CoreV1(), fhr.Namespace, chartPath, fhr.GetValuesFromSources(), fhr.Spec.Values) - if err != nil { - return false - } - - strValues, err := values.YAML() - if err != nil { - return false - } - - return fhr.Status.ValuesChecksum == release.ValuesChecksum([]byte(strValues)) -} - -// ReconcileReleaseDef asks the ChartChangeSync to examine the release -// associated with a HelmRelease, and install or upgrade the -// release if the chart it refers to has changed. -func (chs *ChartChangeSync) ReconcileReleaseDef(fhr fluxv1beta1.HelmRelease) { - defer chs.updateObservedGeneration(fhr) - - releaseName := fhr.ReleaseName() - - // Attempt to retrieve an upgradable release, in case no release - // or error is returned, install it. - rel, err := chs.release.GetUpgradableRelease(releaseName) - if err != nil { - chs.logger.Log("warning", "unable to proceed with release", "resource", fhr.ResourceID().String(), "release", releaseName, "err", err) - return - } - - opts := release.InstallOptions{DryRun: false} - - chartPath, chartRevision, ok := "", "", false - if fhr.Spec.ChartSource.GitChartSource != nil { - // We need to hold the lock until after we're done releasing - // the chart, so that the clone doesn't get swapped out from - // under us. TODO(michael) consider having a lock per clone. - chs.clonesMu.Lock() - defer chs.clonesMu.Unlock() - chartPath, chartRevision, ok = chs.getGitChartSource(fhr) - if !ok { - return - } - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionTrue, ReasonCloned, "successfully cloned git repo") - } else if fhr.Spec.ChartSource.RepoChartSource != nil { - chartPath, chartRevision, ok = chs.getRepoChartSource(fhr) - if !ok { - return - } - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionTrue, ReasonDownloaded, "chart fetched: "+filepath.Base(chartPath)) - } - - if rel == nil { - _, checksum, err := chs.release.Install(chartPath, releaseName, fhr, release.InstallAction, opts, &chs.kubeClient) - if err != nil { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseReleased, v1.ConditionFalse, ReasonInstallFailed, err.Error()) - chs.logger.Log("warning", "failed to install chart", "resource", fhr.ResourceID().String(), "err", err) - return - } - chs.setCondition(fhr, fluxv1beta1.HelmReleaseReleased, v1.ConditionTrue, ReasonSuccess, "helm install succeeded") - if err = status.SetReleaseRevision(chs.ifClient.FluxV1beta1().HelmReleases(fhr.Namespace), fhr, chartRevision); err != nil { - chs.logger.Log("warning", "could not update the release revision", "resource", fhr.ResourceID().String(), "err", err) - } - if err = status.SetValuesChecksum(chs.ifClient.FluxV1beta1().HelmReleases(fhr.Namespace), fhr, checksum); err != nil { - chs.logger.Log("warning", "could not update the values checksum", "namespace", fhr.Namespace, "resource", fhr.Name, "err", err) - } - return - } - - if !chs.release.OwnedByHelmRelease(rel, fhr) { - msg := fmt.Sprintf("release '%s' does not belong to HelmRelease", releaseName) - chs.setCondition(fhr, fluxv1beta1.HelmReleaseReleased, v1.ConditionFalse, ReasonUpgradeFailed, msg) - chs.logger.Log("warning", msg+", this may be an indication that multiple HelmReleases with the same release name exist", "resource", fhr.ResourceID().String()) - return - } - - changed, err := chs.shouldUpgrade(chartPath, rel, fhr) - if err != nil { - chs.logger.Log("warning", "unable to determine if release has changed", "resource", fhr.ResourceID().String(), "err", err) - return - } - if changed { - cFhr, err := chs.ifClient.FluxV1beta1().HelmReleases(fhr.Namespace).Get(fhr.Name, metav1.GetOptions{}) - if err != nil { - chs.logger.Log("warning", "failed to retrieve HelmRelease scheduled for upgrade", "resource", fhr.ResourceID().String(), "err", err) - return - } - if diff := cmp.Diff(fhr.Spec, cFhr.Spec); diff != "" { - chs.logger.Log("warning", "HelmRelease spec has diverged since we calculated if we should upgrade, skipping upgrade", "resource", fhr.ResourceID().String()) - return - } - _, checksum, err := chs.release.Install(chartPath, releaseName, fhr, release.UpgradeAction, opts, &chs.kubeClient) - if err != nil { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseReleased, v1.ConditionFalse, ReasonUpgradeFailed, err.Error()) - if err = status.SetValuesChecksum(chs.ifClient.FluxV1beta1().HelmReleases(fhr.Namespace), fhr, checksum); err != nil { - chs.logger.Log("warning", "could not update the values checksum", "namespace", fhr.Namespace, "resource", fhr.Name, "err", err) - } - chs.logger.Log("warning", "failed to upgrade chart", "resource", fhr.ResourceID().String(), "err", err) - chs.RollbackRelease(fhr) - return - } - chs.setCondition(fhr, fluxv1beta1.HelmReleaseReleased, v1.ConditionTrue, ReasonSuccess, "helm upgrade succeeded") - if err = status.SetReleaseRevision(chs.ifClient.FluxV1beta1().HelmReleases(fhr.Namespace), fhr, chartRevision); err != nil { - chs.logger.Log("warning", "could not update the release revision", "resource", fhr.ResourceID().String(), "err", err) - } - if err = status.SetValuesChecksum(chs.ifClient.FluxV1beta1().HelmReleases(fhr.Namespace), fhr, checksum); err != nil { - chs.logger.Log("warning", "could not update the values checksum", "namespace", fhr.Namespace, "resource", fhr.Name, "err", err) - } - return - } -} - -// RollbackRelease rolls back a helm release -func (chs *ChartChangeSync) RollbackRelease(fhr fluxv1beta1.HelmRelease) { - defer chs.updateObservedGeneration(fhr) - - if !fhr.Spec.Rollback.Enable { - return - } - - releaseName := fhr.ReleaseName() - _, err := chs.release.Rollback(releaseName, fhr) - if err != nil { - chs.logger.Log("warning", "unable to rollback chart release", "resource", fhr.ResourceID().String(), "release", releaseName, "err", err) - chs.setCondition(fhr, fluxv1beta1.HelmReleaseRolledBack, v1.ConditionFalse, ReasonRollbackFailed, err.Error()) - } - chs.setCondition(fhr, fluxv1beta1.HelmReleaseRolledBack, v1.ConditionTrue, ReasonSuccess, "helm rollback succeeded") -} - -// DeleteRelease deletes the helm release associated with a -// HelmRelease. This exists mainly so that the operator code can -// call it when it is handling a resource deletion. -func (chs *ChartChangeSync) DeleteRelease(fhr fluxv1beta1.HelmRelease) { - // FIXME(michael): these may need to stop mirroring a repo. - name := fhr.ReleaseName() - err := chs.release.Delete(name) - if err != nil { - chs.logger.Log("warning", "chart release not deleted", "resource", fhr.ResourceID().String(), "release", name, "err", err) - } - - // Remove the clone we may have for this HelmRelease - chs.clonesMu.Lock() - cloneForChart, ok := chs.clones[name] - if ok { - if cloneForChart.export != nil { - cloneForChart.export.Clean() - } - delete(chs.clones, name) - } - chs.clonesMu.Unlock() -} - -// SyncMirrors instructs all mirrors to refresh from their upstream. -func (chs *ChartChangeSync) SyncMirrors() { - chs.logger.Log("info", "starting mirror sync") - for _, err := range chs.mirrors.RefreshAll(chs.config.GitTimeout) { - chs.logger.Log("error", fmt.Sprintf("failure while syncing mirror: %s", err)) - } - chs.logger.Log("info", "finished syncing mirrors") -} - -// getCustomResourcesForMirror retrieves all the resources that make -// use of the given mirror from the lister. -func (chs *ChartChangeSync) getCustomResourcesForMirror(mirror string) ([]fluxv1beta1.HelmRelease, error) { - var fhrs []v1beta1.HelmRelease - list, err := chs.fhrLister.List(labels.Everything()) - if err != nil { - return nil, err - } - - for _, fhr := range list { - if fhr.Spec.GitChartSource == nil { - continue - } - if mirror != mirrorName(fhr.Spec.GitChartSource) { - continue - } - fhrs = append(fhrs, *fhr) - } - return fhrs, nil -} - -// setCondition saves the status of a condition. -func (chs *ChartChangeSync) setCondition(hr fluxv1beta1.HelmRelease, typ fluxv1beta1.HelmReleaseConditionType, st v1.ConditionStatus, reason, message string) error { - hrClient := chs.ifClient.FluxV1beta1().HelmReleases(hr.Namespace) - condition := status.NewCondition(typ, st, reason, message) - return status.SetCondition(hrClient, hr, condition) -} - -// updateObservedGeneration updates the observed generation of the -// given HelmRelease to the generation. -func (chs *ChartChangeSync) updateObservedGeneration(hr fluxv1beta1.HelmRelease) error { - hrClient := chs.ifClient.FluxV1beta1().HelmReleases(hr.Namespace) - - return status.SetObservedGeneration(hrClient, hr, hr.Generation) -} - -func (chs *ChartChangeSync) getGitChartSource(fhr v1beta1.HelmRelease) (string, string, bool) { - chartPath, chartRevision := "", "" - chartSource := fhr.Spec.GitChartSource - if chartSource == nil { - return chartPath, chartRevision, false - } - - releaseName := fhr.ReleaseName() - chartClone, ok := chs.clones[releaseName] - // Validate the clone we have for the release is the same as - // is being referenced in the chart source. - if ok { - ok = chartClone.remote == chartSource.GitURL && chartClone.ref == chartSource.RefOrDefault() - if !ok { - if chartClone.export != nil { - chartClone.export.Clean() - } - delete(chs.clones, releaseName) - } - } - - // FIXME(michael): if it's not cloned, and it's not going to - // be, we might not want to wait around until the next tick - // before reporting what's wrong with it. But if we just use - // repo.Ready(), we'll force all charts through that blocking - // code, rather than waiting for things to sync in good time. - if !ok { - repo, ok := chs.mirrors.Get(mirrorName(chartSource)) - if !ok { - chs.maybeMirror(fhr) - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionUnknown, ReasonGitNotReady, "git repo "+chartSource.GitURL+" not mirrored yet") - chs.logger.Log("info", "chart repo not cloned yet", "resource", fhr.ResourceID().String()) - } else { - status, err := repo.Status() - if status != git.RepoReady { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionUnknown, ReasonGitNotReady, "git repo not mirrored yet: "+err.Error()) - chs.logger.Log("info", "chart repo not ready yet", "resource", fhr.ResourceID().String(), "status", string(status), "err", err) - } - } - return chartPath, chartRevision, false - } - chartPath = filepath.Join(chartClone.export.Dir(), chartSource.Path) - chartRevision = chartClone.head - - if chs.config.UpdateDeps && !fhr.Spec.ChartSource.GitChartSource.SkipDepUpdate { - if err := updateDependencies(chartPath, ""); err != nil { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseReleased, v1.ConditionFalse, ReasonDependencyFailed, err.Error()) - chs.logger.Log("warning", "failed to update chart dependencies", "resource", fhr.ResourceID().String(), "err", err) - return chartPath, chartRevision, false - } - } - - return chartPath, chartRevision, true -} - -func (chs *ChartChangeSync) getRepoChartSource(fhr v1beta1.HelmRelease) (string, string, bool) { - chartPath, chartRevision := "", "" - chartSource := fhr.Spec.ChartSource.RepoChartSource - if chartSource == nil { - return chartPath, chartRevision, false - } - - path, err := ensureChartFetched(chs.config.ChartCache, chartSource) - if err != nil { - chs.setCondition(fhr, fluxv1beta1.HelmReleaseChartFetched, v1.ConditionFalse, ReasonDownloadFailed, "chart download failed: "+err.Error()) - chs.logger.Log("info", "chart download failed", "resource", fhr.ResourceID().String(), "err", err) - return chartPath, chartRevision, false - } - - chartPath = path - chartRevision = chartSource.Version - - return chartPath, chartRevision, true -} - -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. -func (chs *ChartChangeSync) shouldUpgrade(chartsRepo string, currRel *hapi_release.Release, fhr fluxv1beta1.HelmRelease) (bool, error) { - if currRel == nil { - return false, fmt.Errorf("no chart release provided for %v", fhr.GetName()) - } - - currVals := currRel.GetConfig() - currChart := currRel.GetChart() - - // Get the desired release state - opts := release.InstallOptions{DryRun: true} - tempRelName := string(fhr.UID) - desRel, _, err := chs.release.Install(chartsRepo, tempRelName, fhr, release.InstallAction, opts, &chs.kubeClient) - if err != nil { - return false, err - } - desVals := desRel.GetConfig() - desChart := desRel.GetChart() - - // compare values - if diff := cmp.Diff(currVals, desVals); diff != "" { - if chs.config.LogDiffs { - chs.logger.Log("info", fmt.Sprintf("release %s: values have diverged", currRel.GetName()), "resource", fhr.ResourceID().String(), "diff", diff) - } - return true, nil - } - - // compare chart - if diff := cmp.Diff(sortChartFields(currChart), sortChartFields(desChart)); diff != "" { - if chs.config.LogDiffs { - chs.logger.Log("info", fmt.Sprintf("release %s: chart has diverged", currRel.GetName()), "resource", fhr.ResourceID().String(), "diff", diff) - } - return true, nil - } - - return false, nil -} diff --git a/integrations/helm/chartsync/deps.go b/integrations/helm/chartsync/deps.go deleted file mode 100644 index 2fadf2159..000000000 --- a/integrations/helm/chartsync/deps.go +++ /dev/null @@ -1,72 +0,0 @@ -package chartsync - -import ( - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" -) - -// helmHome is optional; if it's "", it's left to default -func updateDependencies(chartDir, helmhome string) error { - var hasLockFile bool - - // sanity check: does the chart directory exist - if chartDir == "" { - return errors.New("empty path to chart supplied") - } - chartInfo, err := os.Stat(chartDir) - switch { - case os.IsNotExist(err): - return fmt.Errorf("chart path %s does not exist", chartDir) - case err != nil: - return err - case !chartInfo.IsDir(): - return fmt.Errorf("chart path %s is not a directory", chartDir) - } - - // check if the requirements file exists - reqFilePath := filepath.Join(chartDir, "requirements.yaml") - reqInfo, err := os.Stat(reqFilePath) - if err != nil || reqInfo.IsDir() { - return nil - } - - // We are going to use `helm dep build`, which tries to update the - // dependencies in charts/ by looking at the file - // `requirements.lock` in the chart directory. If the lockfile - // does not match what is specified in requirements.yaml, it will - // error out. - // - // If that file doesn't exist, `helm dep build` will fall back on - // `helm dep update`, which populates the charts/ directory _and_ - // creates the lockfile. So that it will have the same behaviour - // the next time it attempts a release, remove the lockfile if it - // was created by helm. - lockfilePath := filepath.Join(chartDir, "requirements.lock") - info, err := os.Stat(lockfilePath) - hasLockFile = (err == nil && !info.IsDir()) - if !hasLockFile { - defer os.Remove(lockfilePath) - } - - cmd := exec.Command("helm", "repo", "update") - if helmhome != "" { - cmd.Args = append(cmd.Args, "--home", helmhome) - } - out, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("could not update repo: %s", string(out)) - } - - cmd = exec.Command("helm", "dep", "build", ".") - cmd.Dir = chartDir - - out, err = cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("could not update dependencies in %s: %s", chartDir, string(out)) - } - - return nil -} diff --git a/integrations/helm/chartsync/deps_test.go b/integrations/helm/chartsync/deps_test.go deleted file mode 100644 index bc19f5354..000000000 --- a/integrations/helm/chartsync/deps_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package chartsync - -import ( - "io/ioutil" - "os" - "os/exec" - "testing" -) - -func Test_updateDependencies(t *testing.T) { - helmhome, err := ioutil.TempDir("", "flux-helm") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(helmhome) - cmd := exec.Command("helm", "init", "--client-only", "--home", helmhome) - if err := cmd.Run(); err != nil { - t.Fatal(err) - } - - type args struct { - chartDir string - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "Chart without dependencies", - args: args{ - chartDir: "test/chart-without-deps", - }, - wantErr: false, - }, - { - name: "non-existent chart", - args: args{ - chartDir: "test/folder-doesnt-exist", - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := updateDependencies(tt.args.chartDir, helmhome); (err != nil) != tt.wantErr { - t.Errorf("updateDependencies() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/integrations/helm/chartsync/download.go b/integrations/helm/chartsync/download.go deleted file mode 100644 index 9f573d96f..000000000 --- a/integrations/helm/chartsync/download.go +++ /dev/null @@ -1,133 +0,0 @@ -package chartsync - -import ( - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/spf13/pflag" - "k8s.io/helm/pkg/getter" - helmenv "k8s.io/helm/pkg/helm/environment" - "k8s.io/helm/pkg/repo" - - flux_v1beta1 "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" -) - -// makeChartPath gives the expected filesystem location for a chart, -// without testing whether the file exists or not. -func makeChartPath(base string, source *flux_v1beta1.RepoChartSource) string { - // We don't need to obscure the location of the charts in the - // filesystem; but we do need a stable, filesystem-friendly path - // to them that is based on the URL. - repoPath := filepath.Join(base, base64.URLEncoding.EncodeToString([]byte(source.CleanRepoURL()))) - if err := os.MkdirAll(repoPath, os.FileMode(os.ModeDir+0660)); err != nil { - panic(err) - } - filename := fmt.Sprintf("%s-%s.tgz", source.Name, source.Version) - return filepath.Join(repoPath, filename) -} - -// ensureChartFetched returns the path to a downloaded chart, fetching -// it first if necessary. It always returns the expected path to the -// chart, and either an error or nil. -func ensureChartFetched(base string, source *flux_v1beta1.RepoChartSource) (string, error) { - chartPath := makeChartPath(base, source) - stat, err := os.Stat(chartPath) - switch { - case os.IsNotExist(err): - return chartPath, downloadChart(chartPath, source) - case err != nil: - return chartPath, err - case stat.IsDir(): - return chartPath, errors.New("path to chart exists but is a directory") - } - return chartPath, nil -} - -// downloadChart attempts to fetch a chart tarball, given the name, -// version and repo URL in `source`, and the path to write the file -// to in `destFile`. -func downloadChart(destFile string, source *flux_v1beta1.RepoChartSource) error { - // Helm's support libs are designed to be driven by the - // command-line client, so there are some inevitable CLI-isms, - // like getting values from flags and the environment. None of - // these things are directly relevant here, _except_ the HELM_HOME - // environment entry. Since there's that exception, we must go - // through the ff (following faff). - var settings helmenv.EnvSettings - // Add the flag definitions .. - flags := pflag.NewFlagSet("helm-env", pflag.ContinueOnError) - settings.AddFlags(flags) - // .. but we're not expecting any _actual_ flags, so there's no - // Parse. This next bit will use any settings from the - // environment. - settings.Init(flags) - getters := getter.All(settings) // <-- aaaand this is the payoff - - // This resolves the repo URL, chart name and chart version to a - // URL for the chart. To be able to resolve the chart name and - // version to a URL, we have to have the index file; and to have - // that, we may need to authenticate. The credentials will be in - // repositories.yaml. - repoFile, err := repo.LoadRepositoriesFile(settings.Home.RepositoryFile()) - if err != nil { - return err - } - - // Now find the entry for the repository, if there is one. If not, - // we'll assume there's no auth needed. - repoEntry := &repo.Entry{} - for _, entry := range repoFile.Repositories { - if urlsMatch(entry.URL, source.CleanRepoURL()) { - repoEntry = entry - break - } - } - - // TODO(michael): could look for an existing index file here, - // and/or update it. Then we're _pretty_ close to just using - // `repo.DownloadTo(...)`. - chartURL, err := repo.FindChartInAuthRepoURL(source.CleanRepoURL(), repoEntry.Username, repoEntry.Password, source.Name, source.Version, repoEntry.CertFile, repoEntry.KeyFile, repoEntry.CAFile, getters) - if err != nil { - return err - } - - // Here I'm reproducing the useful part (for us) of - // `k8s.io/helm/pkg/downloader.Downloader.ResolveChartVersion(...)`, - // stepping around `DownloadTo(...)` as it's too general. The - // former interacts with Helm's local caching, which would mean - // having to maintain the local cache. Since we already have the - // information we need, we can just go ahead and get the file. - u, err := url.Parse(chartURL) - if err != nil { - return err - } - getterConstructor, err := getters.ByScheme(u.Scheme) - if err != nil { - return err - } - - g, err := getterConstructor(chartURL, repoEntry.CertFile, repoEntry.KeyFile, repoEntry.CAFile) - if t, ok := g.(*getter.HttpGetter); ok { - t.SetCredentials(repoEntry.Username, repoEntry.Password) - } - - chartBytes, err := g.Get(u.String()) - if err != nil { - return err - } - if err := ioutil.WriteFile(destFile, chartBytes.Bytes(), 0644); err != nil { - return err - } - - return nil -} - -func urlsMatch(entryURL, sourceURL string) bool { - return strings.TrimRight(entryURL, "/") == strings.TrimRight(sourceURL, "/") -} diff --git a/integrations/helm/chartsync/test/chart-without-deps/.helmignore b/integrations/helm/chartsync/test/chart-without-deps/.helmignore deleted file mode 100644 index f0c131944..000000000 --- a/integrations/helm/chartsync/test/chart-without-deps/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/integrations/helm/chartsync/test/chart-without-deps/Chart.yaml b/integrations/helm/chartsync/test/chart-without-deps/Chart.yaml deleted file mode 100644 index a062526fb..000000000 --- a/integrations/helm/chartsync/test/chart-without-deps/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A bare bones Helm chart for Kubernetes -name: chart-without-deps -version: 0.1.0 diff --git a/integrations/helm/chartsync/test/chart-without-deps/values.yaml b/integrations/helm/chartsync/test/chart-without-deps/values.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/integrations/helm/helm.go b/integrations/helm/helm.go deleted file mode 100644 index f803ee8c3..000000000 --- a/integrations/helm/helm.go +++ /dev/null @@ -1,117 +0,0 @@ -package helm - -import ( - "fmt" - "time" - - "github.com/go-kit/kit/log" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - k8shelm "k8s.io/helm/pkg/helm" - rls "k8s.io/helm/pkg/proto/hapi/services" - "k8s.io/helm/pkg/tlsutil" -) - -const ( - GitOperationTimeout = 30 * time.Second -) - -type TillerOptions struct { - Host string - Port string - Namespace string - TLSVerify bool - TLSEnable bool - TLSKey string - TLSCert string - TLSCACert string - TLSHostname string -} - -// Helm struct provides access to helm client -type Helm struct { - logger log.Logger - Host string - *k8shelm.Client -} - -// newClient creates a new helm client -func newClient(kubeClient *kubernetes.Clientset, opts TillerOptions) (*k8shelm.Client, string, error) { - host, err := tillerHost(kubeClient, opts) - if err != nil { - return &k8shelm.Client{}, "", err - } - - //host = "tiller-deploy.kube-system:44134" - - options := []k8shelm.Option{k8shelm.Host(host)} - if opts.TLSVerify || opts.TLSEnable { - tlsopts := tlsutil.Options{ - KeyFile: opts.TLSKey, - CertFile: opts.TLSCert, - InsecureSkipVerify: true, - } - if opts.TLSVerify { - tlsopts.CaCertFile = opts.TLSCACert - tlsopts.InsecureSkipVerify = false - } - if opts.TLSHostname != "" { - tlsopts.ServerName = opts.TLSHostname - } - tlscfg, err := tlsutil.ClientConfig(tlsopts) - if err != nil { - return nil, "", err - } - options = append(options, k8shelm.WithTLS(tlscfg)) - } - - return k8shelm.NewClient(options...), host, nil -} - -func ClientSetup(logger log.Logger, kubeClient *kubernetes.Clientset, tillerOpts TillerOptions) *k8shelm.Client { - var helmClient *k8shelm.Client - var host string - var err error - for { - helmClient, host, err = newClient(kubeClient, tillerOpts) - if err != nil { - logger.Log("error", fmt.Sprintf("error creating helm client: %s", err.Error())) - time.Sleep(20 * time.Second) - continue - } - version, err := GetTillerVersion(helmClient, host) - if err != nil { - logger.Log("warning", "unable to connect to Tiller", "err", err, "host", host, "options", fmt.Sprintf("%+v", tillerOpts)) - time.Sleep(20 * time.Second) - continue - } - logger.Log("info", "connected to Tiller", "version", version, "host", host, "options", fmt.Sprintf("%+v", tillerOpts)) - break - } - return helmClient -} - -// GetTillerVersion retrieves tiller version -func GetTillerVersion(cl *k8shelm.Client, h string) (string, error) { - var v *rls.GetVersionResponse - var err error - voption := k8shelm.VersionOption(k8shelm.Host(h)) - if v, err = cl.GetVersion(voption); err != nil { - return "", fmt.Errorf("error getting tiller version: %v", err) - } - - return v.GetVersion().String(), nil -} - -// TODO ... set up based on the tiller existing in the cluster, if no ops given -func tillerHost(kubeClient *kubernetes.Clientset, opts TillerOptions) (string, error) { - if opts.Host == "" || opts.Port == "" { - ts, err := kubeClient.CoreV1().Services(opts.Namespace).Get("tiller-deploy", metav1.GetOptions{}) - if err != nil { - return "", err - } - return fmt.Sprintf("%s.%s:%v", ts.Name, ts.Namespace, ts.Spec.Ports[0].Port), nil - } - - return fmt.Sprintf("%s:%s", opts.Host, opts.Port), nil -} diff --git a/integrations/helm/http/daemon/server.go b/integrations/helm/http/daemon/server.go deleted file mode 100644 index a0fb574c2..000000000 --- a/integrations/helm/http/daemon/server.go +++ /dev/null @@ -1,92 +0,0 @@ -package daemon - -import ( - "context" - "fmt" - "net/http" - _ "net/http/pprof" - "sync/atomic" - "time" - - "github.com/go-kit/kit/log" - "github.com/gorilla/mux" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/weaveworks/flux/integrations/helm/api" - transport "github.com/weaveworks/flux/integrations/helm/http" -) - -// ListenAndServe starts a HTTP server instrumented with Prometheus metrics, -// health and API endpoints on the specified address. -func ListenAndServe(listenAddr string, apiServer api.Server, logger log.Logger, stopCh <-chan struct{}) { - mux := http.DefaultServeMux - - // setup metrics and health endpoints - mux.Handle("/metrics", promhttp.Handler()) - mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte("OK")) - }) - - // setup api endpoints - handler := NewHandler(apiServer, transport.NewRouter()) - mux.Handle("/api/", http.StripPrefix("/api", handler)) - - srv := &http.Server{ - Addr: listenAddr, - Handler: mux, - ReadTimeout: 5 * time.Second, - WriteTimeout: 1 * time.Minute, - IdleTimeout: 15 * time.Second, - } - - logger.Log("info", fmt.Sprintf("starting HTTP server on %s", listenAddr)) - - // run server in background - go func() { - if err := srv.ListenAndServe(); err != http.ErrServerClosed { - logger.Log("error", fmt.Sprintf("HTTP server crashed %v", err)) - } - }() - - // wait for close signal and attempt graceful shutdown - <-stopCh - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - if err := srv.Shutdown(ctx); err != nil { - logger.Log("warn", fmt.Sprintf("HTTP server graceful shutdown failed %v", err)) - } else { - logger.Log("info", "HTTP server stopped") - } -} - -// NewHandler registers handlers on the given router. -func NewHandler(s api.Server, r *mux.Router) http.Handler { - handle := &APIServer{server: s} - r.Get(transport.SyncGit).HandlerFunc(handle.SyncGit) - return r -} - -type APIServer struct { - server api.Server - syncingGit uint32 -} - -// SyncGit starts a goroutine in the background to sync all git mirrors -// _if there is not one running at time of request_. It writes back a -// HTTP 200 status header and 'OK' body to inform the request was -// successful. -// TODO(hidde): in the future we may want to give users the option to -// request the status after it has been started. The Flux (daemon) API -// achieves this by working with jobs whos IDs can be tracked. -func (s *APIServer) SyncGit(w http.ResponseWriter, r *http.Request) { - if atomic.CompareAndSwapUint32(&s.syncingGit, 0, 1) { - go func() { - s.server.SyncMirrors() - atomic.StoreUint32(&s.syncingGit, 0) - }() - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("OK")) -} diff --git a/integrations/helm/http/routes.go b/integrations/helm/http/routes.go deleted file mode 100644 index 6b270494a..000000000 --- a/integrations/helm/http/routes.go +++ /dev/null @@ -1,5 +0,0 @@ -package http - -const ( - SyncGit = "SyncGit" -) diff --git a/integrations/helm/http/transport.go b/integrations/helm/http/transport.go deleted file mode 100644 index 19d8348b7..000000000 --- a/integrations/helm/http/transport.go +++ /dev/null @@ -1,13 +0,0 @@ -package http - -import ( - "github.com/gorilla/mux" -) - -// NewRouter creates a new router instance, registers all API routes -// and returns it. -func NewRouter() *mux.Router { - r := mux.NewRouter() - r.NewRoute().Name(SyncGit).Methods("POST").Path("/v1/sync-git") - return r -} diff --git a/integrations/helm/operator/metrics.go b/integrations/helm/operator/metrics.go deleted file mode 100644 index b598f9587..000000000 --- a/integrations/helm/operator/metrics.go +++ /dev/null @@ -1,15 +0,0 @@ -package operator - -import ( - "github.com/go-kit/kit/metrics/prometheus" - stdprometheus "github.com/prometheus/client_golang/prometheus" -) - -var ( - releaseQueueLength = prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ - Namespace: "flux", - Subsystem: "helm_operator", - Name: "release_queue_length_count", - Help: "Count of releases waiting in the queue to be processed.", - }, []string{}) -) diff --git a/integrations/helm/operator/operator.go b/integrations/helm/operator/operator.go deleted file mode 100644 index c08939fdf..000000000 --- a/integrations/helm/operator/operator.go +++ /dev/null @@ -1,317 +0,0 @@ -package operator - -import ( - "fmt" - "sync" - "time" - - "github.com/go-kit/kit/log" - "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" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" - - flux_v1beta1 "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" - ifscheme "github.com/weaveworks/flux/integrations/client/clientset/versioned/scheme" - fhrv1 "github.com/weaveworks/flux/integrations/client/informers/externalversions/flux.weave.works/v1beta1" - iflister "github.com/weaveworks/flux/integrations/client/listers/flux.weave.works/v1beta1" - "github.com/weaveworks/flux/integrations/helm/chartsync" - "github.com/weaveworks/flux/integrations/helm/status" -) - -const ( - controllerAgentName = "helm-operator" - CacheSyncTimeout = 180 * time.Second -) - -const ( - // ChartSynced is used as part of the Event 'reason' when the Chart related to the - // a HelmRelease gets released/updated - ChartSynced = "ChartSynced" - // ErrChartSync is used as part of the Event 'reason' when the related Chart related to the - // a HelmRelease fails to be released/updated - ErrChartSync = "ErrChartSync" - - // MessageChartSynced - the message used for an Event fired when a HelmRelease - // is synced. - MessageChartSynced = "Chart managed by HelmRelease processed" -) - -// Controller is the operator implementation for HelmRelease resources -type Controller struct { - logger log.Logger - logDiffs bool - - fhrLister iflister.HelmReleaseLister - fhrSynced cache.InformerSynced - - sync *chartsync.ChartChangeSync - - // workqueue is a rate limited work queue. This is used to queue work to be - // processed instead of performing it as soon as a change happens. This - // means we can ensure we only process a fixed amount of resources at a - // time, and makes it easy to ensure we are never processing the same item - // simultaneously in two different workers. - releaseWorkqueue workqueue.RateLimitingInterface - - // recorder is an event recorder for recording Event resources to the - // Kubernetes API. - recorder record.EventRecorder -} - -// New returns a new helm-operator -func New( - logger log.Logger, - logReleaseDiffs bool, - kubeclientset kubernetes.Interface, - fhrInformer fhrv1.HelmReleaseInformer, - releaseWorkqueue workqueue.RateLimitingInterface, - sync *chartsync.ChartChangeSync) *Controller { - - // Add helm-operator types to the default Kubernetes Scheme so Events can be - // logged for helm-operator types. - ifscheme.AddToScheme(scheme.Scheme) - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - controller := &Controller{ - logger: logger, - logDiffs: logReleaseDiffs, - fhrLister: fhrInformer.Lister(), - fhrSynced: fhrInformer.Informer().HasSynced, - releaseWorkqueue: releaseWorkqueue, - recorder: recorder, - sync: sync, - } - - controller.logger.Log("info", "setting up event handlers") - - // ----- EVENT HANDLERS for HelmRelease resources change --------- - fhrInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: func(new interface{}) { - fhr, ok := checkCustomResourceType(controller.logger, new) - if ok && !status.HasRolledBack(fhr) { - controller.enqueueJob(new) - } - }, - UpdateFunc: func(old, new interface{}) { - controller.enqueueUpdateJob(old, new) - }, - DeleteFunc: func(old interface{}) { - fhr, ok := checkCustomResourceType(controller.logger, old) - if ok { - controller.deleteRelease(fhr) - } - }, - }) - controller.logger.Log("info", "event handlers set up") - - return controller -} - -// Run starts workers handling the enqueued events. It will block until -// stopCh is closed, at which point it will shutdown the workqueue and -// wait for workers to finish processing their current work items. -func (c *Controller) Run(threadiness int, stopCh <-chan struct{}, wg *sync.WaitGroup) { - defer runtime.HandleCrash() - defer c.releaseWorkqueue.ShutDown() - - c.logger.Log("info", "starting operator") - - c.logger.Log("info", "starting workers") - for i := 0; i < threadiness; i++ { - wg.Add(1) - go wait.Until(c.runWorker, time.Second, stopCh) - } - - <-stopCh - for i := 0; i < threadiness; i++ { - wg.Done() - } - c.logger.Log("info", "stopping workers") -} - -// runWorker is a long-running function calling the -// processNextWorkItem function to read and process a message -// on a workqueue. -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -// processNextWorkItem will read a single work item off the workqueue and -// attempt to process it, by calling the syncHandler. -func (c *Controller) processNextWorkItem() bool { - releaseQueueLength.Set(float64(c.releaseWorkqueue.Len())) - - obj, shutdown := c.releaseWorkqueue.Get() - if shutdown { - return false - } - - // wrapping block in a func to defer c.workqueue.Done - err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We must call Forget if we do not want - // this work item being re-queued. If a transient error - // occurs, we do not call Forget. Instead the item is put back - // on the workqueue and attempted again after a back-off - // period. - defer c.releaseWorkqueue.Done(obj) - - var key string - var ok bool - // We expect strings to come off the workqueue. These are of the - // form "namespace/fhr(custom resource) name". We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date than when the item was initially put onto the - // workqueue. - if key, ok = obj.(string); !ok { - // As the item in the workqueue is actually invalid, we call - // Forget not to get into a loop of attempting to - // process a work item that is invalid. - c.releaseWorkqueue.Forget(obj) - runtime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - - return nil - } - // Run the syncHandler, passing it the namespace/name string of the - // HelmRelease resource to sync the corresponding Chart release. - // If the sync failed, then we return while the item will get requeued - if err := c.syncHandler(key); err != nil { - return fmt.Errorf("errored syncing HelmRelease '%s': %s", key, err.Error()) - } - // If no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.releaseWorkqueue.Forget(obj) - return nil - }(obj) - - if err != nil { - runtime.HandleError(err) - return true - } - return true -} - -// syncHandler acts according to the action -// Deletes/creates or updates a Chart release -func (c *Controller) syncHandler(key string) error { - // Retrieve namespace and Custom Resource name from the key - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - c.logger.Log("error", fmt.Sprintf("key '%s' is invalid: %v", key, err)) - runtime.HandleError(fmt.Errorf("key '%s' is invalid", key)) - return nil - } - - // Custom Resource fhr contains all information we need to know about the Chart release - fhr, err := c.fhrLister.HelmReleases(namespace).Get(name) - if err != nil { - if k8serrors.IsNotFound(err) { - c.logger.Log("info", fmt.Sprintf("HelmRelease '%s' referred to in work queue no longer exists", key)) - runtime.HandleError(fmt.Errorf("HelmRelease '%s' referred to in work queue no longer exists", key)) - return nil - } - c.logger.Log("error", err.Error()) - return err - } - - c.sync.ReconcileReleaseDef(*fhr) - c.recorder.Event(fhr, corev1.EventTypeNormal, ChartSynced, MessageChartSynced) - return nil -} - -func checkCustomResourceType(logger log.Logger, obj interface{}) (flux_v1beta1.HelmRelease, bool) { - var fhr *flux_v1beta1.HelmRelease - var ok bool - if fhr, ok = obj.(*flux_v1beta1.HelmRelease); !ok { - logger.Log("error", fmt.Sprintf("HelmRelease Event Watch received an invalid object: %#v", obj)) - return flux_v1beta1.HelmRelease{}, false - } - return *fhr, true -} - -func getCacheKey(obj interface{}) (string, error) { - var key string - var err error - - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - runtime.HandleError(err) - return "", err - } - return key, nil -} - -// enqueueJob takes a HelmRelease resource and converts it into a namespace/name -// string which is then put onto the work queue. This method should not be -// passed resources of any type other than HelmRelease. -func (c *Controller) enqueueJob(obj interface{}) { - var key string - var err error - if key, err = getCacheKey(obj); err != nil { - return - } - c.releaseWorkqueue.AddRateLimited(key) - releaseQueueLength.Set(float64(c.releaseWorkqueue.Len())) -} - -// enqueueUpdateJob decides if there is a genuine resource update -func (c *Controller) enqueueUpdateJob(old, new interface{}) { - oldFhr, ok := checkCustomResourceType(c.logger, old) - if !ok { - return - } - newFhr, ok := checkCustomResourceType(c.logger, new) - if !ok { - return - } - - diff := cmp.Diff(oldFhr.Spec, newFhr.Spec) - - // Filter out any update notifications that are due to status - // updates, as the dry-run that determines if we should upgrade - // is expensive, but _without_ filtering out updates that are - // from the periodic refresh, as we still want to detect (and - // undo) mutations to Helm charts. - if sDiff := cmp.Diff(oldFhr.Status, newFhr.Status); diff == "" && sDiff != "" { - return - } - - // Skip if the current HelmRelease generation has been rolled - // back, as otherwise we will end up in a loop of failure, but - // continue if the checksum of the values differs, as the failure - // may have been the result of the values contents. - if newFhr.Spec.Rollback.Enable && status.HasRolledBack(newFhr) && c.sync.CompareValuesChecksum(newFhr) { - c.logger.Log("warning", "release has been rolled back, skipping", "resource", newFhr.ResourceID().String()) - return - } - - log := []string{"info", "enqueuing release"} - if diff != "" && c.logDiffs { - log = append(log, "diff", diff) - } - log = append(log, "resource", newFhr.ResourceID().String()) - - l := make([]interface{}, len(log)) - for i, v := range log { - l[i] = v - } - - c.logger.Log(l...) - - c.enqueueJob(new) -} - -func (c *Controller) deleteRelease(fhr flux_v1beta1.HelmRelease) { - c.logger.Log("info", "deleting release", "resource", fhr.ResourceID().String()) - c.sync.DeleteRelease(fhr) -} diff --git a/integrations/helm/release/metrics.go b/integrations/helm/release/metrics.go deleted file mode 100644 index ef4f46042..000000000 --- a/integrations/helm/release/metrics.go +++ /dev/null @@ -1,38 +0,0 @@ -package release - -import ( - "fmt" - "time" - - "github.com/go-kit/kit/metrics/prometheus" - stdprometheus "github.com/prometheus/client_golang/prometheus" -) - -const ( - LabelAction = "action" - LabelDryRun = "dry_run" - LabelSuccess = "success" - LabelNamespace = "namespace" - LabelReleaseName = "release_name" -) - -var ( - durationBuckets = []float64{1, 5, 10, 30, 60, 120, 180, 300} - releaseDuration = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ - Namespace: "flux", - Subsystem: "helm_operator", - Name: "release_duration_seconds", - Help: "Release duration in seconds.", - Buckets: durationBuckets, - }, []string{LabelAction, LabelDryRun, LabelSuccess, LabelNamespace, LabelReleaseName}) -) - -func ObserveRelease(start time.Time, action Action, dryRun, success bool, namespace, releaseName string) { - releaseDuration.With( - LabelAction, string(action), - LabelDryRun, fmt.Sprint(dryRun), - LabelSuccess, fmt.Sprint(success), - LabelNamespace, namespace, - LabelReleaseName, releaseName, - ).Observe(time.Since(start).Seconds()) -} diff --git a/integrations/helm/release/release.go b/integrations/helm/release/release.go deleted file mode 100644 index de971d93c..000000000 --- a/integrations/helm/release/release.go +++ /dev/null @@ -1,599 +0,0 @@ -package release - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "io/ioutil" - "net/url" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/ghodss/yaml" - "github.com/go-kit/kit/log" - "github.com/spf13/pflag" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/client-go/kubernetes" - k8sclientv1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/getter" - k8shelm "k8s.io/helm/pkg/helm" - helmenv "k8s.io/helm/pkg/helm/environment" - hapi_release "k8s.io/helm/pkg/proto/hapi/release" - helmutil "k8s.io/helm/pkg/releaseutil" - - fluxk8s "github.com/weaveworks/flux/cluster/kubernetes" - flux_v1beta1 "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" -) - -type Action string - -const ( - InstallAction Action = "CREATE" - UpgradeAction Action = "UPDATE" -) - -// Release contains clients needed to provide functionality related to helm releases -type Release struct { - logger log.Logger - HelmClient *k8shelm.Client -} - -type Releaser interface { - GetUpgradableRelease(name string) (*hapi_release.Release, error) - Install(dir string, releaseName string, fhr flux_v1beta1.HelmRelease, action Action, opts InstallOptions) (*hapi_release.Release, error) -} - -type DeployInfo struct { - Name string -} - -type InstallOptions struct { - DryRun bool - ReuseName bool -} - -// New creates a new Release instance. -func New(logger log.Logger, helmClient *k8shelm.Client) *Release { - r := &Release{ - logger: logger, - HelmClient: helmClient, - } - return r -} - -// GetUpgradableRelease returns a release if the current state of it -// allows an upgrade, a descriptive error if it is not allowed, or -// nil if the release does not exist. -func (r *Release) GetUpgradableRelease(name string) (*hapi_release.Release, error) { - rls, err := r.HelmClient.ReleaseContent(name) - if err != nil { - if strings.Contains(err.Error(), "not found") { - return nil, nil - } - return nil, err - } - - release := rls.GetRelease() - status := release.GetInfo().GetStatus() - - switch status.GetCode() { - case hapi_release.Status_DEPLOYED: - return release, nil - case hapi_release.Status_FAILED: - return nil, fmt.Errorf("release requires a rollback before it can be upgraded (%s)", status.GetCode().String()) - case hapi_release.Status_PENDING_INSTALL, - hapi_release.Status_PENDING_UPGRADE, - hapi_release.Status_PENDING_ROLLBACK: - return nil, fmt.Errorf("operation pending for release (%s)", status.GetCode().String()) - default: - return nil, fmt.Errorf("current state prevents it from being upgraded (%s)", status.GetCode().String()) - } -} - -// shouldRollback determines if a release should be rolled back -// based on the status of the Helm release. -func (r *Release) shouldRollback(name string) (bool, error) { - rls, err := r.HelmClient.ReleaseStatus(name) - if err != nil { - return false, err - } - - status := rls.GetInfo().GetStatus() - switch status.Code { - case hapi_release.Status_FAILED: - r.logger.Log("info", "rolling back release", "release", name) - return true, nil - case hapi_release.Status_PENDING_ROLLBACK: - r.logger.Log("info", "release already has a rollback pending", "release", name) - return false, nil - default: - return false, fmt.Errorf("release with status %s cannot be rolled back", status.Code.String()) - } -} - -func (r *Release) canDelete(name string) (bool, error) { - rls, err := r.HelmClient.ReleaseStatus(name) - - if err != nil { - return false, err - } - /* - "UNKNOWN": 0, - "DEPLOYED": 1, - "DELETED": 2, - "SUPERSEDED": 3, - "FAILED": 4, - "DELETING": 5, - "PENDING_INSTALL": 6, - "PENDING_UPGRADE": 7, - "PENDING_ROLLBACK": 8, - */ - status := rls.GetInfo().GetStatus() - switch status.Code { - case 1, 4: - r.logger.Log("info", fmt.Sprintf("Deleting release %s", name)) - return true, nil - case 2: - r.logger.Log("info", fmt.Sprintf("Release %s already deleted", name)) - return false, nil - default: - return false, fmt.Errorf("release %s with status %s cannot be deleted", name, status.Code.String()) - } -} - -// Install performs a Chart release given the directory containing the -// charts, and the HelmRelease specifying the release. Depending -// on the release type, this is either a new release, or an upgrade of -// an existing one. -// -// TODO(michael): cloneDir is only relevant if installing from git; -// either split this procedure into two varieties, or make it more -// general and calculate the path to the chart in the caller. -func (r *Release) Install(chartPath, releaseName string, fhr flux_v1beta1.HelmRelease, action Action, opts InstallOptions, kubeClient *kubernetes.Clientset) (release *hapi_release.Release, checksum string, err error) { - defer func(start time.Time) { - ObserveRelease( - start, - action, - opts.DryRun, - err == nil, - fhr.Namespace, - fhr.ReleaseName(), - ) - }(time.Now()) - - if chartPath == "" { - return nil, "", fmt.Errorf("empty path to chart supplied for resource %q", fhr.ResourceID().String()) - } - _, err = os.Stat(chartPath) - switch { - case os.IsNotExist(err): - return nil, "", fmt.Errorf("no file or dir at path to chart: %s", chartPath) - case err != nil: - return nil, "", fmt.Errorf("error statting path given for chart %s: %s", chartPath, err.Error()) - } - - r.logger.Log("info", fmt.Sprintf("processing release %s (as %s)", fhr.ReleaseName(), releaseName), - "action", fmt.Sprintf("%v", action), - "options", fmt.Sprintf("%+v", opts), - "timeout", fmt.Sprintf("%vs", fhr.GetTimeout())) - - vals, err := Values(kubeClient.CoreV1(), fhr.Namespace, chartPath, fhr.GetValuesFromSources(), fhr.Spec.Values) - if err != nil { - r.logger.Log("error", fmt.Sprintf("Failed to compose values for Chart release [%s]: %v", fhr.Spec.ReleaseName, err)) - return nil, "", err - } - - strVals, err := vals.YAML() - if err != nil { - r.logger.Log("error", fmt.Sprintf("Problem with supplied customizations for Chart release [%s]: %v", fhr.Spec.ReleaseName, err)) - return nil, "", err - } - rawVals := []byte(strVals) - checksum = ValuesChecksum(rawVals) - - switch action { - case InstallAction: - res, err := r.HelmClient.InstallRelease( - chartPath, - fhr.GetTargetNamespace(), - k8shelm.ValueOverrides(rawVals), - k8shelm.ReleaseName(releaseName), - k8shelm.InstallDryRun(opts.DryRun), - k8shelm.InstallReuseName(opts.ReuseName), - k8shelm.InstallTimeout(fhr.GetTimeout()), - ) - - if err != nil { - r.logger.Log("error", fmt.Sprintf("Chart release failed: %s: %#v", fhr.Spec.ReleaseName, err)) - // purge the release if the install failed but only if this is the first revision - history, err := r.HelmClient.ReleaseHistory(releaseName, k8shelm.WithMaxHistory(2)) - if err == nil && len(history.Releases) == 1 && history.Releases[0].Info.Status.Code == hapi_release.Status_FAILED { - r.logger.Log("info", fmt.Sprintf("Deleting failed release: [%s]", fhr.Spec.ReleaseName)) - _, err = r.HelmClient.DeleteRelease(releaseName, k8shelm.DeletePurge(true)) - if err != nil { - r.logger.Log("error", fmt.Sprintf("Release deletion error: %#v", err)) - return nil, "", err - } - } - return nil, checksum, err - } - if !opts.DryRun { - r.annotateResources(res.Release, fhr) - } - return res.Release, checksum, err - case UpgradeAction: - res, err := r.HelmClient.UpdateRelease( - releaseName, - chartPath, - k8shelm.UpdateValueOverrides(rawVals), - k8shelm.UpgradeDryRun(opts.DryRun), - k8shelm.UpgradeTimeout(fhr.GetTimeout()), - k8shelm.ResetValues(fhr.Spec.ResetValues), - k8shelm.UpgradeForce(fhr.Spec.ForceUpgrade), - k8shelm.UpgradeWait(fhr.Spec.Rollback.Enable), - ) - - if err != nil { - r.logger.Log("error", fmt.Sprintf("Chart upgrade release failed: %s: %#v", fhr.Spec.ReleaseName, err)) - return nil, checksum, err - } - if !opts.DryRun { - r.annotateResources(res.Release, fhr) - } - return res.Release, checksum, err - default: - err = fmt.Errorf("Valid install options: CREATE, UPDATE. Provided: %s", action) - r.logger.Log("error", err.Error()) - return nil, "", err - } -} - -// Rollback rolls back a Chart release if required -func (r *Release) Rollback(releaseName string, fhr flux_v1beta1.HelmRelease) (*hapi_release.Release, error) { - ok, err := r.shouldRollback(releaseName) - if !ok { - if err != nil { - return nil, err - } - return nil, nil - } - - res, err := r.HelmClient.RollbackRelease( - releaseName, - k8shelm.RollbackVersion(0), // '0' makes Helm fetch the latest deployed release - k8shelm.RollbackTimeout(fhr.Spec.Rollback.GetTimeout()), - k8shelm.RollbackForce(fhr.Spec.Rollback.Force), - k8shelm.RollbackRecreate(fhr.Spec.Rollback.Recreate), - k8shelm.RollbackDisableHooks(fhr.Spec.Rollback.DisableHooks), - k8shelm.RollbackWait(fhr.Spec.Rollback.Wait), - k8shelm.RollbackDescription("Automated rollback by Helm operator"), - ) - if err != nil { - r.logger.Log("error", fmt.Sprintf("failed to rollback release: %#v", err)) - return nil, err - } - - r.annotateResources(res.Release, fhr) - r.logger.Log("info", "rolled back release", "release", releaseName) - - return res.Release, err -} - -// Delete purges a Chart release -func (r *Release) Delete(name string) error { - ok, err := r.canDelete(name) - if !ok { - if err != nil { - return err - } - return nil - } - - _, err = r.HelmClient.DeleteRelease(name, k8shelm.DeletePurge(true)) - if err != nil { - r.logger.Log("error", fmt.Sprintf("Release deletion error: %#v", err)) - return err - } - r.logger.Log("info", fmt.Sprintf("Release deleted: [%s]", name)) - return nil -} - -// OwnedByHelmRelease validates the release is managed by the given -// HelmRelease, by looking for the resource ID in the antecedent -// annotation. This validation is necessary because we can not -// validate the uniqueness of a release name on the creation of a -// HelmRelease, which would result in the operator attempting to -// upgrade a release indefinitely when multiple HelmReleases with the -// same release name exist. -// -// To be able to migrate existing releases to a HelmRelease, empty -// (missing) annotations are handled as true / owned by. -func (r *Release) OwnedByHelmRelease(release *hapi_release.Release, fhr flux_v1beta1.HelmRelease) bool { - objs := releaseManifestToUnstructured(release.Manifest, log.NewNopLogger()) - - escapedAnnotation := strings.ReplaceAll(fluxk8s.AntecedentAnnotation, ".", `\.`) - args := []string{"-o", "jsonpath={.metadata.annotations." + escapedAnnotation + "}", "get"} - - for ns, res := range namespacedResourceMap(objs, release.Namespace) { - for _, r := range res { - a := append(args, "--namespace", ns, r) - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "kubectl", a...) - out, err := cmd.Output() - if err != nil { - continue - } - - v := strings.TrimSpace(string(out)) - if v == "" { - return true - } - return v == fhr.ResourceID().String() - } - } - - return false -} - -// annotateResources annotates each of the resources created (or updated) -// by the release so that we can spot them. -func (r *Release) annotateResources(release *hapi_release.Release, fhr flux_v1beta1.HelmRelease) { - objs := releaseManifestToUnstructured(release.Manifest, r.logger) - for namespace, res := range namespacedResourceMap(objs, release.Namespace) { - args := []string{"annotate", "--overwrite"} - args = append(args, "--namespace", namespace) - args = append(args, res...) - args = append(args, fluxk8s.AntecedentAnnotation+"="+fhr.ResourceID().String()) - - // The timeout is set to a high value as it may take some time - // to annotate large umbrella charts. - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "kubectl", args...) - output, err := cmd.CombinedOutput() - if err != nil { - r.logger.Log("output", string(output), "err", err) - } - } -} - -// Values tries to resolve all given value file sources and merges -// them into one Values struct. It returns the merged Values. -func Values(corev1 k8sclientv1.CoreV1Interface, ns string, chartPath string, valuesFromSource []flux_v1beta1.ValuesFromSource, values chartutil.Values) (chartutil.Values, error) { - result := chartutil.Values{} - - for _, v := range valuesFromSource { - var valueFile chartutil.Values - - switch { - case v.ConfigMapKeyRef != nil: - cm := v.ConfigMapKeyRef - name := cm.Name - key := cm.Key - if key == "" { - key = "values.yaml" - } - optional := cm.Optional != nil && *cm.Optional - configMap, err := corev1.ConfigMaps(ns).Get(name, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) && optional { - continue - } - return result, err - } - d, ok := configMap.Data[key] - if !ok { - if optional { - continue - } - return result, fmt.Errorf("could not find key %v in ConfigMap %s/%s", key, ns, name) - } - if err := yaml.Unmarshal([]byte(d), &valueFile); err != nil { - if optional { - continue - } - return result, fmt.Errorf("unable to yaml.Unmarshal %v from %s in ConfigMap %s/%s", d, key, ns, name) - } - case v.SecretKeyRef != nil: - s := v.SecretKeyRef - name := s.Name - key := s.Key - if key == "" { - key = "values.yaml" - } - optional := s.Optional != nil && *s.Optional - secret, err := corev1.Secrets(ns).Get(name, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) && optional { - continue - } - return result, err - } - d, ok := secret.Data[key] - if !ok { - if optional { - continue - } - return result, fmt.Errorf("could not find key %s in Secret %s/%s", key, ns, name) - } - if err := yaml.Unmarshal(d, &valueFile); err != nil { - return result, fmt.Errorf("unable to yaml.Unmarshal %v from %s in Secret %s/%s", d, key, ns, name) - } - case v.ExternalSourceRef != nil: - es := v.ExternalSourceRef - url := es.URL - optional := es.Optional != nil && *es.Optional - b, err := readURL(url) - if err != nil { - if optional { - continue - } - return result, fmt.Errorf("unable to read value file from URL %s", url) - } - if err := yaml.Unmarshal(b, &valueFile); err != nil { - if optional { - continue - } - return result, fmt.Errorf("unable to yaml.Unmarshal %v from URL %s", b, url) - } - case v.ChartFileRef != nil: - cf := v.ChartFileRef - filePath := cf.Path - optional := cf.Optional != nil && *cf.Optional - f, err := readLocalChartFile(filepath.Join(chartPath, filePath)) - if err != nil { - if optional { - continue - } - return result, fmt.Errorf("unable to read value file from path %s", filePath) - } - if err := yaml.Unmarshal(f, &valueFile); err != nil { - if optional { - continue - } - return result, fmt.Errorf("unable to yaml.Unmarshal %v from URL %s", f, filePath) - } - } - - result = mergeValues(result, valueFile) - } - - result = mergeValues(result, values) - - return result, nil -} - -// ValuesChecksum calculates the SHA256 checksum of the given raw -// values. -func ValuesChecksum(rawValues []byte) string { - hasher := sha256.New() - hasher.Write(rawValues) - return hex.EncodeToString(hasher.Sum(nil)) -} - -// Merges source and destination map, preferring values from the source Values -// This is slightly adapted from https://github.com/helm/helm/blob/2332b480c9cb70a0d8a85247992d6155fbe82416/cmd/helm/install.go#L359 -func mergeValues(dest, src map[string]interface{}) map[string]interface{} { - for k, v := range src { - // If the key doesn't exist already, then just set the key to that value - if _, exists := dest[k]; !exists { - dest[k] = v - continue - } - nextMap, ok := v.(map[string]interface{}) - // If it isn't another map, overwrite the value - if !ok { - dest[k] = v - continue - } - // Edge case: If the key exists in the destination, but isn't a map - destMap, isMap := dest[k].(map[string]interface{}) - // If the source map has a map for this key, prefer it - if !isMap { - dest[k] = v - continue - } - // If we got to this point, it is a map in both, so merge them - dest[k] = mergeValues(destMap, nextMap) - } - return dest -} - -// readURL attempts to read a file from an url. -// This is slightly adapted from https://github.com/helm/helm/blob/2332b480c9cb70a0d8a85247992d6155fbe82416/cmd/helm/install.go#L552 -func readURL(URL string) ([]byte, error) { - var settings helmenv.EnvSettings - flags := pflag.NewFlagSet("helm-env", pflag.ContinueOnError) - settings.AddFlags(flags) - settings.Init(flags) - - u, _ := url.Parse(URL) - p := getter.All(settings) - - getterConstructor, err := p.ByScheme(u.Scheme) - - if err != nil { - return []byte{}, err - } - - getter, err := getterConstructor(URL, "", "", "") - if err != nil { - return []byte{}, err - } - data, err := getter.Get(URL) - return data.Bytes(), err -} - -// readLocalChartFile attempts to read a file from the chart path. -func readLocalChartFile(filePath string) ([]byte, error) { - f, err := ioutil.ReadFile(filePath) - if err != nil { - return []byte{}, err - } - - return f, nil -} - -// releaseManifestToUnstructured turns a string containing YAML -// manifests into an array of Unstructured objects. -func releaseManifestToUnstructured(manifest string, logger log.Logger) []unstructured.Unstructured { - manifests := helmutil.SplitManifests(manifest) - var objs []unstructured.Unstructured - for _, manifest := range manifests { - bytes, err := yaml.YAMLToJSON([]byte(manifest)) - if err != nil { - logger.Log("err", err) - continue - } - - var u unstructured.Unstructured - if err := u.UnmarshalJSON(bytes); err != nil { - logger.Log("err", err) - continue - } - - // Helm charts may include list kinds, we are only interested in - // the items on those lists. - if u.IsList() { - l, err := u.ToList() - if err != nil { - logger.Log("err", err) - continue - } - objs = append(objs, l.Items...) - continue - } - - objs = append(objs, u) - } - return objs -} - -// namespacedResourceMap iterates over the given objects and maps the -// resource identifier against the namespace from the object, if no -// namespace is present (either because the object kind has no namespace -// or it belongs to the release namespace) it gets mapped against the -// given release namespace. -func namespacedResourceMap(objs []unstructured.Unstructured, releaseNamespace string) map[string][]string { - resources := make(map[string][]string) - for _, obj := range objs { - namespace := obj.GetNamespace() - if namespace == "" { - namespace = releaseNamespace - } - resource := obj.GetKind() + "/" + obj.GetName() - resources[namespace] = append(resources[namespace], resource) - } - return resources -} diff --git a/integrations/helm/release/release_test.go b/integrations/helm/release/release_test.go deleted file mode 100644 index 3c95c7bbf..000000000 --- a/integrations/helm/release/release_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package release - -import ( - "testing" - - "github.com/stretchr/testify/assert" - flux_v1beta1 "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/helm/pkg/chartutil" -) - -func TestValues(t *testing.T) { - falseVal := false - - chartValues, _ := chartutil.ReadValues([]byte(`image: - tag: 1.1.1 -valuesDict: - chart: true`)) - - client := fake.NewSimpleClientset( - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "release-configmap", - Namespace: "flux", - }, - Data: map[string]string{ - "values.yaml": `valuesDict: - configmap: true`, - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "release-secret", - Namespace: "flux", - }, - Data: map[string][]byte{ - "values.yaml": []byte(`valuesDict: - secret: true`), - }, - }, - ) - - valuesFromSource := []flux_v1beta1.ValuesFromSource{ - flux_v1beta1.ValuesFromSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "release-configmap", - }, - Key: "values.yaml", - Optional: &falseVal, - }, - SecretKeyRef: nil, - ExternalSourceRef: nil, - ChartFileRef: nil, - }, - flux_v1beta1.ValuesFromSource{ - ConfigMapKeyRef: nil, - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "release-secret", - }, - Key: "values.yaml", - Optional: &falseVal, - }, - ExternalSourceRef: nil, - ChartFileRef: nil, - }} - - values, err := Values(client.CoreV1(), "flux", "", valuesFromSource, chartValues) - assert.NoError(t, err) - assert.Equal(t, "1.1.1", values["image"].(map[string]interface{})["tag"]) - assert.NotNil(t, values["valuesDict"].(map[string]interface{})["chart"]) - assert.NotNil(t, values["valuesDict"].(map[string]interface{})["configmap"]) - assert.NotNil(t, values["valuesDict"].(map[string]interface{})["secret"]) -} diff --git a/integrations/helm/status/conditions.go b/integrations/helm/status/conditions.go deleted file mode 100644 index 927e9de43..000000000 --- a/integrations/helm/status/conditions.go +++ /dev/null @@ -1,66 +0,0 @@ -package status - -import ( - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" - v1beta1client "github.com/weaveworks/flux/integrations/client/clientset/versioned/typed/flux.weave.works/v1beta1" -) - - -// NewCondition creates a new HelmReleaseCondition. -func NewCondition(conditionType v1beta1.HelmReleaseConditionType, status v1.ConditionStatus, reason, message string) v1beta1.HelmReleaseCondition { - return v1beta1.HelmReleaseCondition{ - Type: conditionType, - Status: status, - LastUpdateTime: metav1.Now(), - LastTransitionTime: metav1.Now(), - Reason: reason, - Message: message, - } -} - -// SetCondition updates the HelmRelease to include the given condition. -func SetCondition(client v1beta1client.HelmReleaseInterface, hr v1beta1.HelmRelease, - condition v1beta1.HelmReleaseCondition) error { - - cHr, err := client.Get(hr.Name, metav1.GetOptions{}) - if err != nil { - return err - } - - currCondition := GetCondition(cHr.Status, condition.Type) - if currCondition != nil && currCondition.Status == condition.Status { - condition.LastTransitionTime = currCondition.LastTransitionTime - } - - newConditions := filterOutCondition(cHr.Status.Conditions, condition.Type) - cHr.Status.Conditions = append(newConditions, condition) - - _, err = client.UpdateStatus(cHr) - return err -} - -// GetCondition returns the condition with the given type. -func GetCondition(status v1beta1.HelmReleaseStatus, conditionType v1beta1.HelmReleaseConditionType) *v1beta1.HelmReleaseCondition { - for i := range status.Conditions { - c := status.Conditions[i] - if c.Type == conditionType { - return &c - } - } - return nil -} - -// filterOutCondition returns a new slice of conditions without the -// conditions of the given type. -func filterOutCondition(conditions []v1beta1.HelmReleaseCondition, conditionType v1beta1.HelmReleaseConditionType) []v1beta1.HelmReleaseCondition { - var newConditions []v1beta1.HelmReleaseCondition - for _, c := range conditions { - if c.Type == conditionType { - continue - } - newConditions = append(newConditions, c) - } - return newConditions -} diff --git a/integrations/helm/status/status.go b/integrations/helm/status/status.go deleted file mode 100644 index c0881d54e..000000000 --- a/integrations/helm/status/status.go +++ /dev/null @@ -1,198 +0,0 @@ -/* - -This package is for maintaining the link between `HelmRelease` -resources and the Helm releases to which they -correspond. Specifically, - - 1. updating the `HelmRelease` status based on the progress of - syncing, and the state of the associated Helm release; and, - - 2. attributing each resource in a Helm release (under our control) to - the associated `HelmRelease`. - -*/ -package status - -import ( - "time" - - "github.com/go-kit/kit/log" - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kube "k8s.io/client-go/kubernetes" - "k8s.io/helm/pkg/helm" - helmrelease "k8s.io/helm/pkg/proto/hapi/release" - - "github.com/weaveworks/flux/integrations/apis/flux.weave.works/v1beta1" - ifclientset "github.com/weaveworks/flux/integrations/client/clientset/versioned" - iflister "github.com/weaveworks/flux/integrations/client/listers/flux.weave.works/v1beta1" - v1beta1client "github.com/weaveworks/flux/integrations/client/clientset/versioned/typed/flux.weave.works/v1beta1" -) - -const period = 10 * time.Second - -type Updater struct { - hrClient ifclientset.Interface - hrLister iflister.HelmReleaseLister - kube kube.Interface - helmClient *helm.Client - namespace string -} - -func New(hrClient ifclientset.Interface, hrLister iflister.HelmReleaseLister, helmClient *helm.Client) *Updater { - return &Updater{ - hrClient: hrClient, - hrLister: hrLister, - helmClient: helmClient, - } -} - -func (u *Updater) Loop(stop <-chan struct{}, logger log.Logger) { - ticker := time.NewTicker(period) - var logErr error - -bail: - for { - select { - case <-stop: - break bail - case <-ticker.C: - } - list, err := u.hrLister.List(labels.Everything()) - if err != nil { - logErr = err - break bail - } - for _, hr := range list { - nsHrClient := u.hrClient.FluxV1beta1().HelmReleases(hr.Namespace) - releaseName := hr.ReleaseName() - releaseStatus, _ := u.helmClient.ReleaseStatus(releaseName) - // If we are unable to get the status, we do not care why - if releaseStatus == nil { - continue - } - statusStr := releaseStatus.Info.Status.Code.String() - if err := SetReleaseStatus(nsHrClient, *hr, releaseName, statusStr); err != nil { - logger.Log("namespace", hr.Namespace, "resource", hr.Name, "err", err) - continue - } - } - } - - ticker.Stop() - logger.Log("loop", "stopping", "err", logErr) -} - - -// SetReleaseStatus updates the status of the HelmRelease to the given -// release name and/or release status. -func SetReleaseStatus(client v1beta1client.HelmReleaseInterface, hr v1beta1.HelmRelease, releaseName, releaseStatus string) error { - cHr, err := client.Get(hr.Name, metav1.GetOptions{}) - if err != nil { - return err - } - - if cHr.Status.ReleaseName == releaseName && cHr.Status.ReleaseStatus == releaseStatus { - return nil - } - - cHr.Status.ReleaseName = releaseName - cHr.Status.ReleaseStatus = releaseStatus - - _, err = client.UpdateStatus(cHr) - return err -} - - -// SetReleaseRevision updates the status of the HelmRelease to the -// given revision. -func SetReleaseRevision(client v1beta1client.HelmReleaseInterface, hr v1beta1.HelmRelease, revision string) error { - cHr, err := client.Get(hr.Name, metav1.GetOptions{}) - if err != nil { - return err - } - - if cHr.Status.Revision == revision { - return nil - } - - cHr.Status.Revision = revision - - _, err = client.UpdateStatus(cHr) - return err -} - -// SetValuesChecksum updates the values checksum of the HelmRelease to -// the given checksum. -func SetValuesChecksum(client v1beta1client.HelmReleaseInterface, hr v1beta1.HelmRelease, valuesChecksum string) error { - cHr, err := client.Get(hr.Name, metav1.GetOptions{}) - if err != nil { - return err - } - - if valuesChecksum == "" || cHr.Status.ValuesChecksum == valuesChecksum { - return nil - } - - cHr.Status.ValuesChecksum = valuesChecksum - - _, err = client.UpdateStatus(cHr) - return err -} - -// SetObservedGeneration updates the observed generation status of the -// HelmRelease to the given generation. -func SetObservedGeneration(client v1beta1client.HelmReleaseInterface, hr v1beta1.HelmRelease, generation int64) error { - cHr, err := client.Get(hr.Name, metav1.GetOptions{}) - if err != nil { - return err - } - - if cHr.Status.ObservedGeneration >= generation { - return nil - } - - cHr.Status.ObservedGeneration = generation - - _, err = client.UpdateStatus(cHr) - return err -} - -// ReleaseFailed returns if the roll-out of the HelmRelease failed. -func ReleaseFailed(hr v1beta1.HelmRelease) bool { - return hr.Status.ReleaseStatus == helmrelease.Status_FAILED.String() -} - - -// HasSynced returns if the HelmRelease has been processed by the -// controller. -func HasSynced(hr v1beta1.HelmRelease) bool { - return hr.Status.ObservedGeneration >= hr.Generation -} - -// HasRolledBack returns if the current generation of the HelmRelease -// has been rolled back. -func HasRolledBack(hr v1beta1.HelmRelease) bool { - if !HasSynced(hr) { - return false - } - - rolledBack := GetCondition(hr.Status, v1beta1.HelmReleaseRolledBack) - if rolledBack == nil { - return false - } - - chartFetched := GetCondition(hr.Status, v1beta1.HelmReleaseChartFetched) - if chartFetched != nil { - // NB: as two successful state updates can happen right after - // each other, on which we both want to act, we _must_ compare - // the update timestamps as the transition timestamp will only - // change on a status shift. - if chartFetched.Status == v1.ConditionTrue && rolledBack.LastUpdateTime.Before(&chartFetched.LastUpdateTime) { - return false - } - } - - return rolledBack.Status == v1.ConditionTrue -} diff --git a/test/e2e/run.sh b/test/e2e/run.sh index 4ef3f8ebc..256c1fdb1 100755 --- a/test/e2e/run.sh +++ b/test/e2e/run.sh @@ -77,7 +77,6 @@ kubectl -n "${FLUX_NAMESPACE}" rollout status deployment/gitsrv if [ "${USING_KIND}" = 'true' ]; then echo '>>> Loading images into the Kind cluster' kind --name "${KIND_CLUSTER}" load docker-image 'docker.io/fluxcd/flux:latest' - kind --name "${KIND_CLUSTER}" load docker-image 'docker.io/fluxcd/helm-operator:latest' fi echo '>>> Installing Flux with Helm' @@ -108,8 +107,6 @@ helm install --name flux --wait \ --set git.config.secretName=gitconfig \ --set git.config.enabled=true \ --set-string git.config.data="${GITCONFIG}" \ ---set helmOperator.repository=docker.io/fluxcd/helm-operator \ ---set helmOperator.tag=latest \ --set helmOperator.create=true \ --set helmOperator.createCRD=true \ --set helmOperator.git.secretName=ssh-git \ @@ -144,7 +141,7 @@ until ${ok}; do fi done -echo -n ">>> Waiting for namespace ${DEMO_NAMESPACE} " +echo -n ">>> Waiting for namespace ${DEMO_NAMESPACE}" retries=24 count=1 ok=false @@ -178,7 +175,7 @@ until ${ok}; do done echo ' done' -echo -n '>>> Waiting for Helm release mongodb ' +echo -n '>>> Waiting for Helm release mongodb' retries=24 count=0 ok=false @@ -189,7 +186,6 @@ until ${ok}; do count=$(($count + 1)) if [[ ${count} -eq ${retries} ]]; then kubectl -n "${FLUX_NAMESPACE}" logs deployment/flux - kubectl -n "${FLUX_NAMESPACE}" logs deployment/flux-helm-operator echo ' No more retries left' exit 1 fi @@ -199,9 +195,6 @@ echo ' done' echo '>>> Flux logs' kubectl -n "${FLUX_NAMESPACE}" logs deployment/flux -echo '>>> Helm Operator logs' -kubectl -n "${FLUX_NAMESPACE}" logs deployment/flux-helm-operator - echo '>>> List pods' kubectl -n "${DEMO_NAMESPACE}" get pods diff --git a/tools.go b/tools.go index 29a2a5ccd..93556bc5a 100644 --- a/tools.go +++ b/tools.go @@ -7,5 +7,4 @@ package flux import ( _ "github.com/shurcooL/vfsgen" - _ "k8s.io/code-generator" )