From 32a18ffb711171a5a7ad34b90e3eddcaf2a7e13a Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 13:15:31 -0400 Subject: [PATCH 01/45] Fix typo in EMBEDED_CHART_VERSION variable --- .github/workflows/prom-fed-e2e-ci.yaml | 2 +- package/Dockerfile-prometheus-federator | 4 ++-- scripts/build-chart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/prom-fed-e2e-ci.yaml b/.github/workflows/prom-fed-e2e-ci.yaml index 4e531bc4..aefcc7f4 100644 --- a/.github/workflows/prom-fed-e2e-ci.yaml +++ b/.github/workflows/prom-fed-e2e-ci.yaml @@ -102,7 +102,7 @@ jobs: - name: Perform pre-e2e image build run: | - EMBEDED_CHART_VERSION=0.3.4 REPO=${REPO} TAG=${TAG} make build; + EMBEDDED_CHART_VERSION=0.3.4 REPO=${REPO} TAG=${TAG} make build; REPO=${REPO} TAG=${TAG} make package; - name : Install k3d diff --git a/package/Dockerfile-prometheus-federator b/package/Dockerfile-prometheus-federator index 46fbc9c7..ba8b2623 100644 --- a/package/Dockerfile-prometheus-federator +++ b/package/Dockerfile-prometheus-federator @@ -21,10 +21,10 @@ FROM registry.suse.com/bci/golang:1.22 AS builder # Allow chart version config ARG TARGETPLATFORM -ARG EMBEDED_CHART_VERSION=0.3.4 +ARG EMBEDDED_CHART_VERSION=0.3.4 ARG TAG='' ARG REPO='' -ENV EMBEDED_CHART_VERSION=$EMBEDED_CHART_VERSION TAG=$TAG REPO=$REPO +ENV EMBEDDED_CHART_VERSION=$EMBEDDED_CHART_VERSION TAG=$TAG REPO=$REPO WORKDIR /usr/src/app COPY --from=helm ./helm/bin/helm /usr/local/bin/ diff --git a/scripts/build-chart b/scripts/build-chart index 3b1f05e9..2d0bb140 100755 --- a/scripts/build-chart +++ b/scripts/build-chart @@ -17,7 +17,7 @@ declare -r chart_options=( ) CHART=${CHART:-${chart_options[$BUILD_TARGET]}} -CHART_VERSION=${EMBEDED_CHART_VERSION:-$(find ./charts/${CHART} -maxdepth 1 -mindepth 1 -type d | tr - \~ | sort -rV | tr \~ - | head -n1 | cut -d'/' -f4)} +CHART_VERSION=${EMBEDDED_CHART_VERSION:-$(find ./charts/${CHART} -maxdepth 1 -mindepth 1 -type d | tr - \~ | sort -rV | tr \~ - | head -n1 | cut -d'/' -f4)} # Prepare and package charts package-charts "charts/${CHART}/${CHART_VERSION}" ./build/charts From 9b81ed438af88cb0256b371221cb007092e99209 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 13:52:55 -0400 Subject: [PATCH 02/45] Add tests from hpo+hl repo --- tests/e2e/e2e_suite_test.go | 80 ++ tests/e2e/e2e_test.go | 397 ++++++++++ tests/examples/foo-chart/.helmignore | 23 + tests/examples/foo-chart/Chart.yaml | 6 + .../foo-chart/templates/configmap.yaml | 7 + tests/examples/foo-chart/values.yaml | 1 + tests/go.mod | 64 ++ tests/go.sum | 717 ++++++++++++++++++ 8 files changed, 1295 insertions(+) create mode 100644 tests/e2e/e2e_suite_test.go create mode 100644 tests/e2e/e2e_test.go create mode 100644 tests/examples/foo-chart/.helmignore create mode 100644 tests/examples/foo-chart/Chart.yaml create mode 100644 tests/examples/foo-chart/templates/configmap.yaml create mode 100644 tests/examples/foo-chart/values.yaml create mode 100644 tests/go.mod create mode 100644 tests/go.sum diff --git a/tests/e2e/e2e_suite_test.go b/tests/e2e/e2e_suite_test.go new file mode 100644 index 00000000..76ceaeba --- /dev/null +++ b/tests/e2e/e2e_suite_test.go @@ -0,0 +1,80 @@ +package e2e_test + +import ( + "context" + "errors" + "os" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + lockerv1alpha1 "github.com/rancher/helm-locker/pkg/apis/helm.cattle.io/v1alpha1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + + "k8s.io/client-go/rest" + + env "github.com/caarlos0/env/v11" + "github.com/kralicky/kmatch" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +func TestE2e(t *testing.T) { + SetDefaultEventuallyTimeout(10 * time.Second) + SetDefaultEventuallyPollingInterval(50 * time.Millisecond) + SetDefaultConsistentlyDuration(2 * time.Second) + SetDefaultConsistentlyPollingInterval(50 * time.Millisecond) + RegisterFailHandler(Fail) + RunSpecs(t, "E2e Suite") +} + +var ( + k8sClient client.Client + cfg *rest.Config + testCtx context.Context + clientSet *kubernetes.Clientset +) + +type TestSpec struct { + Kubeconfig string `env:"KUBECONFIG,required"` +} + +func (t *TestSpec) Validate() error { + var errs []error + if _, err := os.Stat(t.Kubeconfig); err != nil { + errs = append(errs, err) + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + return nil +} + +var _ = BeforeSuite(func() { + ts := TestSpec{} + Expect(env.Parse(&ts)).To(Succeed(), "Could not parse test spec from environment variables") + Expect(ts.Validate()).To(Succeed(), "Invalid input e2e test spec") + + ctxCa, ca := context.WithCancel(context.Background()) + DeferCleanup(func() { + ca() + }) + + testCtx = ctxCa + newCfg, err := config.GetConfig() + Expect(err).NotTo(HaveOccurred(), "Could not initialize kubernetes client config") + cfg = newCfg + newClientset, err := kubernetes.NewForConfig(cfg) + Expect(err).To(Succeed(), "Could not initialize kubernetes clientset") + clientSet = newClientset + + newK8sClient, err := client.New(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred(), "Could not initialize kubernetes client") + k8sClient = newK8sClient + lockerv1alpha1.AddToScheme(k8sClient.Scheme()) + apiextensionsv1.AddToScheme(k8sClient.Scheme()) + kmatch.SetDefaultObjectClient(k8sClient) +}) diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go new file mode 100644 index 00000000..755b13af --- /dev/null +++ b/tests/e2e/e2e_test.go @@ -0,0 +1,397 @@ +package e2e_test + +import ( + "errors" + "fmt" + "os/exec" + + . "github.com/kralicky/kmatch" + "github.com/rancher/helm-locker/pkg/apis/helm.cattle.io/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func createIfNotExist(obj client.Object) { + err := k8sClient.Create(testCtx, obj) + if err != nil && !apierrors.IsAlreadyExists(err) { + Fail(fmt.Sprintf("Failed to create object %s", err)) + } +} + +const ( + objectSetHash = "objectset.rio.cattle.io/hash" + + objectSetApplied = "objectset.rio.cattle.io/applied" + objsetSetId = "objectset.rio.cattle.io/id" + objectSetOnwerGVK = "objectset.rio.cattle.io/owner-gvk" + ownerName = "objectset.rio.cattle.io/owner-name" + ownerNamespace = "objectset.rio.cattle.io/owner-namespace" +) + +const ( + exampleReleaseName = "foochart" + exampleReleaseNs = "foo" +) + +var _ = Describe("E2E helm locker operator tests", Ordered, Label("integration"), func() { + When("we use the helm locker operator", func() { + Specify("Expect to find prerequisited CRDs in test cluster", func() { + // loosely checks that the embedded helm controller is installed + gvk := schema.GroupVersionKind{ + Group: "helm.cattle.io", + Version: "v1", + Kind: "HelmChart", + } + + Eventually(GVK(gvk)).Should(Exist()) + }) + + It("should run the helm project operator", func() { + // TODO : setup helm controller here, once we fix the dependency mess + ns := "cattle-helm-system" + err := k8sClient.Create(testCtx, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }) + if err != nil && !apierrors.IsAlreadyExists(err) { + Fail(fmt.Sprintf("Failed to create namespace %s", err)) + } + }) + + It("Should have applied the helmrelease CRD", func() { + helmRelease := schema.GroupVersionKind{ + Group: "helm.cattle.io", + Version: "v1alpha1", + Kind: "HelmRelease", + } + + Eventually(GVK(helmRelease)).Should(Exist()) + }) + + It("should install an example helm chart", func() { + cmd := exec.CommandContext( + testCtx, + "helm", + "upgrade", + "--install", + "-n", + exampleReleaseNs, + "--create-namespace", + exampleReleaseName, + "../examples/foo-chart", + "--set", + "contents=\"abc\"", + ) + err := cmd.Start() + Expect(err).NotTo(HaveOccurred(), "Failed to run helm command") + cmd.Stdout = GinkgoWriter + cmd.Stderr = GinkgoWriter + err = cmd.Wait() + Expect(err).NotTo(HaveOccurred(), "helm upgrade command had a non-zero exit code") + + By("verifying the resource managed by the example chart exists") + cfg := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-configmap", + Namespace: exampleReleaseNs, + }, + } + Eventually(Object(cfg)).Should(ExistAnd( + HaveLabels( + "app.kubernetes.io/managed-by", + "Helm", + ), + HaveAnnotations( + "meta.helm.sh/release-name", + exampleReleaseName, + "meta.helm.sh/release-namespace", + exampleReleaseNs, + ), + HaveData( + "contents", "abc", + ), + )) + }) + + When("we create a helm release", func() { + It("should create a helm release", func() { + release := &v1alpha1.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-release", + Namespace: "cattle-helm-system", + }, + Spec: v1alpha1.HelmReleaseSpec{ + Release: v1alpha1.ReleaseKey{ + Name: exampleReleaseName, + Namespace: exampleReleaseNs, + }, + }, + } + createIfNotExist(release) + + By("Verifing it has the appropriate annotations and finalizers") + Eventually(Object(release)).Should( + ExistAnd( + HaveAnnotations( + "helmreleases.cattle.io/managed-by", "helm-locker", + ), + HaveFinalizers("wrangler.cattle.io/on-helm-release-remove"), + ), + ) + + By("Verifying the helm-locker is consistently in the deployed state", func() { + extractState := func() string { + retRelease, err := Object(release)() + if err != nil { + return v1alpha1.UnknownState + } + return retRelease.Status.State + } + Consistently(extractState).Should(Equal(v1alpha1.DeployedState)) + }) + }) + + Specify("We should not be able to edit or delete resources managed by the helm-chart", func() { + By("verifying the config map has the correct objectset annotations and labels") + origHash := "" + origApplied := "" + cfg := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-configmap", + Namespace: "foo", + }, + Data: map[string]string{ + "contents": "Hello, World! Updated", + }, + } + Eventually(func() error { + errs := []error{} + + retCfg, err := Object(cfg)() + if err != nil { + return err + } + + if val, ok := retCfg.Labels[objectSetHash]; !ok { + errs = append(errs, errors.New("objectset hash label not found")) + } else { + origHash = val + } + + if val, ok := retCfg.Annotations[objectSetApplied]; !ok { + errs = append(errs, errors.New("objectset hash not found or incorrect")) + } else { + origApplied = val + } + + if val, ok := retCfg.Annotations[objsetSetId]; !ok || val != "object-set-applier" { + errs = append(errs, fmt.Errorf("objectset id not found or incorrect: '%s'", val)) + } + if val, ok := retCfg.Annotations[objectSetOnwerGVK]; !ok || val != "internal.cattle.io/v1alpha1, Kind=objectSetState" { + errs = append(errs, fmt.Errorf("objectset owner gvk not found or incorrect '%s'", val)) + } + return errors.Join(errs...) + }).Should(Succeed()) + + Expect(origHash).NotTo(BeEmpty(), "helm locker should manage the objectset hash") + Expect(origApplied).NotTo(BeEmpty(), "helm locker should manage the objectset applied annotation") + + By("trying to update the helm locked resource") + Expect(k8sClient.Update(testCtx, &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-configmap", + Namespace: "foo", + }, + Data: map[string]string{ + "contents": "Hello, World! Updated", + }, + })).To(Succeed()) + + By("verifying the update was not applied") + Eventually(Object(cfg)).Should(ExistAnd( + HaveData( + "contents", "abc", + ), + HaveAnnotations( + ownerName, exampleReleaseName, + ownerNamespace, exampleReleaseNs, + ), + )) + Eventually(func() error { + retCfg, err := Object(cfg)() + if err != nil { + return err + } + if val, ok := retCfg.Labels[objectSetHash]; !ok || val != origHash { + return fmt.Errorf("objectset hash label does not match the original one : '%s' vs '%s'", origHash, val) + } + if val, ok := retCfg.Annotations[objectSetApplied]; !ok || val != origApplied { + return fmt.Errorf("objectset applied annotation does not match the original one : '%s' vs '%s'", origApplied, val) + } + return nil + }).Should(Succeed()) + + Consistently(Object(cfg)).Should(ExistAnd( + HaveData( + "contents", "abc", + ), + HaveAnnotations( + ownerName, exampleReleaseName, + ownerNamespace, exampleReleaseNs, + ), + )) + }) + + Specify("We should only be able to update resources managed by the helm chart through helm", func() { + cfg := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-configmap", + Namespace: exampleReleaseNs, + }, + } + By("extracting the objectset hash before the helm update") + origHash := "" + origApplied := "" + Eventually(func() error { + retCfg, err := Object(cfg)() + if err != nil { + return err + } + if val, ok := retCfg.Labels[objectSetHash]; ok { + origHash = val + } + if val, ok := retCfg.Annotations[objectSetApplied]; ok { + origApplied = val + } + return nil + }).Should(Succeed()) + Expect(origHash).NotTo(BeEmpty(), "helm locker should be managing the object set hash") + Expect(origApplied).NotTo(BeEmpty(), "helm locker should be managing the object set hash") + Eventually(func() error { + return nil + }).Should(Succeed()) + + By("upgrading the helm resource using helm") + cmd := exec.CommandContext( + testCtx, + "helm", + "upgrade", + "--install", + "-n", + exampleReleaseNs, + "--create-namespace", + exampleReleaseName, + "../examples/foo-chart", + "--set", + "contents=\"Updated!\"", + ) + err := cmd.Start() + Expect(err).NotTo(HaveOccurred(), "Failed to run helm command") + + err = cmd.Wait() + Expect(err).NotTo(HaveOccurred(), "helm install command had a non-zero exit code") + + By("verifying the resource managed by the example chart exists") + Eventually(Object(cfg)).Should(ExistAnd( + HaveLabels( + "app.kubernetes.io/managed-by", + "Helm", + ), + HaveAnnotations( + "meta.helm.sh/release-name", + exampleReleaseName, + "meta.helm.sh/release-namespace", + exampleReleaseNs, + ), + HaveData( + "contents", "Updated!", + ), + )) + + Consistently(Object(cfg)).Should(ExistAnd( + HaveData( + "contents", "Updated!", + ), + )) + + By("extracting the objectset hash after the helm update") + newHash := "" + newApplied := "" + Eventually(func() error { + retCfg, err := Object(cfg)() + if err != nil { + return err + } + if val, ok := retCfg.Labels[objectSetHash]; ok { + newHash = val + } + if val, ok := retCfg.Annotations[objectSetApplied]; ok { + newApplied = val + } + return nil + }).Should(Succeed()) + Expect(newHash).NotTo(BeEmpty(), "helm locker should be managing the object set hash") + Expect(newApplied).NotTo(BeEmpty(), "helm locker should be managing the object set hash") + Expect(newHash).To(Equal(origHash), "objectset hash should not have changed after helm update, since no new resource keys are tracked") + Expect(newApplied).NotTo( + Equal(origApplied), + "objectset applied annotation should have changed after helm update", + ) + }) + }) + + When("we delete the helm release", func() { + It("should remove the helm release", func() { + release := &v1alpha1.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-release", + Namespace: "cattle-helm-system", + }, + } + err := k8sClient.Delete(testCtx, release) + Expect(err).ToNot(HaveOccurred()) + + By("Verifing it has the appropriate annotations and finalizers") + Eventually(Object(release)).Should(Not(Exist())) + }) + + Specify("we should be able to edit and delete resources managed by the helm-chart", func() { + Expect(k8sClient.Update(testCtx, &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-configmap", + Namespace: exampleReleaseNs, + }, + Data: map[string]string{ + "contents": "Hello, World! Updated", + }, + })).To(Succeed()) + + By("verifying the update was applied") + cfg := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-configmap", + Namespace: exampleReleaseNs, + }, + } + Eventually(Object(cfg)).Should(ExistAnd( + HaveData( + "contents", "Hello, World! Updated", + ), + )) + + Consistently(Object(cfg)).Should(ExistAnd( + HaveData( + "contents", "Hello, World! Updated", + ), + )) + }) + }) + }) +}) diff --git a/tests/examples/foo-chart/.helmignore b/tests/examples/foo-chart/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/tests/examples/foo-chart/.helmignore @@ -0,0 +1,23 @@ +# 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 +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/tests/examples/foo-chart/Chart.yaml b/tests/examples/foo-chart/Chart.yaml new file mode 100644 index 00000000..6854e913 --- /dev/null +++ b/tests/examples/foo-chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: foo-chart +description: A Helm chart for Kubernetes +type: application +version: 0.1.0 +appVersion: "0.1.0" diff --git a/tests/examples/foo-chart/templates/configmap.yaml b/tests/examples/foo-chart/templates/configmap.yaml new file mode 100644 index 00000000..b6a11d53 --- /dev/null +++ b/tests/examples/foo-chart/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo-configmap + namespace: {{ .Release.Namespace }} +data: + "contents" : {{ .Values.contents}} diff --git a/tests/examples/foo-chart/values.yaml b/tests/examples/foo-chart/values.yaml new file mode 100644 index 00000000..c96ca22a --- /dev/null +++ b/tests/examples/foo-chart/values.yaml @@ -0,0 +1 @@ +contents : "Hello, World!" \ No newline at end of file diff --git a/tests/go.mod b/tests/go.mod new file mode 100644 index 00000000..e907fb55 --- /dev/null +++ b/tests/go.mod @@ -0,0 +1,64 @@ +module example.com + +go 1.22.3 + +require ( + github.com/caarlos0/env/v11 v11.0.0 + github.com/kralicky/kmatch v0.0.0-20240530002100-abef8971a37b + github.com/onsi/ginkgo/v2 v2.17.3 + github.com/onsi/gomega v1.33.1 + github.com/rancher/helm-locker v0.0.1 + k8s.io/api v0.30.1 + k8s.io/apiextensions-apiserver v0.30.0 + k8s.io/apimachinery v0.30.1 + k8s.io/client-go v0.30.0 + sigs.k8s.io/controller-runtime v0.18.2 +) + +require ( + emperror.dev/errors v0.8.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rancher/wrangler v0.8.11-0.20220217210408-3ecd23dfea3b // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.21.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/tests/go.sum b/tests/go.sum new file mode 100644 index 00000000..e6aa32e8 --- /dev/null +++ b/tests/go.sum @@ -0,0 +1,717 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +emperror.dev/errors v0.8.1 h1:UavXZ5cSX/4u9iyvH6aDcuGkVjeexUGJ7Ij7G4VfQT0= +emperror.dev/errors v0.8.1/go.mod h1:YcRvLPh626Ubn2xqtoprejnA5nFha+TJ+2vew48kWuE= +github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +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/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/caarlos0/env/v11 v11.0.0 h1:ZIlkOjuL3xoZS0kmUJlF74j2Qj8GMOq3CDLX/Viak8Q= +github.com/caarlos0/env/v11 v11.0.0/go.mod h1:2RC3HQu8BQqtEK3V4iHPxj0jOdWdbPpWJ6pOueeU1xM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b/go.mod h1:NAJj0yf/KaRKURN6nyi7A9IZydMivZEm9oQLWNjfKDc= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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 v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= +github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= +github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kralicky/kmatch v0.0.0-20240530002100-abef8971a37b h1:OZRT5i0+pRzxyMu0db5nZF0z7e9M6BmSezGeCueuryg= +github.com/kralicky/kmatch v0.0.0-20240530002100-abef8971a37b/go.mod h1:JRnTh8vZ0vEr8ljMxpSWPDD6b9LcTtdJ+ofrI6yCLiA= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +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-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU= +github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA= +github.com/rancher/helm-locker v0.0.1 h1:v/m7Uu5wGivn+FQn5/xMuUG2L+CzocSzD7sjM7+/74E= +github.com/rancher/helm-locker v0.0.1/go.mod h1:PRThM9wL4o7MXJwUDeAk/+9s1vpmbRbacnGm+HoGqbY= +github.com/rancher/lasso v0.0.0-20210616224652-fc3ebd901c08/go.mod h1:9qZd/S8DqWzfKtjKGgSoHqGEByYmUE3qRaBaaAHwfEM= +github.com/rancher/wrangler v0.8.11-0.20220217210408-3ecd23dfea3b h1:nFwp2dz+JHH96joqSVRYelOgmTQS9K8DSC3A/d1Gc9I= +github.com/rancher/wrangler v0.8.11-0.20220217210408-3ecd23dfea3b/go.mod h1:Lte9WjPtGYxYacIWeiS9qawvu2R4NujFU9xuXWJvc/0= +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-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= +github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= +go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +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/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190125232054-d66bd3c5d5a6/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/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191017205301-920acffc3e65/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +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.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48= +k8s.io/api v0.0.0-20191214185829-ca1d04f8b0d3/go.mod h1:itOjKREfmUTvcjantxOsyYU5mbFsU7qUnyUuRfF5+5M= +k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= +k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= +k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY= +k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY= +k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM= +k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= +k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= +k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= +k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y= +k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4= +k8s.io/apimachinery v0.0.0-20191214185652-442f8fb2f03a/go.mod h1:Ng1IY8TS7sC44KJxT/WUR6qFRfWwahYYYpNXyYRKOCY= +k8s.io/apimachinery v0.0.0-20191216025728-0ee8b4573e3a/go.mod h1:Ng1IY8TS7sC44KJxT/WUR6qFRfWwahYYYpNXyYRKOCY= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig= +k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U= +k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= +k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= +k8s.io/cli-runtime v0.0.0-20191214191754-e6dc6d5c8724/go.mod h1:wzlq80lvjgHW9if6MlE4OIGC86MDKsy5jtl9nxz/IYY= +k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= +k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk= +k8s.io/client-go v0.0.0-20191214190045-a32a6f7a3052/go.mod h1:tAaoc/sYuIL0+njJefSAmE28CIcxyaFV4kbIujBlY2s= +k8s.io/client-go v0.0.0-20191219150334-0b8da7416048/go.mod h1:ZEe8ZASDUAuqVGJ+UN0ka0PfaR+b6a6E1PGsSNZRui8= +k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= +k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= +k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= +k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE= +k8s.io/code-generator v0.0.0-20191214185510-0b9b3c99f9f2/go.mod h1:BjGKcoq1MRUmcssvHiSxodCco1T6nVIt4YeCT5CMSao= +k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA= +k8s.io/component-base v0.0.0-20191214190519-d868452632e2/go.mod h1:wupxkh1T/oUDqyTtcIjiEfpbmIHGm8By/vqpSKC6z8c= +k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= +k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-aggregator v0.18.0/go.mod h1:ateewQ5QbjMZF/dihEFXwaEwoA4v/mayRvzfmvb6eqI= +k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd/go.mod h1:9ehGcuUGjXVZh0qbYSB0vvofQw2JQe6c6cO0k4wu/Oo= +k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +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/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/cli-utils v0.16.0/go.mod h1:9Jqm9K2W6ShhCxsEuaz6HSRKKOXigPUx3ZfypGgxBLY= +sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= +sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q= +sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= +sigs.k8s.io/kustomize/kyaml v0.4.0/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= From 9cae444ab28880fdfeffbc276837ad0a08653c24 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 14:05:19 -0400 Subject: [PATCH 03/45] Merge repo readmes --- README-helm-locker.md | 81 +++++++++++++++++++++++++++++++++ README-helm-project-operator.md | 56 +++++++++++++++++++++++ README.md | 27 ++++++++--- 3 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 README-helm-locker.md create mode 100644 README-helm-project-operator.md diff --git a/README-helm-locker.md b/README-helm-locker.md new file mode 100644 index 00000000..072958da --- /dev/null +++ b/README-helm-locker.md @@ -0,0 +1,81 @@ +helm-locker +======== + +Helm Locker is a Kubernetes operator that prevents resource drift on (i.e. "locks") Kubernetes objects that are tracked by Helm 3 releases. + +Once installed, a user can create a `HelmRelease` CR in the `Helm Release Registration Namespace` (default: `cattle-helm-system`) by providing: +1. The name of a Helm 3 release +2. The namespace that contains the Helm Release Secret (supplied as `--namespace` on the `helm install` command that created the release) + +Once created, the Helm Locker controllers will watch all resources tracked by the Helm Release Secret and automatically revert any changes to the persisted resources that were not made through Helm (e.g. changes that were directly applied via `kubectl` or other controllers). + +## Getting Started + +For more information, see the [Getting Started guide](docs/gettingstarted.md). + +## Who needs Helm Locker? + +Anyone who would like to declaratively manage resources deployed by existing Helm chart releases. + +## How is this different from projects like `fluxcd/helm-controller`? + +Projects like [`fluxcd/helm-controller`](https://github.com/fluxcd/helm-controller) allow users to declaratively manage **Helm charts from deployment to release**, whereas this project only allows you lock an **existing** Helm chart release; as a result, the scope of this project is much more narrow than what is offered by `fluxcd/helm-controller` and should be integrable with any solution that produces Helm releases. + +If you are looking for a larger, more opinionated solution that also has features around **how** Helm charts should be deployed onto a cluster (e.g. from a `GitRepository` or `Bucket` or `HelmRepository`), this is not the project for you. + +However, if you are looking for something light-weight that simply guarentees that **Helm is the only way to modify resources tracked by Helm releases**, this is a good solution to use. + +## How does Helm Locker know whether a release was changed by Helm or by another source? + +In order to prevent multiple Helm instances from performing the same upgrade at the same time, Helm 3 will always first update the `info.status` field on a Helm Release Secret from `deployed` to another state (e.g. `pending-upgrade`, `pending-install`, `uninstalling`, etc.) before performing the operation; once the operation is complete, the Helm Release Secret is expected to be reverted back to `deployed`. + +Therefore, if Helm Locker observes a Helm Release Secret tied to a `HelmRelease` has been updated, it will check to see what the current status of the release is; if the release is anything but `deployed`, Helm Locker will not perform any operations on the resources tracked by this release, which will allow upgrades to occur as expected. + +However, once a release is `deployed`, if what is tracked in the Helm secret is different than what is currently installed onto the cluster, Helm Locker will revert all resources back to what was tracked by the Helm release (in case a change was made to the resource tracked by the Helm Release while the release was being modified). + +## Developing + +### Which branch do I make changes on? + +Helm Locker is built and released off the contents of the `main` branch. To make a contribution, open up a PR to the `main` branch. + +For more information, see the [Developing guide](docs/developing.md). + +## Debugging + +### How do I manually inspect the content of the Helm Release Secret to debug a possible Helm Locker issue? + +Identify the release namespace (`RELEASE_NAMESPACE`), release name (`RELEASE_NAME`), and release version (`RELEASE_VERSION`) that identifies the Secret used by Helm to store the release data. Then, with access to your Kubernetes cluster via `kubectl`, run the following command (e.g. run base64 decode, base64 decode, gzip decompress the .data.release of the Secret): + +```bash +RELEASE_NAMESPACE=default +RELEASE_NAME=test +RELEASE_VERSION=v1 + +# Magic one-liner! jq call is optional... +kubectl get secrets -n ${RELEASE_NAMESPACE} sh.helm.release.v1.${RELEASE_NAME}.${RELEASE_VERSION} -o=jsonpath='{ .data.release }' | base64 -d | base64 -d | gunzip -c | jq -r '.' +``` + +## Building + +`make` + + +## Running + +`./bin/helm-locker` + +## License +Copyright (c) 2020 [Rancher Labs, Inc.](http://rancher.com) + +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](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/README-helm-project-operator.md b/README-helm-project-operator.md new file mode 100644 index 00000000..80d80872 --- /dev/null +++ b/README-helm-project-operator.md @@ -0,0 +1,56 @@ +helm-project-operator +======== + +This repo contains a set of two interlinked projects: + +- The **Helm Project Operator** is a generic design for a Kubernetes Operator that acts on `ProjectHelmChart` CRs. +- **Helm Locker** is a Kubernetes operator that prevents resource drift on (i.e. "locks") Kubernetes objects that are tracked by Helm 3 releases. + +**Note: These project are not intended for standalone use.** + +For more info on _Helm Locker_, see the [dedicated README file](README-helm-locker.md). + +Helm Project Operator is intended to be implemented by a Project Operator (e.g. [`rancher/prometheus-federator`](https://github.com/rancher/prometheus-federator)) but provides a common definition for all Project Operators to use in order to support deploy specific, pre-bundled Helm charts (tied to a unique registered `spec.helmApiVersion` associated with the operator) across all project namespaces detected by this operator. + +## Getting Started + +For more information, see the [Getting Started guide](docs/helm-project-operator/gettingstarted.md). + +## Developing + +### Which branch do I make changes on? + +Helm Project Operator is built and released off the contents of the `main` branch. To make a contribution, open up a PR to the `main` branch. + +For more information, see the [Developing guide](docs/helm-project-operator/developing.md). + +## Design + +Helm Project Operator is built on top of [k3s-io/helm-controller](https://github.com/k3s-io/helm-controller) and [rancher/helm-locker](https://github.com/rancher/helm-locker). For more information on the design of the underlying components, please see the `README.md` on their respective repositories. + +For an example of how Helm Project Operator can be implemented, please see [`rancher/prometheus-federator`](https://github.com/rancher/prometheus-federator). + +For more information in general, please see [docs/design.md](docs/helm-project-operator/design.md). + +## Building + +`make` + +## Running + +`./bin/helm-project-operator` + +## License +Copyright (c) 2020 [Rancher Labs, Inc.](http://rancher.com) + +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](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/README.md b/README.md index fbb63009..c9aa8370 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,25 @@ prometheus-federator ======== -Prometheus Federator is an operator (powered by [`rancher/helm-project-operator`](https://github.com/rancher/helm-project-operator) and [`rancher/charts-build-scripts`](https://github.com/rancher/charts-build-scripts)) that manages deploying one or more Project Monitoring Stacks composed of the following set of resources that are scoped to project namespaces: +This repo contains a set of three interlinked projects: + +- The **Prometheus Federator** is a k8s Operator that manages deploying Project Monitoring Stacks. +- The **Helm Project Operator** is a generic design for a Kubernetes Operator that acts on `ProjectHelmChart` CRs. +- **Helm Locker** is a Kubernetes operator that prevents resource drift on (i.e. "locks") Kubernetes objects that are tracked by Helm 3 releases. + +> [!NOTE] +> The last two project (helm-project-operator and helm-locker) are not intended or supported for standalone use. + +For more info on _Helm Project Operator_, see the [dedicated README file](README-helm-project-operator.md). +For more info on _Helm Locker_, see the [dedicated README file](README-helm-locker.md). + +## Getting Started + +For more information, see the [Getting Started guide](docs/prometheus-federator/gettingstarted.md). + +## More Info + +Prometheus Federator is an operator (powered by [`rancher/helm-project-operator`](README-helm-project-operator.md) and [`rancher/charts-build-scripts`](README-helm-locker.md)) that manages deploying one or more Project Monitoring Stacks composed of the following set of resources that are scoped to project namespaces: - [Prometheus](https://prometheus.io/) (managed externally by [Prometheus Operator](https://github.com/prometheus-operator/prometheus-operator)) - [Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/) (managed externally by [Prometheus Operator](https://github.com/prometheus-operator/prometheus-operator)) - [Grafana](https://github.com/helm/charts/tree/master/stable/grafana) (deployed via an embedded Helm chart) @@ -16,11 +34,6 @@ For more information on ProjectHelmCharts and how to configure the underlying op For more information on how to configure the underlying Project Monitoring Stack, please read the [`README.md` of the underlying chart](packages/rancher-project-monitoring/charts/README.md) (`rancher-project-monitoring`). - -## Getting Started - -For more information, see the [Getting Started guide](docs/prometheus-federator/gettingstarted.md). - ## Developing ### Which branch do I make changes on? @@ -37,7 +50,7 @@ For more information, see the [Developing guide](docs/prometheus-federator/devel ## Running -`./bin/prometheus-federator` +`./build/bin/prometheus-federator` ## Versioning and Releasing For Rancher From 392b7fdc2e2b0209286528c4f2f24a518da5f806 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 14:09:41 -0400 Subject: [PATCH 04/45] Bring over docker files --- package/Dockerfile-helm-locker | 16 +++++++++ package/Dockerfile-helm-project-operator | 44 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 package/Dockerfile-helm-locker create mode 100644 package/Dockerfile-helm-project-operator diff --git a/package/Dockerfile-helm-locker b/package/Dockerfile-helm-locker new file mode 100644 index 00000000..3dd3d266 --- /dev/null +++ b/package/Dockerfile-helm-locker @@ -0,0 +1,16 @@ +FROM --platform=$BUILDPLATFORM registry.suse.com/bci/golang:1.22 AS builder +WORKDIR /usr/src/app +RUN zypper -n install git vim less file curl wget +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN BUILD_TARGET=helm-locker make build + +FROM registry.suse.com/bci/bci-micro:15.5 +RUN echo 'helmlocker:x:1000:1000::/home/helmlocker:/bin/bash' >> /etc/passwd && \ + echo 'helmlocker:x:1000:' >> /etc/group && \ + mkdir /home/helmlocker && \ + chown -R helmlocker:helmlocker /home/helmlocker +COPY --from=builder /usr/src/app/bin/helm-locker /usr/bin/ +USER helmlocker +CMD ["helm-locker"] diff --git a/package/Dockerfile-helm-project-operator b/package/Dockerfile-helm-project-operator new file mode 100644 index 00000000..c76d44f0 --- /dev/null +++ b/package/Dockerfile-helm-project-operator @@ -0,0 +1,44 @@ +# Image that provides cross compilation tooling. +FROM --platform=$BUILDPLATFORM rancher/mirrored-tonistiigi-xx:1.3.0 AS xx + +FROM --platform=$BUILDPLATFORM registry.suse.com/bci/golang:1.22 AS helm + +# Clone repository once, and reuse it for target archs. +ARG HELM_VERSION=release-v3.9.0 +ADD --keep-git-dir=true https://github.com/rancher/helm.git#${HELM_VERSION} /helm +RUN cd /helm && go mod download + +COPY --from=xx / / + +# Cross-compile instead of emulating the compilation on the target arch. +ARG TARGETPLATFORM +RUN xx-go --wrap && mkdir -p /run/lock +RUN make -C /helm + +RUN xx-verify --static /helm/bin/helm + +FROM registry.suse.com/bci/golang:1.22 AS builder + +ARG TARGETARCH +ENV BUILD_TARGET="helm-project-operator" + +WORKDIR /usr/src/app +ENV YQ_VERSION=v4.25.1 +RUN zypper -n install git curl wget make +RUN export ARCH=$(go env GOHOSTARCH) +RUN wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64 -O /usr/bin/yq && chmod +x /usr/bin/yq; +COPY --from=helm /helm/bin/helm /usr/local/bin/ +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN make build-chart +RUN make build + +FROM registry.suse.com/bci/bci-micro:15.6 +RUN echo 'helmprojectoperator:x:1000:1000::/home/helmprojectoperator:/bin/bash' >> /etc/passwd && \ + echo 'helmprojectoperator:x:1000:' >> /etc/group && \ + mkdir /home/helmprojectoperator && \ + chown -R helmprojectoperator:helmprojectoperator /home/helmprojectoperator +COPY --from=builder /usr/src/app/bin/helm-project-operator /usr/bin/ +USER helmprojectoperator +CMD ["helm-project-operator"] From 3af8c44e75f96e49c04f5e7eee66dd9d65cdab0d Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 14:25:45 -0400 Subject: [PATCH 05/45] Copy in hpo examples --- .../helm-project-operator/ci/namespace.yaml | 8 ++++++++ .../ci/project-helm-chart.yaml | 15 +++++++++++++++ examples/helm-project-operator/ci/project.yaml | 8 ++++++++ examples/helm-project-operator/example.yaml | 18 ++++++++++++++++++ .../project-label-example.yaml | 15 +++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 examples/helm-project-operator/ci/namespace.yaml create mode 100644 examples/helm-project-operator/ci/project-helm-chart.yaml create mode 100644 examples/helm-project-operator/ci/project.yaml create mode 100644 examples/helm-project-operator/example.yaml create mode 100644 examples/helm-project-operator/project-label-example.yaml diff --git a/examples/helm-project-operator/ci/namespace.yaml b/examples/helm-project-operator/ci/namespace.yaml new file mode 100644 index 00000000..39b8b1f3 --- /dev/null +++ b/examples/helm-project-operator/ci/namespace.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Namespace +metadata: + annotations: + field.cattle.io/projectId: local:p-example + labels: + field.cattle.io/projectId: p-example + name: e2e-hpo diff --git a/examples/helm-project-operator/ci/project-helm-chart.yaml b/examples/helm-project-operator/ci/project-helm-chart.yaml new file mode 100644 index 00000000..887392b5 --- /dev/null +++ b/examples/helm-project-operator/ci/project-helm-chart.yaml @@ -0,0 +1,15 @@ +# This is an example of a ProjectHelmChart that would be deployed onto a Helm Project Operator +# instance that responds to helmApiVersion dummy.cattle.io/v1alpha1 and does not utilize --project-label +# +# When --project-label is not provided, it is expected that spec.projectNamespaceSelector should be provided +# +apiVersion: helm.cattle.io/v1alpha1 +kind: ProjectHelmChart +metadata: + name: project-operator-example-chart + namespace: cattle-project-p-example +spec: + helmApiVersion: dummy.cattle.io/v1alpha1 + values: + data: + hello: e2e-ci diff --git a/examples/helm-project-operator/ci/project.yaml b/examples/helm-project-operator/ci/project.yaml new file mode 100644 index 00000000..708aaee5 --- /dev/null +++ b/examples/helm-project-operator/ci/project.yaml @@ -0,0 +1,8 @@ +apiVersion: management.cattle.io/v3 +kind: Project +metadata: + name: p-example + namespace: local +spec: + clusterName: local + displayName: HelmProjectOperator Example diff --git a/examples/helm-project-operator/example.yaml b/examples/helm-project-operator/example.yaml new file mode 100644 index 00000000..4cb7aa3e --- /dev/null +++ b/examples/helm-project-operator/example.yaml @@ -0,0 +1,18 @@ +# This is an example of a ProjectHelmChart that would be deployed onto a Helm Project Operator +# instance that responds to helmApiVersion dummy.cattle.io/v1alpha1 and does not utilize --project-label +# +# When --project-label is not provided, it is expected that spec.projectNamespaceSelector should be provided +# +apiVersion: helm.cattle.io/v1alpha1 +kind: ProjectHelmChart +metadata: + name: test + namespace: cattle-helm-system +spec: + helmApiVersion: dummy.cattle.io/v1alpha1 + projectNamespaceSelector: + matchLabels: + team: rancher + values: + data: + hello: world \ No newline at end of file diff --git a/examples/helm-project-operator/project-label-example.yaml b/examples/helm-project-operator/project-label-example.yaml new file mode 100644 index 00000000..7eee0e08 --- /dev/null +++ b/examples/helm-project-operator/project-label-example.yaml @@ -0,0 +1,15 @@ +# This is an example of a ProjectHelmChart that would be deployed onto a Helm Project Operator +# instance that responds to helmApiVersion dummy.cattle.io/v1alpha1 and utilizes --project-label +# +# When --project-label is provided, spec.projectNamespaceSelector is ignored and can be omitted. +# +apiVersion: helm.cattle.io/v1alpha1 +kind: ProjectHelmChart +metadata: + name: test + namespace: cattle-project-p-ranch +spec: + helmApiVersion: dummy.cattle.io/v1alpha1 + values: + data: + hello: world \ No newline at end of file From 750e9e6e3e1d504fdc0bcb1898f9d52d0e321664 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 14:26:33 -0400 Subject: [PATCH 06/45] bring in docs --- docs/helm-locker/developing.md | 94 +++++++++++++ docs/helm-locker/gettingstarted.md | 41 ++++++ docs/helm-project-operator/design.md | 77 +++++++++++ docs/helm-project-operator/developing.md | 128 ++++++++++++++++++ .../helm-project-operator/experimental/e2e.md | 67 +++++++++ docs/helm-project-operator/gettingstarted.md | 49 +++++++ 6 files changed, 456 insertions(+) create mode 100644 docs/helm-locker/developing.md create mode 100644 docs/helm-locker/gettingstarted.md create mode 100644 docs/helm-project-operator/design.md create mode 100644 docs/helm-project-operator/developing.md create mode 100644 docs/helm-project-operator/experimental/e2e.md create mode 100644 docs/helm-project-operator/gettingstarted.md diff --git a/docs/helm-locker/developing.md b/docs/helm-locker/developing.md new file mode 100644 index 00000000..a174ec3c --- /dev/null +++ b/docs/helm-locker/developing.md @@ -0,0 +1,94 @@ +# Developing Helm Locker + +## Repository Structure + +```bash +## This directory contains Helm charts that can be used to deploy Helm Locker in a Kubernetes cluster in the cattle-helm-system namespace +charts/ + + ## The main chart that deploys Helm Locker in the cluster. + helm-locker/ + + ## A dummy chart that can be deployed as a Helm release in the cluster under the release name 'helm-locker-example' and the namespace 'cattle-helm-system' + ## + ## By default, it deploys with a HelmRelease CR that targets itself. + ## + ## Depends on 'helm-locker' being deployed onto the cluster first. + helm-locker-example/ + +## This directory will contain additional docs to assist users in getting started with using Helm Locker +docs/ + +## This directory contains the image that is used to build rancher/helm-locker, which is hosted on hub.docker.com +package/ + Dockerfile-helm-project-operator + +## The main source directory for the code. See below for more details. +pkg/ + +## The Dockerfile-helm-project-operator used to run CI and other scripts executed by make in a Docker container (powered by https://github.com/rancher/dapper) +Dockerfile-helm-project-operator.dapper + +## The file that contains the underlying actions that 'go generate' needs to execute on a call to it. Includes the logic for generating controllers and updating crds.yaml under the crds/ directory +generate.go + +## The main entrypoint into HelmLocker +main.go +``` + +## Making changes to the codebase (`pkg`) + +Most of the code for Helm Locker is contained in the `pkg` directory, which has the following structure: + +```bash +## This directory contains the definition of a HelmRelease CR under release.go; if you need to add new fields to HelmRelease CRs, this is where you would make the change +apis/ + +## These directories manage all the logic around 'go generate', including the creation of the 'generated/' directory that contains all the underlying controllers that are auto-generated based on the API definition of the HelmRelease CR defined under 'apis/' +codegen/ +crd/ +version/ +generated/ + +## These directories are the core controller directories that manage how the operator watches HelmReleases and executes operations on the underlying in-memory ObjectSet LockableRegister (Lock, Unlock, Set, Delete) +controllers/ + ## This directory is where logic is defined for watching Helm Release Secrets targeted by HelmReleases and automatically keeping resources locked or unlocked + release/ + ## This is where the underlying context used by all controllers of this operator are registered, all using the same underlying SharedControllerFactory + controller.go +## A utility package to help wrap getting Helm releases via Helm library calls +releases/ + +## These directories implement an object that satisfies the LockableRegister interface; it is used as an underlying set of libraries that Helm Locker calls upon to achieve locking or unlocking HelmReleases (tracked as ObjectSets, or a []runtime.Object) and dynamically starting controllers based on GVKs observed in tracked object sets +gvk/ +informerfactory/ +objectset/ +``` + +## Once you have made a change + +If you modified `pkg/apis` or `generate.go`, make sure you run `go generate`. + +Also, make sure you run `go mod tidy`. + +## Creating a Docker image based off of your changes + +To test your changes and create a Docker image to a specific Docker repository with a given tag, you should run `REPO= TAG= make` (e.g. `REPO=arvindiyengar TAG=dev make`), which will run the `./scripts/ci` script that builds, tests, validates, and packages your changes into a local Docker image (if you run `docker images`, it should show up as an image in the format `${REPO}/helm-locker:${TAG}`). + +If you don't want to run all the steps in CI every time you make a change, you could also run the following one-liner to build and package the image: + +```bash +REPO= +TAG= + +GOOS=linux CGO_ENABLED=0 go build -ldflags "-extldflags -static -s" -o bin/helm-locker && REPO=${REPO} TAG=${TAG} make package +``` + +Once the image is successfully packaged, simply run `docker push ${REPO}/helm-locker:${TAG}` to push your image to your Docker repository. + +## Testing a custom Docker image build + +1. Ensure that your `KUBECONFIG` environment variable is pointing to your cluster (e.g. `export KUBECONFIG=; kubectl get nodes` should show the nodes of your cluster) and pull in this repository locally +2. Go to the root of your local copy of this repository and deploy the Helm Locker chart as a Helm 3 chart onto your cluster after overriding the image and tag values with your Docker repository and tag: run `helm upgrade --install --set image.repository="${REPO}/helm-locker" --set image.tag="${TAG}" --set image.pullPolicy=Always helm-locker -n cattle-helm-system charts/helm-locker` +> Note: Why do we set the Image Pull Policy to `Always`? If you update the Docker image on your fork, setting the Image Pull Policy to `Always` ensures that running `kubectl rollout restart -n cattle-helm-system deployment/helm-locker` is all you need to do to update your running deployment to the new image, since this would ensure redeploying a deployment triggers a image pull that uses your most up-to-date Docker image. +3. Profit! \ No newline at end of file diff --git a/docs/helm-locker/gettingstarted.md b/docs/helm-locker/gettingstarted.md new file mode 100644 index 00000000..8414f357 --- /dev/null +++ b/docs/helm-locker/gettingstarted.md @@ -0,0 +1,41 @@ +# Getting Started + +## Simple Installation + +### In Rancher (via Apps & Marketplace) + +1. Navigate to `Apps & Marketplace -> Repositories` in your target downstream cluster and create a Repository that points to a `Git repository containing Helm chart or cluster template definitions` where the `Git Repo URL` is `https://github.com/rancher/helm-locker` and the `Git Branch` is `main` +2. Navigate to `Apps & Marketplace -> Charts`; you should see two charts under the new Repository you created: `Helm Locker` and `Helm Locker Example Chart`. +3. Install `Helm Locker` first +4. Install `Helm Locker Example Chart` + +### In a normal Kubernetes cluster (via running Helm 3 locally) + +1. Install `helm-locker` onto your cluster via Helm to install the Helm Locker Operator + +``` +helm install -n cattle-helm-system helm-locker charts/helm-locker +``` + +2. Install `helm-locker-example` to check out a simple Helm chart containing a ConfigMap and a HelmRelease CR that targets the release itself and keeps it locked into place + +```bash +helm install -n cattle-helm-system helm-locker-example charts/helm-locker-example +``` + +### Checking if the HelmRelease works + +1. Ensure that the logs of `helm-locker` in the `cattle-helm-system` namespace show that the controller was able to acquire a lock and has started in that namespace +2. Try to delete or modify the ConfigMaps deployed by the `helm-locker-example` chart (`cattle-helm-system/my-config-map` and `cattle-helm-system/my-config-map-2`); any changes should automatically be overwritten and a log will show up in the Helm Locker logs that showed which ConfigMap it detected a change in +3. Run `kubectl describe helmreleases -n cattle-helm-system helm-locker-example`; you should be able to see events that have been triggered on changes. +4. Upgrade the `helm-locker-example` values to change the contents of the ConfigMap; you should see the modifications show up in the ConfigMap deployed in the cluster as well as events that have been triggered on Helm Locker noticing that change (i.e. you should see a `Transitioning` event that is emitted). + +## Uninstalling Helm Locker + +After deleting the Helm Charts, you may want to manually uninstall the CRDs from the cluster to clean them up: + +```bash +kubectl delete crds helmreleases.helm.cattle.io +``` + +> Note: Why aren't we packaging Helm Locker CRDs in a CRD chart? Since Helm Locker CRDs can be used for other projects (e.g. [rancher/helm-project-operator](https://github.com/rancher/helm-project-operator), [rancher/prometheus-federator](https://github.com/rancher/prometheus-federator), etc.) and Helm Locker itself can be deployed multiple times to the same cluster, the ownership model of having a single CRD chart that manages installing, upgrading, and uninstalling Helm Locker CRDs isn't a good model for managing CRDs. Instead, it's left as an explicit action that the user should take in order to delete the Helm Locker CRDs from the cluster with caution that it could affect other deployments reliant on those CRDs. \ No newline at end of file diff --git a/docs/helm-project-operator/design.md b/docs/helm-project-operator/design.md new file mode 100644 index 00000000..85cff75c --- /dev/null +++ b/docs/helm-project-operator/design.md @@ -0,0 +1,77 @@ +# Design + +## How does the operator work? + +1. On deploying a Helm Project Operator, users can create ProjectHelmCharts CRs with `spec.helmApiVersion` set to `dummy.cattle.io/v1alpha1` in a **Project Registration Namespace (`cattle-project-`)**. +2. On seeing each ProjectHelmChartCR, the operator will automatically deploy the embedded Helm chart on the Project Owner's behalf in the **Project Release Namespace (`cattle-project--dummy`)** based on a HelmChart CR and a HelmRelease CR automatically created by the ProjectHelmChart controller in the **Operator / System Namespace**. +3. RBAC will automatically be assigned in the Project Release Namespace to allow users to based on Role created in the Project Release Namespace with a given set of labels; this will be based on RBAC defined on the Project Registration Namespace against the [default Kubernetes user-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) (see below for more information about configuring RBAC). + +### What is a Project? + +In Helm Project Operator, a Project is a group of namespaces that can be identified by a `metav1.LabelSelector`; by default, the label used to identify projects is `field.cattle.io/projectId`, the label used to identify namespaces that are contained within a given [Rancher](https://rancher.com/) Project. + +### What is a ProjectHelmChart? + +A ProjectHelmChart is an instance of a (project-scoped) Helm chart deployed on behalf of a user who has permissions to create ProjectHelmChart resources in a Project Registration namespace. + +Generally, the best way to think about the ProjectHelmChart model is by comparing it to two other models: +1. Managed Kubernetes providers (EKS, GKE, AKS, etc.): in this model, a user has the ability to say "I want a Kubernetes cluster" but the underlying cloud provider is responsible for provisioning the infrastructure and offering **limited view and access** of the underlying resources created on their behalf; similarly, Helm Project Operator allows a Project Owner to say "I want this Helm chart deployed", but the underlying Operator is responsible for "provisioning" (deploying) the Helm chart and offering **limited view and access** of the underlying Kubernetes resources created on their behalf (based on configuring "least-privilege" Kubernetes RBAC for the Project Owners / Members in the newly created Project Release Namespace). +2. Dynamically-provisioned Persistent Volumes: in this model, a single resource (PersistentVolume) exists that allows you to specify a Storage Class that actually implements provisioning the underlying storage via a Storage Class Provisioner (e.g. Longhorn). Similarly, the ProjectHelmChart exists that allows you to specify a `spec.helmApiVersion` ("storage class") that actually implements deploying the underlying Helm chart via a Helm Project Operator (e.g. [`rancher/prometheus-federator`](https://github.com/rancher/prometheus-federator)). + +### Configuring the Helm release created by a ProjectHelmChart + +The `spec.values` of this ProjectHelmChart resources will correspond to the `values.yaml` override to be supplied to the underlying Helm chart deployed by the operator on the user's behalf; to see the underlying chart's `values.yaml` spec, either: +- View to the chart's definition located at [`rancher/helm-project-operator` under `charts/project-operator-example`](https://github.com/rancher/helm-project-operator/blob/main/charts/project-operator-example) (where the chart version will be tied to the version of this operator) +- Look for the ConfigMap named `dummy.cattle.io.v1alpha1` that is automatically created in each Project Registration Namespace, which will contain both the `values.yaml` and `questions.yaml` that was used to configure the chart (which was embedded directly into the `helm-project-operator` binary). + +### Namespaces + +All Helm Project Operators have three different classifications of namespaces that the operator looks out for: +1. **Operator / System Namespace**: this is the namespace that the operator is deployed into (e.g. `cattle-helm-system`). This namespace will contain all HelmCharts and HelmReleases for all ProjectHelmCharts watched by this operator. **Only Cluster Admins should have access to this namespace.** +2. **Project Registration Namespace (`cattle-project-`)**: this is the set of namespaces that the operator watches for ProjectHelmCharts within. The RoleBindings and ClusterRoleBindings that apply to this namespace will also be the source of truth for the auto-assigned RBAC created in the Project Release Namespace (see more details below). **Project Owners (admin), Project Members (edit), and Read-Only Members (view) should have access to this namespace**. +> Note: Project Registration Namespaces will be auto-generated by the operator and imported into the Project it is tied to if `.Values.global.cattle.projectLabel` is provided (which is set to `field.cattle.io/projectId` by default); this indicates that a Project Registration Namespace should be created by the operator if at least one namespace is observed with that label. The operator will not let these namespaces be deleted unless either all namespaces with that label are gone (e.g. this is the last namespace in that project, in which case the namespace will be marked with the label `"helm.cattle.io/helm-project-operator-orphaned": "true"`, which signals that it can be deleted) or it is no longer watching that project (because the project ID was provided under `.Values.helmProjectOperator.otherSystemProjectLabelValues`, which serves as a denylist for Projects). These namespaces will also never be auto-deleted to avoid destroying user data; it is recommended that users clean up these namespaces manually if desired on creating or deleting a project +> Note: if `.Values.global.cattle.projectLabel` is not provided, the Operator / System Namespace will also be the Project Registration Namespace +3. **Project Release Namespace (`cattle-project--dummy`)**: this is the set of namespaces that the operator deploys Helm charts within on behalf of a ProjectHelmChart; the operator will also automatically assign RBAC to Roles created in this namespace by the Helm charts based on bindings found in the Project Registration Namespace. **Only Cluster Admins should have access to this namespace; Project Owners (admin), Project Members (edit), and Read-Only Members (view) will be assigned limited access to this namespace by the deployed Helm Chart and Helm Project Operator.** +> Note: Project Release Namespaces are automatically deployed and imported into the project whose ID is specified under `.Values.helmProjectOperator.projectReleaseNamespaces.labelValue` (which defaults to the value of `.Values.global.cattle.systemProjectId` if not specified) whenever a ProjectHelmChart is specified in a Project Registration Namespace +> Note: Project Release Namespaces follow the same orphaning conventions as Project Registration Namespaces (see note above) +> Note: if `.Values.projectReleaseNamespaces.enabled` is false, the Project Release Namespace will be the same as the Project Registration Namespace + +### Helm Resources (HelmChart, HelmRelease) + +On deploying a ProjectHelmChart, the Helm Project Operator will automatically create and manage two child custom resources that manage the underlying Helm resources in turn: +- A HelmChart CR (managed via an embedded [k3s-io/helm-contoller](https://github.com/k3s-io/helm-controller) in the operator): this custom resource automatically creates a Job in the same namespace that triggers a `helm install`, `helm upgrade`, or `helm uninstall` depending on the change applied to the HelmChart CR; this CR is automatically updated on changes to the ProjectHelmChart (e.g. modifying the values.yaml) or changes to the underlying Project definition (e.g. adding or removing namespaces from a project). +> **Important Note: If a ProjectHelmChart is not deploying or updating the underlying Project Monitoring Stack for some reason, the Job created by this resource in the Operator / System namespace should be the first place you check to see if there's something wrong with the Helm operation; however, this is generally only accessible by a Cluster Admin.** +- A HelmRelease CR (managed via an embedded [rancher/helm-locker](https://github.com/rancher/helm-locker) in the operator): this custom resource automatically locks a deployed Helm release in place and automatically overwrites updates to underlying resources unless the change happens via a Helm operation (`helm install`, `helm upgrade`, or `helm uninstall` performed by the HelmChart CR). +> Note: HelmRelease CRs emit Kubernetes Events that detect when an underlying Helm release is being modified and locks it back to place; to view these events, you can use `kubectl describe helmrelease -n `; you can also view the logs on this operator to see when changes are detected and which resources were attempted to be modified + +Both of these resources are created for all Helm charts in the Operator / System namespaces to avoid escalation of privileges to underprivileged users. + +### RBAC + +As described in the section on namespaces above, Helm Project Operator expects that Project Owners, Project Members, and other users in the cluster with Project-level permissions (e.g. permissions in a certain set of namespaces identified by a single label selector) have minimal permissions in any namespaces except the Project Registration Namespace (which is imported into the project by default) and those that already comprise their projects. Therefore, in order to allow Project Owners to assign specific chart permissions to other users in their Project namespaces, the Helm Project Operator will automatically watch the following bindings: +- ClusterRoleBindings +- RoleBindings in the Project Release Namespace + +On observing a change to one of those types of bindings, the Helm Project Operator will check whether the `roleRef` that the the binding points to matches a ClusterRole with the name provided under `helmProjectOperator.releaseRoleBindings.clusterRoleRefs.admin`, `helmProjectOperator.releaseRoleBindings.clusterRoleRefs.edit`, or `helmProjectOperator.releaseRoleBindings.clusterRoleRefs.view`; by default, these roleRefs correspond will correspond to `admin`, `edit`, and `view` respectively, which are the [default Kubernetes user-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles). + +> Note: for Rancher RBAC users, these [default Kubernetes user-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) directly correlate to the `Project Owner`, `Project Member`, and `Read-Only` default Project Role Templates. + +If the `roleRef` matches, the Helm Project Operator will filter the `subjects` of the binding for all Users and Groups and use that to automatically construct a RoleBinding for each Role in the Project Release Namespace with the same name as the role and the following labels: +- `helm.cattle.io/project-helm-chart-role: {{ .Release.Name }}` +- `helm.cattle.io/project-helm-chart-role-aggregate-from: ` + +By default, the `project-operator-example` (the underlying chart deployed by Helm Project Operator) does not create any default roles; however, if a Cluster Admin would like to assign additional permissions to certain users, they can either directly assign RoleBindings in the Project Release Namespace to certain users or created Roles with the above two labels on them to allow Project Owners to control assigning those RBAC roles to users in their Project Registration namespaces. + +### Advanced Helm Project Operator Configuration + +|Value|Configuration| +|---|---------------------------| +|`valuesOverride`| Allows an Operator to override values that are set on each ProjectHelmChart deployment on an operator-level; user-provided options (specified on the `spec.values` of the ProjectHelmChart) are automatically overridden if operator-level values are provided. For an exmaple, see how the default value overrides `federate.targets` (note: when overriding list values like `federate.targets`, user-provided list values will **not** be concatenated) | +|`projectReleaseNamespaces.labelValues`| The value of the Project that all Project Release Namespaces should be auto-imported into (via label and annotation). Not recommended to be overridden on a Rancher setup. | +|`otherSystemProjectLabelValues`| Other namespaces that the operator should treat as a system namespace that should not be monitored. By default, all namespaces that match `global.cattle.systemProjectId` will not be matched. `kube-system` is explicitly marked as a system namespace as well, regardless of label or annotation. | +|`releaseRoleBindings.aggregate`| Whether to automatically create RBAC resources in Project Release namespaces +|`releaseRoleBindings.clusterRoleRefs.`| ClusterRoles to reference to discover subjects to create RoleBindings for in the Project Release Namespace for all corresponding Project Release Roles. See RBAC above for more information | +|`hardenedNamespaces.enabled`| Whether to automatically patch the default ServiceAccount with `automountServiceAccountToken: false` and create a default NetworkPolicy in all managed namespaces in the cluster; the default values ensure that the creation of the namespace does not break a CIS 1.16 hardened scan | +|`hardenedNamespaces.configuration`| The configuration to be supplied to the default ServiceAccount or auto-generated NetworkPolicy on managing a namespace | +|`helmController.enabled`| Whether to enable an embedded k3s-io/helm-controller instance within the Helm Project Operator. Should be disabled for RKE2 clusters since RKE2 clusters already run Helm Controller to manage internal Kubernetes components | +|`helmLocker.enabled`| Whether to enable an embedded rancher/helm-locker instance within the Helm Project Operator. | \ No newline at end of file diff --git a/docs/helm-project-operator/developing.md b/docs/helm-project-operator/developing.md new file mode 100644 index 00000000..7b9f3566 --- /dev/null +++ b/docs/helm-project-operator/developing.md @@ -0,0 +1,128 @@ +# Developing Helm Project Operator + +## Repository Structure + +```bash +## This directory contains a Helm chart that can be used to deploy Helm Project Operator in a Kubernetes cluster in the cattle-helm-system namespace, +## which deploys project-operator-example (located under charts/project-operator-example) on seeing a ProjectHelmChart with spec.helmApiVersion: dummy.cattle.io/v1alpha1. +charts/ + ## The main chart that deploys Helm Project Operator in the cluster. + helm-project-operator/ + + ## A dummy chart that is deployed onto the cluster on seeing a valid ProjectHelmChart (which means that it is contained within + ## a Project Registration Namespace with spec.helmApiVersion set to dummy.cattle.io/v1alpha1) + ## + ## This chart is not expected to ever be deployed standalone; it is embedded into the Helm Project Operator binary itself. + project-operator-example/ + +## This directory will contain additional docs to assist users in getting started with using Helm Project Operator. +docs/ + +## This directory contains example ProjectHelmCharts that can be deployed that work on the default project-operator-example packaged with the Helm Project Operator +examples/ + +## This directory contains the image that is used to build rancher/helm-project-operator, which is hosted on hub.docker.com. +package/ + Dockerfile-helm-project-operator + +## The main source directory for the code. See below for more details. +pkg/ + +## The Dockerfile-helm-project-operator used to run CI and other scripts executed by make in a Docker container (powered by https://github.com/rancher/dapper) +Dockerfile-helm-project-operator.dapper + +## The file that contains the underlying actions that 'go generate' needs to execute on a call to it. Includes the logic for generating +## controllers and updating the crds.yaml under the crds/ directory +generate.go + +## The main entrypoint into Helm Project Operator; this serves as an example of how Helm Project Operator can be used. +## For a real-world example, please see the main.go on https://github.com/rancher/prometheus-federator. +dummy.go +``` + +## Making changes to the codebase (`pkg`) + +Most of the code for Helm Locker is contained in the `pkg` directory, which has the following structure: + +```bash +## This directory contains the definition of a ProjectHelmChart CR under project.go; if you need to add new fields to ProjectHelmChart CRs, this is +## where you would make the change +apis/ + +## These directories manage all the logic around 'go generate', including the creation of the 'generated/' directory that contains all the underlying +## controllers that are auto-generated based on the API definition of the ProjectHelmChart CR defined under 'apis/' +codegen/ +crd/ +version/ +generated/ + +## This directory provides a utility function Init that allows projects implementing Helm Project Operator to quickly set up a Helm Project Operator +## instance based on provided options. +## +## For a real-world example of how this code is used, please see the main.go on https://github.com/rancher/prometheus-federator. +operator/ + +## These directories are the core controller directories that manage how the operator watches for Kubernetes resources +controllers/ + ## This directory is where all common code shared by all controllers is placed (e.g. options that can be provided, utility functions, constants, etc.) + common/ + ## This directory is where logic for hardening Helm Project Operated namespaces exists + hardened/ + ## This directory is where the logic for creating Project Registration Namespaces lives + namespace/ + ## This directory is where the logic for creating Project Release Namespaces and underlying Helm releases via HelmChart and HelmRelease CRs on seeing + ## changes to ProjectHelmCharts exist + project/ + ## This is where the underlying context used by all controllers of this operator are registered, all using the same underlying SharedControllerFactory + controller.go + ## This is where the logic for parsing the values.yaml and questions.yaml from an embedded Helm chart (provided as a .tgz.base64 in ChartContent) exists + parse.go +``` + +Within each of the directories under `pkg/controllers`, here are some important files: + +```bash +## Where the core controller logic and all OnChange, OnRemove, or GeneratingHandlers live +controller.go +## Where all indexes that need to be registered for this controller live; indexers are added in order to allow for the operator to efficiently +## query the cache for the latest state of an object instead of requiring the operator to make list API calls to the Kubernetes API server any time +## it needs to know the state of dependent resources (e.g. HelmCharts, HelmReleases) on re-enqueing the parent resource (namespace, ProjectHelmChart) +indexers.go +## Where custom reconcilers live which allow for the operator to modify how wrangler.apply performs the upgrade of a resource. For example, the current +## usage of this code is in order to all a reconciler that deletes and recreates ConfigMaps instead of attempting to patch the resource. +reconcilers.go +## Where resolvers live, which are handlers that are triggered on dependent resources being modified that signal to the operator that the main parent +## resource should be re-enqueued. Generally, you need at least one resolver per resource created in resource.go to ensure that changes to the underlying +## resources are resynced on modification. +resolvers.go +## Where the definition of resources that are deployed on behalf of a parent resource lives. +resources.go +``` + +## Once you have made a change + +If you modified `pkg/apis` or `generate.go`, make sure you run `go generate`. + +Also, make sure you run `go mod tidy`. + +## Creating a Docker image based off of your changes + +To test your changes and create a Docker image to a specific Docker repository with a given tag, you should run `REPO= TAG= make` (e.g. `REPO=arvindiyengar TAG=dev make`), which will run the `./scripts/ci` script that builds, tests, validates, and packages your changes into a local Docker image (if you run `docker images`, it should show up as an image in the format `${REPO}/helm-project-operator:${TAG}`). + +If you don't want to run all the steps in CI every time you make a change, you could also run the following one-liner to build and package the image: + +```bash +REPO= +TAG= + +./scripts/build-chart && GOOS=linux CGO_ENABLED=0 go build -ldflags "-extldflags -static -s" -o bin/helm-project-operator && REPO=${REPO} TAG=${TAG} make package +``` + +Once the image is successfully packaged, simply run `docker push ${REPO}/helm-project-operator:${TAG}` to push your image to your Docker repository. + +## Testing a custom Docker image build + +1. Ensure that your `KUBECONFIG` environment variable is pointing to your cluster (e.g. `export KUBECONFIG=; kubectl get nodes` should show the nodes of your cluster) and pull in this repository locally +2. Go to the root of your local copy of this repository and deploy the Helm Project Operator chart as a Helm 3 chart onto your cluster after overriding the image and tag values with your Docker repository and tag: run `helm upgrade --install --set image.repository="${REPO}/helm-project-operator" --set image.tag="${TAG}" --set image.pullPolicy=Always helm-project-operator -n cattle-helm-system charts/helm-project-operator` +> Note: Why do we set the Image Pull Policy to `Always`? If you update the Docker image on your fork, setting the Image Pull Policy to `Always` ensures that running `kubectl rollout restart -n cattle-helm-system deployment/helm-project-operator` is all you need to do to update your running deployment to the new image, since this would ensure redeploying a deployment triggers a image pull that uses your most up-to-date Docker image. Also, since the underlying Helm chart deployed by the operator (e.g. `project-operator-example`) is directly embedded into the Helm Project Operator image, you also do not need to update the Deployment object itself to see all the HelmCharts in your cluster automatically be updated to the latest embedded version of the chart. +3. Profit! \ No newline at end of file diff --git a/docs/helm-project-operator/experimental/e2e.md b/docs/helm-project-operator/experimental/e2e.md new file mode 100644 index 00000000..2e34f7a3 --- /dev/null +++ b/docs/helm-project-operator/experimental/e2e.md @@ -0,0 +1,67 @@ +# Experimental: E2E CI Tests + +## What does E2E CI do? + +The E2E CI described in [.github/scripts/](../../../.github/workflows/hpo-e2e-ci.yaml) checks out the current Git repository, builds a Docker image using the repository's build scripts, sets up a [k3d](https://k3d.io) cluster, imports the built `helm-project-operator` image into the cluster (which automatically uses the latest `project-operator-example` chart since it is embedded into the binary as part of the build process), and then uses Helm to install `helm-project-operator` (using the Helm chart contained in the repository). + +Once it is installed, it will run checks to ensure that all workloads are up and running in the Helm install and then mimic creating a Project (by creating a namespace with a particular label on it). + +On creating the Project, it asserts that the Registration Namespace is auto-created and installs the example ProjectHelmChart into that namespace, which triggers the deployment of the Example Chart in that namespace. + +Finally, it deletes the ProjectHelmChart, asserts the helm uninstall Job on the Example Chart successfully completes, and then performs a Helm uninstall of the Helm Project Operator chart to ensure that it is not left hanging. + +## Running the Github Workflow CI locally for testing + +To run the end-to-end GitHub Workflow CI locally to test whether your changes work, it's recommended to install [`nektos/act`](https://github.com/nektos/act). + +An slim image has been defined in [`.github/workflows/e2e/package/Dockerfile-helm-project-operator`](../../../.github/workflows/e2e/package/Dockerfile-helm-project-operator) that has the necessary dependencies to be used as a Runner for act for this GitHub Workflow. To build the image, run the following commmand (make sure you re-run it if you make any changes to add dependencies): + +```bash +docker build -f ./.github/workflows/e2e/package/Dockerfile-helm-project-operator -t rancher/helm-project-operator-e2e:latest . +``` + +Once you have built the image and installed `act`, simply run the following command on the root of this repository and it will run your GitHub workflow within a Docker container: + +```bash +act pull_request -j e2e-helm-project-operator -P ubuntu-latest=rancher/helm-project-operator-e2e:latest +``` + +> **Important Note**: When using local runs, `act` will create the k3d cluster locally in your system. It should automatically get deleted from your system at the end of a workflow run (failed or successful) at the end of CI, but if it does not execute make sure you clean it up manually via `k3d cluster delete e2e-ci-helm-project-operator`. + +## Running E2E Tests on an already provisioned cluster + +To verify that the functionality of Helm Project Operator on a live cluster that you have already configured your `KUBECONFIG` environment variable to point to, you can use the utility script found in [script/e2e-ci](../../../scripts/e2e-ci) to run the relevant CI commands to install Monitoring, install Helm Project Operator using your forked image, and run the remaining CI steps. + +> **Note:** For now, this script only works on k3s, RKE1, and RKE2 clusters but it can be easily extended to work on different cluster types by supplying the right values in `install-helm-project-operator.sh` to enable and verify the correct cluster-type specific testing. Contributions are welcome! + +However, to do this, your Helm Project Operator image will need to already be imported and accessible by the cluster you plan to run the scripts on, so make sure you push your image to a registry accessible by your cluster before running these scripts. + +For example, if you wanted to run your tests on the `arvindiyengar/helm-project-operator:dev` image, you would run the following command: + +```bash +KUBERNETES_DISTRIBUTION_TYPE= REPO=arvindiyengar TAG=dev ./scripts/e2e-ci +``` + +To enable debug logging, pass it in as an environment variable: + +```bash +DEBUG=true KUBERNETES_DISTRIBUTION_TYPE= REPO=arvindiyengar TAG=dev ./scripts/e2e-ci +``` + +If you are pointing at a Rancher 2.6+ downstream cluster, the `KUBERNETES_DISTRIBUTION_TYPE` will be auto-inferred from the `cluster.management.cattle.io` named `local` that exists in every downstream cluster, so it can be omitted: + +```bash +REPO=arvindiyengar TAG=dev ./scripts/e2e-ci +``` + +To skip uninstalling the Helm Project Operator chart (if you would like to perform some validations of your own after the fact), pass in `SKIP_UNINSTALL=true`: + +```bash +SKIP_UNINSTALL=true REPO=arvindiyengar TAG=dev ./scripts/e2e-ci +``` + +To run it against the latest image, just run: + +```bash +TAG= ./scripts/e2e-ci +``` diff --git a/docs/helm-project-operator/gettingstarted.md b/docs/helm-project-operator/gettingstarted.md new file mode 100644 index 00000000..9e7bb133 --- /dev/null +++ b/docs/helm-project-operator/gettingstarted.md @@ -0,0 +1,49 @@ +# Getting Started + +## Simple Installation + +### In Rancher (via Apps & Marketplace) + +1. Navigate to `Apps & Marketplace -> Repositories` in your target downstream cluster and create a Repository that points to a `Git repository containing Helm chart or cluster template definitions` where the `Git Repo URL` is `https://github.com/rancher/helm-project-operator` and the `Git Branch` is `main` +2. Navigate to `Apps & Marketplace -> Charts`; you should see a chart under the new Repository you created: `Helm Project Operator`. +3. Install `Helm Project Operator` + +### In a normal Kubernetes cluster (via running Helm 3 locally) + +Install `helm-project-operator` onto your cluster via Helm to install the Helm Project Operator + +``` +helm install -n cattle-helm-system helm-project-operator charts/helm-project-operator +``` + +### Checking if ProjectHelmCharts work + +1. Ensure that the logs of `helm-project-operator` in the `cattle-helm-system` namespace show that the controller was able to acquire a lock and has started in that namespace +2. Deploy a ProjectHelmChart into a Project Registration Namespace (see [design.md](design.md) for more information on how to identify this) +3. Check to see if a HelmChart CR was created on behalf of that ProjectHelmChart in the Operator / System (`cattle-helm-system`) namespace +4. Find the Job in the Operator / System (`cattle-helm-system`) namespace tied to the HelmChart object to view the Helm operation logs that were performed on behalf of the HelmChart resource created; these logs should show as successful. +5. Check to see if a HelmRelease CR was created on behalf of that ProjectHelmChart in the Operator / System (`cattle-helm-system`) namespace +6. Ensure that the status of the HelmRelease CR shows that it has successfully found the Helm release secret for the Helm chart deployed by the HelmChart CR. +7. Locate the Project Release Namespace (see [design.md](design.md) for more information on how to identify this) and ensure that the ConfigMaps contained within `charts/project-operator-example` were deployed onto the cluster. +8. Try to modify or delete the resources; you should see that they are instantly recreated or fixed back into place. +9. Try supplying overrides to the deployed Helm chart by modifying `spec.values` on the ProjectHelmChart resource the `data` value to any YAML you want; on supplying new YAML to the ProjectHelmChart, you should see the Helm Operator Job (deployed on behalf of the HelmChart resource) be modified and you should observe that the HelmRelease CR emits an event (observable by running `kubectl describe -n cattle-helm-system ` on the HelmRelease object) that indicates that it is Transitioning and then Locked; the release number will also be updated. + +## Uninstalling Helm Project Operator + +After deleting the Helm Charts, you may want to manually uninstall the CRDs from the cluster to clean them up: + +```bash +## Helm Project Operator CRDs +kubectl delete crds projecthelmcharts.helm.cattle.io + +## Helm Locker CRDs +kubectl delete crds helmreleases.helm.cattle.io + +## Helm Controller CRDs +## +## IMPORTANT NOTE: Do NOT delete if you are running in a k3s/RKE2 cluster since these CRDs are used to also manage internal k8s components +kubectl delete crds helmcharts.helm.cattle.io +kubectl delete crds helmchartconfigs.helm.cattle.io +``` + +> Note: Why aren't we packaging Helm Project Operator CRDs in a CRD chart? Since Helm Project Operator CRDs are shared across all Helm Project Operators (e.g. [rancher/prometheus-federator](https://github.com/rancher/prometheus-federator)), the ownership model of having a single CRD chart that manages installing, upgrading, and uninstalling Helm Project Operator CRDs isn't a good model for managing CRDs. Instead, it's left as an explicit action that the user should take in order to delete the Helm Project Operator CRDs from the cluster with caution that it could affect other deployments reliant on those CRDs. \ No newline at end of file From 8935f1570e267e359923731519250e997939e0d5 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 17:02:00 -0400 Subject: [PATCH 07/45] Add helm-locker source --- cmd/helm-locker/main.go | 69 ++++ .../apis/helm.cattle.io/v1alpha1/doc.go | 21 + .../apis/helm.cattle.io/v1alpha1/release.go | 59 +++ .../v1alpha1/zz_generated_deepcopy.go | 142 +++++++ .../v1alpha1/zz_generated_list_types.go | 42 ++ .../v1alpha1/zz_generated_register.go | 60 +++ .../helm.cattle.io/zz_generated_register.go | 24 ++ pkg/helm-locker/codegen/cleanup/main.go | 20 + pkg/helm-locker/codegen/main.go | 39 ++ pkg/helm-locker/controllers/controller.go | 177 +++++++++ .../controllers/release/controller.go | 238 +++++++++++ pkg/helm-locker/controllers/release/decode.go | 45 +++ pkg/helm-locker/controllers/release/info.go | 53 +++ pkg/helm-locker/controllers/release/utils.go | 43 ++ pkg/helm-locker/crd/crds.go | 104 +++++ .../controllers/helm.cattle.io/factory.go | 67 ++++ .../controllers/helm.cattle.io/interface.go | 43 ++ .../helm.cattle.io/v1alpha1/helmrelease.go | 376 ++++++++++++++++++ .../helm.cattle.io/v1alpha1/interface.go | 48 +++ pkg/helm-locker/gvk/gvk.go | 141 +++++++ pkg/helm-locker/gvk/lister.go | 44 ++ pkg/helm-locker/gvk/wrapper.go | 24 ++ pkg/helm-locker/informerfactory/wrapper.go | 29 ++ pkg/helm-locker/objectset/cache.go | 370 +++++++++++++++++ pkg/helm-locker/objectset/controller.go | 56 +++ pkg/helm-locker/objectset/handler.go | 103 +++++ pkg/helm-locker/objectset/parser/parse.go | 33 ++ pkg/helm-locker/objectset/state.go | 117 ++++++ pkg/helm-locker/objectset/utils.go | 13 + pkg/helm-locker/objectset/wrapper.go | 23 ++ pkg/helm-locker/releases/releases.go | 47 +++ 31 files changed, 2670 insertions(+) create mode 100644 cmd/helm-locker/main.go create mode 100644 pkg/helm-locker/apis/helm.cattle.io/v1alpha1/doc.go create mode 100644 pkg/helm-locker/apis/helm.cattle.io/v1alpha1/release.go create mode 100644 pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_deepcopy.go create mode 100644 pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_list_types.go create mode 100644 pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_register.go create mode 100644 pkg/helm-locker/apis/helm.cattle.io/zz_generated_register.go create mode 100644 pkg/helm-locker/codegen/cleanup/main.go create mode 100644 pkg/helm-locker/codegen/main.go create mode 100644 pkg/helm-locker/controllers/controller.go create mode 100644 pkg/helm-locker/controllers/release/controller.go create mode 100644 pkg/helm-locker/controllers/release/decode.go create mode 100644 pkg/helm-locker/controllers/release/info.go create mode 100644 pkg/helm-locker/controllers/release/utils.go create mode 100644 pkg/helm-locker/crd/crds.go create mode 100644 pkg/helm-locker/generated/controllers/helm.cattle.io/factory.go create mode 100644 pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go create mode 100644 pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go create mode 100644 pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go create mode 100644 pkg/helm-locker/gvk/gvk.go create mode 100644 pkg/helm-locker/gvk/lister.go create mode 100644 pkg/helm-locker/gvk/wrapper.go create mode 100644 pkg/helm-locker/informerfactory/wrapper.go create mode 100644 pkg/helm-locker/objectset/cache.go create mode 100644 pkg/helm-locker/objectset/controller.go create mode 100644 pkg/helm-locker/objectset/handler.go create mode 100644 pkg/helm-locker/objectset/parser/parse.go create mode 100644 pkg/helm-locker/objectset/state.go create mode 100644 pkg/helm-locker/objectset/utils.go create mode 100644 pkg/helm-locker/objectset/wrapper.go create mode 100644 pkg/helm-locker/releases/releases.go diff --git a/cmd/helm-locker/main.go b/cmd/helm-locker/main.go new file mode 100644 index 00000000..b2159c58 --- /dev/null +++ b/cmd/helm-locker/main.go @@ -0,0 +1,69 @@ +//go:build helm_locker + +package main + +import ( + "fmt" + "log" + "net/http" + _ "net/http/pprof" + + "github.com/rancher/helm-project-operator/pkg/helm-locker/controllers" + "github.com/rancher/helm-project-operator/pkg/helm-locker/crd" + "github.com/rancher/prometheus-federator/pkg/version" + command "github.com/rancher/wrangler-cli" + _ "github.com/rancher/wrangler/pkg/generated/controllers/apiextensions.k8s.io" + _ "github.com/rancher/wrangler/pkg/generated/controllers/networking.k8s.io" + "github.com/rancher/wrangler/pkg/kubeconfig" + "github.com/rancher/wrangler/pkg/ratelimit" + "github.com/spf13/cobra" +) + +var ( + debugConfig command.DebugConfig +) + +type HelmLocker struct { + Kubeconfig string `usage:"Kubeconfig file" env:"KUBECONFIG"` + Namespace string `usage:"Namespace to watch for HelmReleases" default:"cattle-helm-system" env:"NAMESPACE"` + ControllerName string `usage:"Unique name to identify this controller that is added to all HelmReleases tracked by this controller" default:"helm-locker" env:"CONTROLLER_NAME"` + NodeName string `usage:"Name of the node this controller is running on" env:"NODE_NAME"` +} + +func (a *HelmLocker) Run(cmd *cobra.Command, _ []string) error { + if len(a.Namespace) == 0 { + return fmt.Errorf("helm-locker can only be started in a single namespace") + } + + go func() { + log.Println(http.ListenAndServe("localhost:6060", nil)) + }() + debugConfig.MustSetupDebug() + + cfg := kubeconfig.GetNonInteractiveClientConfig(a.Kubeconfig) + clientConfig, err := cfg.ClientConfig() + if err != nil { + return err + } + clientConfig.RateLimiter = ratelimit.None + + ctx := cmd.Context() + if err := crd.Create(ctx, clientConfig); err != nil { + return err + } + + if err := controllers.Register(ctx, a.Namespace, a.ControllerName, a.NodeName, cfg); err != nil { + return err + } + + <-cmd.Context().Done() + return nil +} + +func main() { + cmd := command.Command(&HelmLocker{}, cobra.Command{ + Version: version.FriendlyVersion(), + }) + cmd = command.AddDebug(cmd, &debugConfig) + command.Main(cmd) +} diff --git a/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/doc.go b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/doc.go new file mode 100644 index 00000000..20da9d22 --- /dev/null +++ b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +// +k8s:deepcopy-gen=package +// +groupName=helm.cattle.io +package v1alpha1 diff --git a/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/release.go b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/release.go new file mode 100644 index 00000000..d72c4ac6 --- /dev/null +++ b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/release.go @@ -0,0 +1,59 @@ +package v1alpha1 + +import ( + "github.com/rancher/wrangler/pkg/genericcondition" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // Helm Release Statuses + + // SecretNotFoundState is the state when a Helm release secret has not been found for this HelmRelease + SecretNotFoundState = "SecretNotFound" + + // UnknownState is the state when the Helm release secret reports that it does not know the state of the underlying Helm release + UnknownState = "Unknown" + + // DeployedState is the state where the underlying Helm release has been successfully deployed, indicating Helm Locker should lock the release + DeployedState = "Deployed" + + // UninstalledState is the state when the underlying Helm release is uninstalled but the Helm release secret has not been deleted + UninstalledState = "Uninstalled" + + // ErrorState is a state where Helm Locker has encountered an unexpected bug on trying to parse the underlying Helm release + ErrorState = "Error" + + // FailedState is the state when the underlying Helm release has failed its last Helm operation + FailedState = "Failed" + + // TransitioningState is the transitionary state when a Helm operation is being performed on the release (install, upgrade, uninstall) + TransitioningState = "Transitioning" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type HelmRelease struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec HelmReleaseSpec `json:"spec"` + Status HelmReleaseStatus `json:"status"` +} + +type HelmReleaseSpec struct { + Release ReleaseKey `json:"release,omitempty"` +} + +type ReleaseKey struct { + Name string `json:"name,omitempty"` + Namespace string `json:"namespace,omitempty"` +} + +type HelmReleaseStatus struct { + State string `json:"state,omitempty"` + Version int `json:"version,omitempty"` + Description string `json:"description,omitempty"` + Notes string `json:"notes,omitempty"` + + Conditions []genericcondition.GenericCondition `json:"conditions,omitempty"` +} diff --git a/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_deepcopy.go b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_deepcopy.go new file mode 100644 index 00000000..fbf352eb --- /dev/null +++ b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_deepcopy.go @@ -0,0 +1,142 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package v1alpha1 + +import ( + genericcondition "github.com/rancher/wrangler/pkg/genericcondition" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmRelease) DeepCopyInto(out *HelmRelease) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmRelease. +func (in *HelmRelease) DeepCopy() *HelmRelease { + if in == nil { + return nil + } + out := new(HelmRelease) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HelmRelease) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmReleaseList) DeepCopyInto(out *HelmReleaseList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HelmRelease, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseList. +func (in *HelmReleaseList) DeepCopy() *HelmReleaseList { + if in == nil { + return nil + } + out := new(HelmReleaseList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HelmReleaseList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmReleaseSpec) DeepCopyInto(out *HelmReleaseSpec) { + *out = *in + out.Release = in.Release + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseSpec. +func (in *HelmReleaseSpec) DeepCopy() *HelmReleaseSpec { + if in == nil { + return nil + } + out := new(HelmReleaseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmReleaseStatus) DeepCopyInto(out *HelmReleaseStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]genericcondition.GenericCondition, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmReleaseStatus. +func (in *HelmReleaseStatus) DeepCopy() *HelmReleaseStatus { + if in == nil { + return nil + } + out := new(HelmReleaseStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReleaseKey) DeepCopyInto(out *ReleaseKey) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReleaseKey. +func (in *ReleaseKey) DeepCopy() *ReleaseKey { + if in == nil { + return nil + } + out := new(ReleaseKey) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_list_types.go b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_list_types.go new file mode 100644 index 00000000..135106ac --- /dev/null +++ b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_list_types.go @@ -0,0 +1,42 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +// +k8s:deepcopy-gen=package +// +groupName=helm.cattle.io +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// HelmReleaseList is a list of HelmRelease resources +type HelmReleaseList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []HelmRelease `json:"items"` +} + +func NewHelmRelease(namespace, name string, obj HelmRelease) *HelmRelease { + obj.APIVersion, obj.Kind = SchemeGroupVersion.WithKind("HelmRelease").ToAPIVersionAndKind() + obj.Name = name + obj.Namespace = namespace + return &obj +} diff --git a/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_register.go b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_register.go new file mode 100644 index 00000000..0597f360 --- /dev/null +++ b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_register.go @@ -0,0 +1,60 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +// +k8s:deepcopy-gen=package +// +groupName=helm.cattle.io +package v1alpha1 + +import ( + helm "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + HelmReleaseResourceName = "helmreleases" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: helm.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &HelmRelease{}, + &HelmReleaseList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/helm-locker/apis/helm.cattle.io/zz_generated_register.go b/pkg/helm-locker/apis/helm.cattle.io/zz_generated_register.go new file mode 100644 index 00000000..11c331c8 --- /dev/null +++ b/pkg/helm-locker/apis/helm.cattle.io/zz_generated_register.go @@ -0,0 +1,24 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package helm + +const ( + // Package-wide consts from generator "zz_generated_register". + GroupName = "helm.cattle.io" +) diff --git a/pkg/helm-locker/codegen/cleanup/main.go b/pkg/helm-locker/codegen/cleanup/main.go new file mode 100644 index 00000000..82b688d0 --- /dev/null +++ b/pkg/helm-locker/codegen/cleanup/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "os" + + "github.com/rancher/wrangler/pkg/cleanup" + "github.com/sirupsen/logrus" +) + +func main() { + if err := cleanup.Cleanup("./pkg/helm-locker/apis"); err != nil { + logrus.Fatal(err) + } + if err := os.RemoveAll("./pkg/helm-locker/generated"); err != nil { + logrus.Fatal(err) + } + if err := os.RemoveAll("./crds/helm-locker"); err != nil { + logrus.Fatal(err) + } +} diff --git a/pkg/helm-locker/codegen/main.go b/pkg/helm-locker/codegen/main.go new file mode 100644 index 00000000..cad6df0d --- /dev/null +++ b/pkg/helm-locker/codegen/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "os" + + v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/helm-project-operator/pkg/helm-locker/crd" + "github.com/sirupsen/logrus" + + controllergen "github.com/rancher/wrangler/pkg/controller-gen" + "github.com/rancher/wrangler/pkg/controller-gen/args" +) + +func main() { + if len(os.Args) > 2 && os.Args[1] == "crds" { + if len(os.Args) != 3 { + logrus.Fatal("usage: ./codegen crds ") + } + logrus.Infof("Writing CRDs to %s", os.Args[2]) + if err := crd.WriteFile(os.Args[2]); err != nil { + panic(err) + } + return + } + + os.Unsetenv("GOPATH") + controllergen.Run(args.Options{ + OutputPackage: "github.com/rancher/helm-project-operator/pkg/helm-locker/generated", + Boilerplate: "gen/boilerplate.go.txt", + Groups: map[string]args.Group{ + "helm.cattle.io": { + Types: []interface{}{ + v1alpha1.HelmRelease{}, + }, + GenerateTypes: true, + }, + }, + }) +} diff --git a/pkg/helm-locker/controllers/controller.go b/pkg/helm-locker/controllers/controller.go new file mode 100644 index 00000000..ea1b4105 --- /dev/null +++ b/pkg/helm-locker/controllers/controller.go @@ -0,0 +1,177 @@ +package controllers + +import ( + "context" + "errors" + "time" + + "github.com/rancher/helm-project-operator/pkg/helm-locker/controllers/release" + "github.com/rancher/helm-project-operator/pkg/helm-locker/generated/controllers/helm.cattle.io" + helmcontroller "github.com/rancher/helm-project-operator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" + "github.com/rancher/helm-project-operator/pkg/helm-locker/objectset" + "github.com/rancher/lasso/pkg/cache" + "github.com/rancher/lasso/pkg/client" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/apply" + "github.com/rancher/wrangler/pkg/generated/controllers/core" + corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" + "github.com/rancher/wrangler/pkg/generic" + "github.com/rancher/wrangler/pkg/leader" + "github.com/rancher/wrangler/pkg/ratelimit" + "github.com/rancher/wrangler/pkg/schemes" + "github.com/rancher/wrangler/pkg/start" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" + typedv1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" +) + +type appContext struct { + helmcontroller.Interface + + K8s kubernetes.Interface + Core corecontroller.Interface + + Apply apply.Apply + + ObjectSetRegister objectset.LockableRegister + ObjectSetHandler *controller.SharedHandler + + EventBroadcaster record.EventBroadcaster + + starters []start.Starter +} + +func (a *appContext) start(ctx context.Context) error { + return start.All(ctx, 50, a.starters...) +} + +func Register(ctx context.Context, systemNamespace, controllerName, nodeName string, cfg clientcmd.ClientConfig) error { + if len(systemNamespace) == 0 { + return errors.New("cannot start controllers on system namespace: system namespace not provided") + } + + appCtx, err := newContext(ctx, systemNamespace, cfg) + if err != nil { + return err + } + + appCtx.EventBroadcaster.StartLogging(logrus.Debugf) + appCtx.EventBroadcaster.StartRecordingToSink(&typedv1.EventSinkImpl{ + Interface: appCtx.K8s.CoreV1().Events(systemNamespace), + }) + recorder := appCtx.EventBroadcaster.NewRecorder(schemes.All, corev1.EventSource{ + Component: "helm-locker", + Host: nodeName, + }) + + if len(controllerName) == 0 { + controllerName = "helm-locker" + } + + // TODO: Register all controllers + release.Register(ctx, + systemNamespace, + controllerName, + appCtx.HelmRelease(), + appCtx.HelmRelease().Cache(), + appCtx.Core.Secret(), + appCtx.Core.Secret().Cache(), + appCtx.K8s, + appCtx.ObjectSetRegister, + appCtx.ObjectSetHandler, + recorder, + ) + + leader.RunOrDie(ctx, systemNamespace, "helm-locker-lock", appCtx.K8s, func(ctx context.Context) { + if err := appCtx.start(ctx); err != nil { + logrus.Fatal(err) + } + logrus.Info("All controllers have been started") + }) + + return nil +} + +func controllerFactory(rest *rest.Config) (controller.SharedControllerFactory, error) { + rateLimit := workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 60*time.Second) + clientFactory, err := client.NewSharedClientFactory(rest, nil) + if err != nil { + return nil, err + } + + cacheFactory := cache.NewSharedCachedFactory(clientFactory, nil) + return controller.NewSharedControllerFactory(cacheFactory, &controller.SharedControllerFactoryOptions{ + DefaultRateLimiter: rateLimit, + DefaultWorkers: 50, + }), nil +} + +func newContext(_ context.Context, systemNamespace string, cfg clientcmd.ClientConfig) (*appContext, error) { + client, err := cfg.ClientConfig() + if err != nil { + return nil, err + } + client.RateLimiter = ratelimit.None + + k8s, err := kubernetes.NewForConfig(client) + if err != nil { + return nil, err + } + + discovery, err := discovery.NewDiscoveryClientForConfig(client) + if err != nil { + return nil, err + } + + scf, err := controllerFactory(client) + if err != nil { + return nil, err + } + + core, err := core.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + SharedControllerFactory: scf, + }) + if err != nil { + return nil, err + } + corev := core.Core().V1() + + helm, err := helm.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + Namespace: systemNamespace, + SharedControllerFactory: scf, + }) + if err != nil { + return nil, err + } + helmv := helm.Helm().V1alpha1() + + apply := apply.New(discovery, apply.NewClientFactory(client)) + + objectSet, objectSetRegister, objectSetHandler := objectset.NewLockableRegister("object-set-register", apply, scf, discovery, nil) + + return &appContext{ + Interface: helmv, + + K8s: k8s, + Core: corev, + + Apply: apply, + + ObjectSetRegister: objectSetRegister, + ObjectSetHandler: objectSetHandler, + + EventBroadcaster: record.NewBroadcaster(), + + starters: []start.Starter{ + objectSet, + core, + helm, + }, + }, nil +} diff --git a/pkg/helm-locker/controllers/release/controller.go b/pkg/helm-locker/controllers/release/controller.go new file mode 100644 index 00000000..5fde8539 --- /dev/null +++ b/pkg/helm-locker/controllers/release/controller.go @@ -0,0 +1,238 @@ +package release + +import ( + "context" + "fmt" + + v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + helmcontroller "github.com/rancher/helm-project-operator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" + "github.com/rancher/helm-project-operator/pkg/helm-locker/objectset" + "github.com/rancher/helm-project-operator/pkg/helm-locker/objectset/parser" + "github.com/rancher/helm-project-operator/pkg/helm-locker/releases" + "github.com/rancher/helm-project-operator/pkg/remove" + "github.com/rancher/lasso/pkg/controller" + corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" + "github.com/rancher/wrangler/pkg/relatedresource" + "github.com/sirupsen/logrus" + "helm.sh/helm/v3/pkg/storage/driver" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/record" +) + +const ( + // HelmReleaseByReleaseKey is the key used to get HelmRelease objects by the namespace/name of the underlying Helm Release it points to + HelmReleaseByReleaseKey = "helm.cattle.io/helm-release-by-release-key" + + // ManagedBy is an annotation attached to HelmRelease objects that indicates that they are managed by this operator + ManagedBy = "helmreleases.cattle.io/managed-by" +) + +type handler struct { + systemNamespace string + managedBy string + + helmReleases helmcontroller.HelmReleaseController + helmReleaseCache helmcontroller.HelmReleaseCache + secrets corecontroller.SecretController + secretCache corecontroller.SecretCache + + releases releases.HelmReleaseGetter + + lockableObjectSetRegister objectset.LockableRegister + recorder record.EventRecorder +} + +func Register( + ctx context.Context, + systemNamespace, managedBy string, + helmReleases helmcontroller.HelmReleaseController, + helmReleaseCache helmcontroller.HelmReleaseCache, + secrets corecontroller.SecretController, + secretCache corecontroller.SecretCache, + k8s kubernetes.Interface, + lockableObjectSetRegister objectset.LockableRegister, + lockableObjectSetHandler *controller.SharedHandler, + recorder record.EventRecorder, +) { + + h := &handler{ + systemNamespace: systemNamespace, + managedBy: managedBy, + + helmReleases: helmReleases, + helmReleaseCache: helmReleaseCache, + secrets: secrets, + secretCache: secretCache, + + releases: releases.NewHelmReleaseGetter(k8s), + + lockableObjectSetRegister: lockableObjectSetRegister, + recorder: recorder, + } + + lockableObjectSetHandler.Register(ctx, "on-objectset-change", controller.SharedControllerHandlerFunc(h.OnObjectSetChange)) + + helmReleaseCache.AddIndexer(HelmReleaseByReleaseKey, helmReleaseToReleaseKey) + + relatedresource.Watch(ctx, "on-helm-secret-change", h.resolveHelmRelease, helmReleases, secrets) + + helmReleases.OnChange(ctx, "apply-lock-on-release", h.OnHelmRelease) + + remove.RegisterScopedOnRemoveHandler(ctx, helmReleases, "on-helm-release-remove", + func(_ string, obj runtime.Object) (bool, error) { + if obj == nil { + return false, nil + } + helmRelease, ok := obj.(*v1alpha1.HelmRelease) + if !ok { + return false, nil + } + return h.shouldManage(helmRelease) + }, + helmcontroller.FromHelmReleaseHandlerToHandler(h.OnHelmReleaseRemove), + ) +} + +func (h *handler) OnObjectSetChange(setID string, obj runtime.Object) (runtime.Object, error) { + helmReleases, err := h.helmReleaseCache.GetByIndex(HelmReleaseByReleaseKey, setID) + if err != nil { + return nil, fmt.Errorf("unable to find HelmReleases for objectset %s to trigger event", setID) + } + for _, helmRelease := range helmReleases { + if helmRelease == nil { + continue + } + if obj != nil { + h.recorder.Eventf(helmRelease, corev1.EventTypeNormal, "Locked", "Applied ObjectSet %s tied to HelmRelease %s/%s to lock into place", setID, helmRelease.Namespace, helmRelease.Name) + } else { + h.recorder.Eventf(helmRelease, corev1.EventTypeNormal, "Untracked", "ObjectSet %s tied to HelmRelease %s/%s is not tracked", setID, helmRelease.Namespace, helmRelease.Name) + } + } + return nil, nil +} + +func helmReleaseToReleaseKey(helmRelease *v1alpha1.HelmRelease) ([]string, error) { + releaseKey := releaseKeyFromRelease(helmRelease) + return []string{releaseKeyToString(releaseKey)}, nil +} + +func (h *handler) resolveHelmRelease(_ /* secretNamespace */, _ /* secretName */ string, obj runtime.Object) ([]relatedresource.Key, error) { + secret, ok := obj.(*corev1.Secret) + if !ok { + return nil, nil + } + releaseKey := releaseKeyFromSecret(secret) + if releaseKey == nil { + // No release found matching this secret + return nil, nil + } + helmReleases, err := h.helmReleaseCache.GetByIndex(HelmReleaseByReleaseKey, releaseKeyToString(*releaseKey)) + if err != nil { + return nil, err + } + + keys := make([]relatedresource.Key, len(helmReleases)) + for i, helmRelease := range helmReleases { + keys[i] = relatedresource.Key{ + Name: helmRelease.Name, + Namespace: helmRelease.Namespace, + } + } + + return keys, nil +} + +// shouldManage determines if this HelmRelease should be handled by this operator +func (h *handler) shouldManage(helmRelease *v1alpha1.HelmRelease) (bool, error) { + if helmRelease == nil { + return false, nil + } + if helmRelease.Namespace != h.systemNamespace { + return false, nil + } + if helmRelease.Annotations != nil { + managedBy, ok := helmRelease.Annotations[ManagedBy] + if ok { + // if the label exists, only handle this if the managedBy label matches that of this controller + return managedBy == h.managedBy, nil + } + } + // The managedBy label does not exist, so we trigger claiming the HelmRelease + // We then return false since this update will automatically retrigger an OnChange operation + helmReleaseCopy := helmRelease.DeepCopy() + if helmReleaseCopy.Annotations == nil { + helmReleaseCopy.SetAnnotations(map[string]string{ + ManagedBy: h.managedBy, + }) + } else { + helmReleaseCopy.Annotations[ManagedBy] = h.managedBy + } + _, err := h.helmReleases.Update(helmReleaseCopy) + return false, err +} + +func (h *handler) OnHelmReleaseRemove(_ string, helmRelease *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) { + if helmRelease == nil { + return nil, nil + } + if helmRelease.Status.State == v1alpha1.SecretNotFoundState || helmRelease.Status.State == v1alpha1.UninstalledState { + // HelmRelease was not tracking any underlying objectSet + return helmRelease, nil + } + // HelmRelease CRs are only pointers to Helm releases... if the HelmRelease CR is removed, we should do nothing, but should warn the user + // that we are leaving behind resources in the cluster + logrus.Warnf("HelmRelease %s/%s was removed, resources tied to Helm release may need to be manually deleted", helmRelease.Namespace, helmRelease.Name) + logrus.Warnf("To delete the contents of a Helm release automatically, delete the Helm release secret before deleting the HelmRelease.") + releaseKey := releaseKeyFromRelease(helmRelease) + h.lockableObjectSetRegister.Delete(releaseKey, false) // remove the objectset, but don't purge the underlying resources + return helmRelease, nil +} + +func (h *handler) OnHelmRelease(_ string, helmRelease *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) { + if shouldManage, err := h.shouldManage(helmRelease); err != nil { + return helmRelease, err + } else if !shouldManage { + return helmRelease, nil + } + if helmRelease.DeletionTimestamp != nil { + return helmRelease, nil + } + releaseKey := releaseKeyFromRelease(helmRelease) + latestRelease, err := h.releases.Last(releaseKey.Namespace, releaseKey.Name) + if err != nil { + if err == driver.ErrReleaseNotFound { + logrus.Warnf("waiting for release %s/%s to be found to reconcile HelmRelease %s, deleting any orphaned resources", releaseKey.Namespace, releaseKey.Name, helmRelease.GetName()) + h.lockableObjectSetRegister.Delete(releaseKey, true) // remove the objectset and purge any untracked resources + helmRelease.Status.Version = 0 + helmRelease.Status.Description = "Could not find Helm Release Secret" + helmRelease.Status.State = v1alpha1.SecretNotFoundState + helmRelease.Status.Notes = "" + return h.helmReleases.UpdateStatus(helmRelease) + } + return helmRelease, fmt.Errorf("unable to find latest Helm Release Secret tied to Helm Release %s: %s", helmRelease.GetName(), err) + } + logrus.Infof("loading latest release version %d of HelmRelease %s", latestRelease.Version, helmRelease.GetName()) + releaseInfo := newReleaseInfo(latestRelease) + helmRelease, err = h.helmReleases.UpdateStatus(releaseInfo.GetUpdatedStatus(helmRelease)) + if err != nil { + return helmRelease, fmt.Errorf("unable to update status of HelmRelease %s: %s", helmRelease.GetName(), err) + } + if !releaseInfo.Locked() { + // TODO: add status + logrus.Infof("detected HelmRelease %s is not deployed or transitioning (state is %s), unlocking release", helmRelease.GetName(), releaseInfo.State) + h.lockableObjectSetRegister.Unlock(releaseKey) + h.recorder.Eventf(helmRelease, corev1.EventTypeNormal, "Transitioning", "Unlocked HelmRelease %s/%s to allow changes while Helm operation is being executed", helmRelease.Namespace, helmRelease.Name) + return helmRelease, nil + } + manifestOS, err := parser.Parse(releaseInfo.Manifest) + if err != nil { + // TODO: add status + return helmRelease, fmt.Errorf("unable to parse objectset from manifest for HelmRelease %s: %s", helmRelease.GetName(), err) + } + logrus.Infof("detected HelmRelease %s is deployed, locking release %s with %d objects", helmRelease.GetName(), releaseKey, len(manifestOS.All())) + locked := true + h.lockableObjectSetRegister.Set(releaseKey, manifestOS, &locked) + return helmRelease, nil +} diff --git a/pkg/helm-locker/controllers/release/decode.go b/pkg/helm-locker/controllers/release/decode.go new file mode 100644 index 00000000..c3f61245 --- /dev/null +++ b/pkg/helm-locker/controllers/release/decode.go @@ -0,0 +1,45 @@ +package release + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "encoding/json" + "io" + + rspb "helm.sh/helm/v3/pkg/release" +) + +// decodeRelease decodes the bytes of data into a release +// type. Data must contain a base64 encoded gzipped string of a +// valid release, otherwise an error is returned. +func decodeRelease(data string) (*rspb.Release, error) { + // base64 decode string + b, err := base64.StdEncoding.DecodeString(data) + if err != nil { + return nil, err + } + + // For backwards compatibility with releases that were stored before + // compression was introduced we skip decompression if the + // gzip magic header is not found + if bytes.Equal(b[0:3], []byte{0x1f, 0x8b, 0x08}) { + r, err := gzip.NewReader(bytes.NewReader(b)) + if err != nil { + return nil, err + } + defer r.Close() + b2, err := io.ReadAll(r) + if err != nil { + return nil, err + } + b = b2 + } + + var rls rspb.Release + // unmarshal release object bytes + if err := json.Unmarshal(b, &rls); err != nil { + return nil, err + } + return &rls, nil +} diff --git a/pkg/helm-locker/controllers/release/info.go b/pkg/helm-locker/controllers/release/info.go new file mode 100644 index 00000000..02a2e8a2 --- /dev/null +++ b/pkg/helm-locker/controllers/release/info.go @@ -0,0 +1,53 @@ +package release + +import ( + v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + rspb "helm.sh/helm/v3/pkg/release" +) + +func newReleaseInfo(release *rspb.Release) *releaseInfo { + info := &releaseInfo{} + info.Version = int(release.Version) + info.Manifest = release.Manifest + if release.Info != nil { + info.Description = release.Info.Description + info.Notes = release.Info.Notes + switch release.Info.Status { + case rspb.StatusUnknown: + info.State = v1alpha1.UnknownState + case rspb.StatusDeployed: + info.State = v1alpha1.DeployedState + case rspb.StatusUninstalled: + info.State = v1alpha1.UninstalledState + case rspb.StatusSuperseded: + // note: this should never be the case since we always get the latest secret + info.State = v1alpha1.ErrorState + case rspb.StatusFailed: + info.State = v1alpha1.FailedState + default: + // uninstalling, pending install, pending upgrade, pending rollback + info.State = v1alpha1.TransitioningState + } + } + return info +} + +type releaseInfo struct { + Version int + Manifest string + Description string + Notes string + State string +} + +func (i *releaseInfo) Locked() bool { + return i.State == v1alpha1.DeployedState +} + +func (i *releaseInfo) GetUpdatedStatus(helmRelease *v1alpha1.HelmRelease) *v1alpha1.HelmRelease { + helmRelease.Status.Version = i.Version + helmRelease.Status.Description = i.Description + helmRelease.Status.State = i.State + helmRelease.Status.Notes = i.Notes + return helmRelease +} diff --git a/pkg/helm-locker/controllers/release/utils.go b/pkg/helm-locker/controllers/release/utils.go new file mode 100644 index 00000000..b6b3077e --- /dev/null +++ b/pkg/helm-locker/controllers/release/utils.go @@ -0,0 +1,43 @@ +package release + +import ( + "fmt" + + v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/wrangler/pkg/relatedresource" + corev1 "k8s.io/api/core/v1" +) + +const ( + // HelmReleaseSecretType is the type of a secret that is considered a Helm Release secret + HelmReleaseSecretType = "helm.sh/release.v1" +) + +func releaseKeyToString(key relatedresource.Key) string { + return fmt.Sprintf("%s/%s", key.Namespace, key.Name) +} + +func releaseKeyFromRelease(release *v1alpha1.HelmRelease) relatedresource.Key { + return relatedresource.Key{ + Namespace: release.Spec.Release.Namespace, + Name: release.Spec.Release.Name, + } +} + +func releaseKeyFromSecret(secret *corev1.Secret) *relatedresource.Key { + if !isHelmReleaseSecret(secret) { + return nil + } + releaseNameFromLabel, ok := secret.GetLabels()["name"] + if !ok { + return nil + } + return &relatedresource.Key{ + Namespace: secret.GetNamespace(), + Name: releaseNameFromLabel, + } +} + +func isHelmReleaseSecret(secret *corev1.Secret) bool { + return secret.Type == HelmReleaseSecretType +} diff --git a/pkg/helm-locker/crd/crds.go b/pkg/helm-locker/crd/crds.go new file mode 100644 index 00000000..2d8a11a8 --- /dev/null +++ b/pkg/helm-locker/crd/crds.go @@ -0,0 +1,104 @@ +package crd + +import ( + "context" + "io" + "os" + "path/filepath" + + v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/wrangler/pkg/crd" + "github.com/rancher/wrangler/pkg/yaml" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/rest" +) + +// WriteFile writes CRDs to the path specified +func WriteFile(filename string) error { + if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { + return err + } + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + return Print(f) +} + +// Print prints CRDs to out +func Print(out io.Writer) error { + obj, err := Objects(false) + if err != nil { + return err + } + data, err := yaml.Export(obj...) + if err != nil { + return err + } + + _, err = out.Write(data) + return err +} + +// Objects returns runtime.Objects for every CRD +func Objects(v1beta1 bool) (result []runtime.Object, err error) { + for _, crdDef := range List() { + if v1beta1 { + crd, err := crdDef.ToCustomResourceDefinitionV1Beta1() + if err != nil { + return nil, err + } + result = append(result, crd) + } else { + crd, err := crdDef.ToCustomResourceDefinition() + if err != nil { + return nil, err + } + result = append(result, crd) + } + } + return +} + +// List returns the set of CRDs that need to be generated +func List() []crd.CRD { + return []crd.CRD{ + newCRD(&v1alpha1.HelmRelease{}, func(c crd.CRD) crd.CRD { + return c. + WithColumn("Release Name", ".spec.release.name"). + WithColumn("Release Namespace", ".spec.release.namespace"). + WithColumn("Version", ".status.version"). + WithColumn("State", ".status.state") + }), + } +} + +// Create creates the necessary CRDs on starting this program onto the target cluster +func Create(ctx context.Context, cfg *rest.Config) error { + factory, err := crd.NewFactoryFromClient(cfg) + if err != nil { + return err + } + + return factory.BatchCreateCRDs(ctx, List()...).BatchWait() +} + +// newCRD returns the CustomResourceDefinition of an object that is customized +// according to the provided customize function +func newCRD(obj interface{}, customize func(crd.CRD) crd.CRD) crd.CRD { + crd := crd.CRD{ + GVK: schema.GroupVersionKind{ + Group: "helm.cattle.io", + Version: "v1alpha1", + }, + Status: true, + SchemaObject: obj, + } + if customize != nil { + crd = customize(crd) + } + return crd +} diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/factory.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/factory.go new file mode 100644 index 00000000..38c58cd4 --- /dev/null +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/factory.go @@ -0,0 +1,67 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package helm + +import ( + "github.com/rancher/wrangler/pkg/generic" + "k8s.io/client-go/rest" +) + +type Factory struct { + *generic.Factory +} + +func NewFactoryFromConfigOrDie(config *rest.Config) *Factory { + f, err := NewFactoryFromConfig(config) + if err != nil { + panic(err) + } + return f +} + +func NewFactoryFromConfig(config *rest.Config) (*Factory, error) { + return NewFactoryFromConfigWithOptions(config, nil) +} + +func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) { + return NewFactoryFromConfigWithOptions(config, &FactoryOptions{ + Namespace: namespace, + }) +} + +type FactoryOptions = generic.FactoryOptions + +func NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) { + f, err := generic.NewFactoryFromConfigWithOptions(config, opts) + return &Factory{ + Factory: f, + }, err +} + +func NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory { + f, err := NewFactoryFromConfigWithOptions(config, opts) + if err != nil { + panic(err) + } + return f +} + +func (c *Factory) Helm() Interface { + return New(c.ControllerFactory()) +} diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go new file mode 100644 index 00000000..0ca3c85a --- /dev/null +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go @@ -0,0 +1,43 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package helm + +import ( + v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" + "github.com/rancher/lasso/pkg/controller" +) + +type Interface interface { + V1alpha1() v1alpha1.Interface +} + +type group struct { + controllerFactory controller.SharedControllerFactory +} + +// New returns a new Interface. +func New(controllerFactory controller.SharedControllerFactory) Interface { + return &group{ + controllerFactory: controllerFactory, + } +} + +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.controllerFactory) +} diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go new file mode 100644 index 00000000..307da414 --- /dev/null +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go @@ -0,0 +1,376 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/lasso/pkg/client" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/apply" + "github.com/rancher/wrangler/pkg/condition" + "github.com/rancher/wrangler/pkg/generic" + "github.com/rancher/wrangler/pkg/kv" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" +) + +type HelmReleaseHandler func(string, *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) + +type HelmReleaseController interface { + generic.ControllerMeta + HelmReleaseClient + + OnChange(ctx context.Context, name string, sync HelmReleaseHandler) + OnRemove(ctx context.Context, name string, sync HelmReleaseHandler) + Enqueue(namespace, name string) + EnqueueAfter(namespace, name string, duration time.Duration) + + Cache() HelmReleaseCache +} + +type HelmReleaseClient interface { + Create(*v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) + Update(*v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) + UpdateStatus(*v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) + Delete(namespace, name string, options *metav1.DeleteOptions) error + Get(namespace, name string, options metav1.GetOptions) (*v1alpha1.HelmRelease, error) + List(namespace string, opts metav1.ListOptions) (*v1alpha1.HelmReleaseList, error) + Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error) + Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.HelmRelease, err error) +} + +type HelmReleaseCache interface { + Get(namespace, name string) (*v1alpha1.HelmRelease, error) + List(namespace string, selector labels.Selector) ([]*v1alpha1.HelmRelease, error) + + AddIndexer(indexName string, indexer HelmReleaseIndexer) + GetByIndex(indexName, key string) ([]*v1alpha1.HelmRelease, error) +} + +type HelmReleaseIndexer func(obj *v1alpha1.HelmRelease) ([]string, error) + +type helmReleaseController struct { + controller controller.SharedController + client *client.Client + gvk schema.GroupVersionKind + groupResource schema.GroupResource +} + +func NewHelmReleaseController(gvk schema.GroupVersionKind, resource string, namespaced bool, controller controller.SharedControllerFactory) HelmReleaseController { + c := controller.ForResourceKind(gvk.GroupVersion().WithResource(resource), gvk.Kind, namespaced) + return &helmReleaseController{ + controller: c, + client: c.Client(), + gvk: gvk, + groupResource: schema.GroupResource{ + Group: gvk.Group, + Resource: resource, + }, + } +} + +func FromHelmReleaseHandlerToHandler(sync HelmReleaseHandler) generic.Handler { + return func(key string, obj runtime.Object) (ret runtime.Object, err error) { + var v *v1alpha1.HelmRelease + if obj == nil { + v, err = sync(key, nil) + } else { + v, err = sync(key, obj.(*v1alpha1.HelmRelease)) + } + if v == nil { + return nil, err + } + return v, err + } +} + +func (c *helmReleaseController) Updater() generic.Updater { + return func(obj runtime.Object) (runtime.Object, error) { + newObj, err := c.Update(obj.(*v1alpha1.HelmRelease)) + if newObj == nil { + return nil, err + } + return newObj, err + } +} + +func UpdateHelmReleaseDeepCopyOnChange(client HelmReleaseClient, obj *v1alpha1.HelmRelease, handler func(obj *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error)) (*v1alpha1.HelmRelease, error) { + if obj == nil { + return obj, nil + } + + copyObj := obj.DeepCopy() + newObj, err := handler(copyObj) + if newObj != nil { + copyObj = newObj + } + if obj.ResourceVersion == copyObj.ResourceVersion && !equality.Semantic.DeepEqual(obj, copyObj) { + return client.Update(copyObj) + } + + return copyObj, err +} + +func (c *helmReleaseController) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) { + c.controller.RegisterHandler(ctx, name, controller.SharedControllerHandlerFunc(handler)) +} + +func (c *helmReleaseController) AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler) { + c.AddGenericHandler(ctx, name, generic.NewRemoveHandler(name, c.Updater(), handler)) +} + +func (c *helmReleaseController) OnChange(ctx context.Context, name string, sync HelmReleaseHandler) { + c.AddGenericHandler(ctx, name, FromHelmReleaseHandlerToHandler(sync)) +} + +func (c *helmReleaseController) OnRemove(ctx context.Context, name string, sync HelmReleaseHandler) { + c.AddGenericHandler(ctx, name, generic.NewRemoveHandler(name, c.Updater(), FromHelmReleaseHandlerToHandler(sync))) +} + +func (c *helmReleaseController) Enqueue(namespace, name string) { + c.controller.Enqueue(namespace, name) +} + +func (c *helmReleaseController) EnqueueAfter(namespace, name string, duration time.Duration) { + c.controller.EnqueueAfter(namespace, name, duration) +} + +func (c *helmReleaseController) Informer() cache.SharedIndexInformer { + return c.controller.Informer() +} + +func (c *helmReleaseController) GroupVersionKind() schema.GroupVersionKind { + return c.gvk +} + +func (c *helmReleaseController) Cache() HelmReleaseCache { + return &helmReleaseCache{ + indexer: c.Informer().GetIndexer(), + resource: c.groupResource, + } +} + +func (c *helmReleaseController) Create(obj *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) { + result := &v1alpha1.HelmRelease{} + return result, c.client.Create(context.TODO(), obj.Namespace, obj, result, metav1.CreateOptions{}) +} + +func (c *helmReleaseController) Update(obj *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) { + result := &v1alpha1.HelmRelease{} + return result, c.client.Update(context.TODO(), obj.Namespace, obj, result, metav1.UpdateOptions{}) +} + +func (c *helmReleaseController) UpdateStatus(obj *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) { + result := &v1alpha1.HelmRelease{} + return result, c.client.UpdateStatus(context.TODO(), obj.Namespace, obj, result, metav1.UpdateOptions{}) +} + +func (c *helmReleaseController) Delete(namespace, name string, options *metav1.DeleteOptions) error { + if options == nil { + options = &metav1.DeleteOptions{} + } + return c.client.Delete(context.TODO(), namespace, name, *options) +} + +func (c *helmReleaseController) Get(namespace, name string, options metav1.GetOptions) (*v1alpha1.HelmRelease, error) { + result := &v1alpha1.HelmRelease{} + return result, c.client.Get(context.TODO(), namespace, name, result, options) +} + +func (c *helmReleaseController) List(namespace string, opts metav1.ListOptions) (*v1alpha1.HelmReleaseList, error) { + result := &v1alpha1.HelmReleaseList{} + return result, c.client.List(context.TODO(), namespace, result, opts) +} + +func (c *helmReleaseController) Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error) { + return c.client.Watch(context.TODO(), namespace, opts) +} + +func (c *helmReleaseController) Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (*v1alpha1.HelmRelease, error) { + result := &v1alpha1.HelmRelease{} + return result, c.client.Patch(context.TODO(), namespace, name, pt, data, result, metav1.PatchOptions{}, subresources...) +} + +type helmReleaseCache struct { + indexer cache.Indexer + resource schema.GroupResource +} + +func (c *helmReleaseCache) Get(namespace, name string) (*v1alpha1.HelmRelease, error) { + obj, exists, err := c.indexer.GetByKey(namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(c.resource, name) + } + return obj.(*v1alpha1.HelmRelease), nil +} + +func (c *helmReleaseCache) List(namespace string, selector labels.Selector) (ret []*v1alpha1.HelmRelease, err error) { + + err = cache.ListAllByNamespace(c.indexer, namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HelmRelease)) + }) + + return ret, err +} + +func (c *helmReleaseCache) AddIndexer(indexName string, indexer HelmReleaseIndexer) { + utilruntime.Must(c.indexer.AddIndexers(map[string]cache.IndexFunc{ + indexName: func(obj interface{}) (strings []string, e error) { + return indexer(obj.(*v1alpha1.HelmRelease)) + }, + })) +} + +func (c *helmReleaseCache) GetByIndex(indexName, key string) (result []*v1alpha1.HelmRelease, err error) { + objs, err := c.indexer.ByIndex(indexName, key) + if err != nil { + return nil, err + } + result = make([]*v1alpha1.HelmRelease, 0, len(objs)) + for _, obj := range objs { + result = append(result, obj.(*v1alpha1.HelmRelease)) + } + return result, nil +} + +type HelmReleaseStatusHandler func(obj *v1alpha1.HelmRelease, status v1alpha1.HelmReleaseStatus) (v1alpha1.HelmReleaseStatus, error) + +type HelmReleaseGeneratingHandler func(obj *v1alpha1.HelmRelease, status v1alpha1.HelmReleaseStatus) ([]runtime.Object, v1alpha1.HelmReleaseStatus, error) + +func RegisterHelmReleaseStatusHandler(ctx context.Context, controller HelmReleaseController, condition condition.Cond, name string, handler HelmReleaseStatusHandler) { + statusHandler := &helmReleaseStatusHandler{ + client: controller, + condition: condition, + handler: handler, + } + controller.AddGenericHandler(ctx, name, FromHelmReleaseHandlerToHandler(statusHandler.sync)) +} + +func RegisterHelmReleaseGeneratingHandler(ctx context.Context, controller HelmReleaseController, apply apply.Apply, + condition condition.Cond, name string, handler HelmReleaseGeneratingHandler, opts *generic.GeneratingHandlerOptions) { + statusHandler := &helmReleaseGeneratingHandler{ + HelmReleaseGeneratingHandler: handler, + apply: apply, + name: name, + gvk: controller.GroupVersionKind(), + } + if opts != nil { + statusHandler.opts = *opts + } + controller.OnChange(ctx, name, statusHandler.Remove) + RegisterHelmReleaseStatusHandler(ctx, controller, condition, name, statusHandler.Handle) +} + +type helmReleaseStatusHandler struct { + client HelmReleaseClient + condition condition.Cond + handler HelmReleaseStatusHandler +} + +func (a *helmReleaseStatusHandler) sync(key string, obj *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) { + if obj == nil { + return obj, nil + } + + origStatus := obj.Status.DeepCopy() + obj = obj.DeepCopy() + newStatus, err := a.handler(obj, obj.Status) + if err != nil { + // Revert to old status on error + newStatus = *origStatus.DeepCopy() + } + + if a.condition != "" { + if errors.IsConflict(err) { + a.condition.SetError(&newStatus, "", nil) + } else { + a.condition.SetError(&newStatus, "", err) + } + } + if !equality.Semantic.DeepEqual(origStatus, &newStatus) { + if a.condition != "" { + // Since status has changed, update the lastUpdatedTime + a.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339)) + } + + var newErr error + obj.Status = newStatus + newObj, newErr := a.client.UpdateStatus(obj) + if err == nil { + err = newErr + } + if newErr == nil { + obj = newObj + } + } + return obj, err +} + +type helmReleaseGeneratingHandler struct { + HelmReleaseGeneratingHandler + apply apply.Apply + opts generic.GeneratingHandlerOptions + gvk schema.GroupVersionKind + name string +} + +func (a *helmReleaseGeneratingHandler) Remove(key string, obj *v1alpha1.HelmRelease) (*v1alpha1.HelmRelease, error) { + if obj != nil { + return obj, nil + } + + obj = &v1alpha1.HelmRelease{} + obj.Namespace, obj.Name = kv.RSplit(key, "/") + obj.SetGroupVersionKind(a.gvk) + + return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts). + WithOwner(obj). + WithSetID(a.name). + ApplyObjects() +} + +func (a *helmReleaseGeneratingHandler) Handle(obj *v1alpha1.HelmRelease, status v1alpha1.HelmReleaseStatus) (v1alpha1.HelmReleaseStatus, error) { + if !obj.DeletionTimestamp.IsZero() { + return status, nil + } + + objs, newStatus, err := a.HelmReleaseGeneratingHandler(obj, status) + if err != nil { + return newStatus, err + } + + return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts). + WithOwner(obj). + WithSetID(a.name). + ApplyObjects(objs...) +} diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go new file mode 100644 index 00000000..2e911ae3 --- /dev/null +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go @@ -0,0 +1,48 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/schemes" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func init() { + schemes.Register(v1alpha1.AddToScheme) +} + +type Interface interface { + HelmRelease() HelmReleaseController +} + +func New(controllerFactory controller.SharedControllerFactory) Interface { + return &version{ + controllerFactory: controllerFactory, + } +} + +type version struct { + controllerFactory controller.SharedControllerFactory +} + +func (c *version) HelmRelease() HelmReleaseController { + return NewHelmReleaseController(schema.GroupVersionKind{Group: "helm.cattle.io", Version: "v1alpha1", Kind: "HelmRelease"}, "helmreleases", true, c.controllerFactory) +} diff --git a/pkg/helm-locker/gvk/gvk.go b/pkg/helm-locker/gvk/gvk.go new file mode 100644 index 00000000..6dc50679 --- /dev/null +++ b/pkg/helm-locker/gvk/gvk.go @@ -0,0 +1,141 @@ +package gvk + +import ( + "context" + "fmt" + "sync" + + "github.com/hashicorp/go-multierror" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/relatedresource" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Resolver is a relatedresource.Resolver that can work on multiple GVKs +type Resolver func(gvk schema.GroupVersionKind, namespace, name string, _ runtime.Object) ([]relatedresource.Key, error) + +// ForGVK returns the relatedresource.Resolver for a particular GVK +func (r Resolver) ForGVK(gvk schema.GroupVersionKind) relatedresource.Resolver { + return func(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) { + return r(gvk, namespace, name, obj) + } +} + +// Watcher starts controllers for one or more GVKs using the provided SharedControllerFactory +// After starting a GVK controller, it will register a relatedresource.Watch using the provided +// relatedresource.Enqueuer and Resolver +type Watcher interface { + // Start will run all the watchers that have been registered thus far and deferred from starting + Start(ctx context.Context, workers int) error + // Watch will start a new watcher for a particular GVK; if the Watcher has not started yet, + // watching will be deferred till the first Start call is made. + Watch(gvk schema.GroupVersionKind) error +} + +// NewWatcher returns an object that satisfies the Watcher interface +func NewWatcher(scf controller.SharedControllerFactory, gvkResolver Resolver, enqueuer relatedresource.Enqueuer) Watcher { + return &watcher{ + scf: scf, + gvkResolver: gvkResolver, + enqueuer: enqueuer, + + gvkRegistered: make(map[schema.GroupVersionKind]bool), + gvkStarted: make(map[schema.GroupVersionKind]bool), + } +} + +// watcher is a Watcher based on a provided resolver and enqueuer +type watcher struct { + + // scf is the controller.SharedControllerFactory to use to generate controllers from + scf controller.SharedControllerFactory + + // gvkResolver is the Resolver that is used to register the relatedresource.Watch + gvkResolver Resolver + + // enqueuer is the relatedresource.Enqueuer that is used to register the relatedresource.Watch + enqueuer relatedresource.Enqueuer + + // gvkRegistered is the list of all gvks that have been registered for Watch + // note: the associated gvkControllers will not be started if this Watcher has not been started yet + gvkRegistered map[schema.GroupVersionKind]bool + // gvkStarted is the list of all gvks that have already started watching and triggering enqueues + gvkStarted map[schema.GroupVersionKind]bool + + // started is whether the Watcher has started actually registering relatedresource.Watch + started bool + // controllerCtx is the context provided on start that all watchers will use + controllerCtx context.Context + // controllerWorkers is the number of worker threads each watcher should use to process resources + controllerWorkers int + + // lock ensures concurrent calls to Watch and Start happen atomically + lock sync.RWMutex +} + +// Watch begins watching a GVK or defers its start for after Start is called +func (w *watcher) Watch(gvk schema.GroupVersionKind) error { + w.lock.Lock() + defer w.lock.Unlock() + w.gvkRegistered[gvk] = true + return w.startGVK(gvk) +} + +// Start begins watching all registered GVKs +func (w *watcher) Start(ctx context.Context, workers int) error { + w.lock.Lock() + defer w.lock.Unlock() + w.started = true + w.controllerCtx = ctx + w.controllerWorkers = workers + var multierr error + for gvk := range w.gvkRegistered { + if err := w.startGVK(gvk); err != nil { + multierr = multierror.Append(multierr, err) + } + } + return multierr +} + +// startGVK starts watching a particular GVK if the Watcher has been started +func (w *watcher) startGVK(gvk schema.GroupVersionKind) error { + if !w.started { + return nil + } + if _, ok := w.gvkStarted[gvk]; ok { + // gvk was already started + return nil + } + gvkController, err := w.scf.ForKind(gvk) + if err != nil { + return err + } + + name := fmt.Sprintf("%s Watcher", gvk) + logrus.Infof("Starting %s", name) + + // NOTE: The order here (namely, calling relatedresource.Watch before gvkController.Start) is important. + // + // By default, the controller returned by a shared controller factory is a deferred controller + // that won't populate the actual underlying controller until at least one function is called on + // the controller (e.g. Enqueue, EnqueueAfter, EnqueueKey, Informer, or RegisterHandler) + // + // Therefore, running Start on an empty controller will result in the controller never registering + // the relatedresource.Watch we provide here, since the underlying informer is nil. + + relatedresource.Watch( + w.controllerCtx, + name, + w.gvkResolver.ForGVK(gvk), + w.enqueuer, + wrapController(gvkController), + ) + + if err := gvkController.Start(w.controllerCtx, w.controllerWorkers); err != nil { + return err + } + w.gvkStarted[gvk] = true + return nil +} diff --git a/pkg/helm-locker/gvk/lister.go b/pkg/helm-locker/gvk/lister.go new file mode 100644 index 00000000..bcaa43db --- /dev/null +++ b/pkg/helm-locker/gvk/lister.go @@ -0,0 +1,44 @@ +package gvk + +import ( + "strings" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" +) + +// Lister is any object that can list a set of GVKs or return an error +type Lister interface { + List() ([]schema.GroupVersionKind, error) +} + +// NewLister returns an object that implements the Lister interface +func NewLister(discovery discovery.DiscoveryInterface) Lister { + return &lister{ + discovery: discovery, + } +} + +// lister implements the Lister interface given the provided discovery interface +type lister struct { + discovery discovery.DiscoveryInterface +} + +// List returns a list of schema.GroupVersionKinds that you can run informers on +func (l *lister) List() ([]schema.GroupVersionKind, error) { + _, resources, err := l.discovery.ServerGroupsAndResources() + if err != nil { + return nil, err + } + var gvks []schema.GroupVersionKind + for _, resource := range resources { + for _, apiResource := range resource.APIResources { + if strings.Contains(apiResource.Name, "/") { + // Ignore subresources + continue + } + gvks = append(gvks, schema.FromAPIVersionAndKind(resource.GroupVersion, apiResource.Kind)) + } + } + return gvks, nil +} diff --git a/pkg/helm-locker/gvk/wrapper.go b/pkg/helm-locker/gvk/wrapper.go new file mode 100644 index 00000000..497076d1 --- /dev/null +++ b/pkg/helm-locker/gvk/wrapper.go @@ -0,0 +1,24 @@ +package gvk + +import ( + "context" + + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/generic" + "github.com/rancher/wrangler/pkg/relatedresource" +) + +// sharedControllerToWrapper converts a SharedController to a relatedresource.ControllerWrapper +type sharedControllerToWrapper struct { + controller.SharedController +} + +// AddGenericHandler registers a generic Handler on a SharedController +func (w sharedControllerToWrapper) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) { + w.RegisterHandler(ctx, name, controller.SharedControllerHandlerFunc(handler)) +} + +// wrapController returns a relatedresource.ControllerWrapper on top of a SharedController +func wrapController(c controller.SharedController) relatedresource.ControllerWrapper { + return sharedControllerToWrapper{c} +} diff --git a/pkg/helm-locker/informerfactory/wrapper.go b/pkg/helm-locker/informerfactory/wrapper.go new file mode 100644 index 00000000..bdc60f1d --- /dev/null +++ b/pkg/helm-locker/informerfactory/wrapper.go @@ -0,0 +1,29 @@ +package informerfactory + +import ( + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/apply" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/tools/cache" +) + +// New wraps the provided SharedControllerFactory to satisfy the apply.InformerFactory interface +func New(scf controller.SharedControllerFactory) apply.InformerFactory { + return informerFactoryWrapper{ + SharedControllerFactory: scf, + } +} + +// informerFactoryWrapper satisfies the apply.InformerFactory interface +type informerFactoryWrapper struct { + controller.SharedControllerFactory +} + +// Get returns a cache.SharedIndexInformer for a given GVK from the SharedControllerFactory +func (w informerFactoryWrapper) Get(gvk schema.GroupVersionKind, _ schema.GroupVersionResource) (cache.SharedIndexInformer, error) { + controller, err := w.ForKind(gvk) + if err != nil { + return nil, nil + } + return controller.Informer(), nil +} diff --git a/pkg/helm-locker/objectset/cache.go b/pkg/helm-locker/objectset/cache.go new file mode 100644 index 00000000..8a53321d --- /dev/null +++ b/pkg/helm-locker/objectset/cache.go @@ -0,0 +1,370 @@ +package objectset + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/rancher/helm-project-operator/pkg/helm-locker/gvk" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/objectset" + "github.com/rancher/wrangler/pkg/relatedresource" + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" +) + +// LockableRegister implements Register and Locker +type LockableRegister interface { + Register + Locker +} + +// Register can keep track of sets of ObjectSets +type Register interface { + relatedresource.Enqueuer + + // Set allows you to set and lock an objectset associated with a specific key + // if os or locked are not provided, the currently persisted values will be used + Set(key relatedresource.Key, os *objectset.ObjectSet, locked *bool) + + // Delete allows you to delete an objectset associated with a specific key + Delete(key relatedresource.Key, purge bool) +} + +// Locker can lock or unlock object sets tied to a specific key +type Locker interface { + // Lock allows you to lock an objectset associated with a specific key + Lock(key relatedresource.Key) + + // Unlock allows you to unlock an objectset associated with a specific key + Unlock(key relatedresource.Key) +} + +// newLockableObjectSetRegisterAndCache returns: +// 1) a LockableRegister that allows registering new ObjectSets, locking them, unlocking them, or deleting them +// 2) a cache.SharedIndexInformer that listens to events on objectSetStates that are created from interacting with the provided register +// +// Note: This function is intentionally internal since the cache.SharedIndexInformer responds to an internal runtime.Object type (objectSetState) +func newLockableObjectSetRegisterAndCache(scf controller.SharedControllerFactory, triggerOnDelete func(string, bool)) (LockableRegister, cache.SharedIndexInformer) { + c := lockableObjectSetRegisterAndCache{ + stateByKey: make(map[relatedresource.Key]*objectSetState), + keyByResourceKeyByGVK: make(map[schema.GroupVersionKind]map[relatedresource.Key]relatedresource.Key), + + stateChanges: make(chan watch.Event, 50), + + triggerOnDelete: triggerOnDelete, + } + // initialize watcher that populates watch queue + c.gvkWatcher = gvk.NewWatcher(scf, c.Resolve, &c) + // initialize informer + c.SharedIndexInformer = cache.NewSharedIndexInformer(&c, &objectSetState{}, 10*time.Hour, cache.Indexers{ + cache.NamespaceIndex: cache.MetaNamespaceIndexFunc, + }) + return &c, &c +} + +// lockableObjectSetRegisterAndCache is a cache.SharedIndexInformer that operates on objectSetStates +// and implements the LockableRegister interface via the informer +// +// internal note: also implements cache.ListerWatcher on objectSetStates +// internal note: also implements watch.Interface on objectSetStates +type lockableObjectSetRegisterAndCache struct { + cache.SharedIndexInformer + + // stateChanges is the internal channel tracking events that happen to ObjectSetStates + stateChanges chan watch.Event + // gvkWatcher watches all GVKs tied to resources tracked by any ObjectSet tracked by this register + // It will automatically trigger an Enqueue on seeing changes, which will trigger an event that + // the underlying cache.SharedIndexInformer will process + gvkWatcher gvk.Watcher + // started represents whether the cache has been started yet + started bool + // startLock is a lock that prevents a Watch from occurring before the Informer has been started + startLock sync.RWMutex + + // stateByKey is a map that keeps track of the desired state of the Register + stateByKey map[relatedresource.Key]*objectSetState + // stateMapLock is a lock on the stateByKey map + stateMapLock sync.RWMutex + + // keyByResourceKeyByGVK is a map that keeps track of which resources are tied to a particular ObjectSet + // This is used to make resolving the objectset on seeing changes to underlying resources more efficient + keyByResourceKeyByGVK map[schema.GroupVersionKind]map[relatedresource.Key]relatedresource.Key + // keyMapLock is a lock on the keyByResourceKeyByGVK map + keyMapLock sync.RWMutex + + // triggerOnDelete allows registering a function that gets called on a delete from the cache + // purge indicates whether or not the triggerOnDelete function is expected to purge underlying + // resources on deleting an objectSet + triggerOnDelete func(key string, purge bool) +} + +// init initializes the register and the cache +func (c *lockableObjectSetRegisterAndCache) init() { + c.startLock.Lock() + defer c.startLock.Unlock() + // do not start twice + if !c.started { + c.started = true + } +} + +// Run starts the objectSetState informer and starts watching GVKs tracked by ObjectSets +func (c *lockableObjectSetRegisterAndCache) Run(stopCh <-chan struct{}) { + c.init() + err := c.gvkWatcher.Start(context.TODO(), 50) + if err != nil { + logrus.Errorf("unable to watch gvks: %s", err) + } + + c.SharedIndexInformer.Run(stopCh) +} + +// Stop is a noop +// Allows implementing watch.Interface on objectSetStates +func (c *lockableObjectSetRegisterAndCache) Stop() {} + +// ResultChan returns the channel that watch.Events on objectSetStates are registered on +// Allows implementing watch.Interface on objectSetStates +func (c *lockableObjectSetRegisterAndCache) ResultChan() <-chan watch.Event { + return c.stateChanges +} + +// List returns an objectSetStateList +// Allows implementing cache.ListerWatcher on objectSetStates +func (c *lockableObjectSetRegisterAndCache) List(options metav1.ListOptions) (runtime.Object, error) { + c.stateMapLock.RLock() + defer c.stateMapLock.RUnlock() + objectSetStateList := &objectSetStateList{} + for _, objectSetState := range c.stateByKey { + if objectSetState != nil { + objectSetStateList.Items = append(objectSetStateList.Items, *objectSetState) + } + } + objectSetStateList.ResourceVersion = options.ResourceVersion + return objectSetStateList, nil +} + +// List returns an watch.Interface if the cache has been started that watches for events on objectSetStates +// Allows implementing cache.ListerWatcher on objectSetStates +func (c *lockableObjectSetRegisterAndCache) Watch(_ metav1.ListOptions) (watch.Interface, error) { + c.startLock.RLock() + defer c.startLock.RUnlock() + if !c.started { + return nil, fmt.Errorf("cache is not started yet") + } + return c, nil +} + +// Set allows you to set and lock an objectset associated with a specific key +func (c *lockableObjectSetRegisterAndCache) Set(key relatedresource.Key, os *objectset.ObjectSet, locked *bool) { + logrus.Debugf("set objectset for %s/%s", key.Namespace, key.Name) + c.setState(key, os, locked, false) +} + +// Lock allows you to lock an objectset associated with a specific key +func (c *lockableObjectSetRegisterAndCache) Lock(key relatedresource.Key) { + logrus.Debugf("locking %s/%s", key.Namespace, key.Name) + s, ok := c.getState(key) + if !ok { + // nothing to lock + return + } + if s.ObjectSet == nil { + // nothing to lock + return + } + c.lock(key, s.ObjectSet) +} + +// Unlock allows you to unlock an objectset associated with a specific key +func (c *lockableObjectSetRegisterAndCache) Unlock(key relatedresource.Key) { + logrus.Debugf("unlocking %s/%s", key.Namespace, key.Name) + c.unlock(key) +} + +// Delete allows you to delete an objectset associated with a specific key +func (c *lockableObjectSetRegisterAndCache) Delete(key relatedresource.Key, purge bool) { + logrus.Debugf("deleting %s/%s", key.Namespace, key.Name) + c.deleteState(key) + c.triggerOnDelete(fmt.Sprintf("%s/%s", key.Namespace, key.Name), purge) +} + +// Enqueue allows you to enqueue an objectset associated with a specific key +func (c *lockableObjectSetRegisterAndCache) Enqueue(namespace, name string) { + key := keyFunc(namespace, name) + c.setState(key, nil, nil, true) +} + +// Resolve allows you to resolve an object seen in the cluster to an ObjectSet tracked in this LockableRegister +// Objects will only be resolved if the LockableRegister has locked this ObjectSet +func (c *lockableObjectSetRegisterAndCache) Resolve(gvk schema.GroupVersionKind, namespace, name string, _ runtime.Object) ([]relatedresource.Key, error) { + resourceKey := keyFunc(namespace, name) + + c.keyMapLock.RLock() + defer c.keyMapLock.RUnlock() + keyByResourceKey, ok := c.keyByResourceKeyByGVK[gvk] + if !ok { + // do nothing since we're not watching this GVK anymore + return nil, nil + } + key, ok := keyByResourceKey[resourceKey] + if !ok { + // do nothing since the resource is not tied to a set + return nil, nil + } + logrus.Infof("detected change in %s/%s (%s), enqueuing objectset %s/%s", namespace, name, gvk, key.Namespace, key.Name) + return []relatedresource.Key{key}, nil +} + +// getState returns the underlying objectSetState for a given key +func (c *lockableObjectSetRegisterAndCache) getState(key relatedresource.Key) (*objectSetState, bool) { + c.stateMapLock.RLock() + defer c.stateMapLock.RUnlock() + state, ok := c.stateByKey[key] + return state, ok +} + +// setState allows a user to set the objectSetState for a given key +func (c *lockableObjectSetRegisterAndCache) setState(key relatedresource.Key, os *objectset.ObjectSet, locked *bool, forceEnqueue bool) { + // get old state and use as the base + originalState, modifying := c.getState(key) + var s *objectSetState + + // generate new state to be set + if !modifying { + s = newObjectSetState(key.Namespace, key.Name, objectSetState{}) + } else { + s = originalState.DeepCopy() + s.Generation++ + s.ResourceVersion = fmt.Sprintf("%d", s.Generation) + } + + // apply provided settings or use original state as default + if os != nil { + s.ObjectSet = os + } + if locked != nil { + s.Locked = *locked + } + + // do nothing if the object has not changed + objectChanged := forceEnqueue || !modifying + if modifying { + objectChanged = objectChanged || s.ObjectSet != originalState.ObjectSet || s.Locked != originalState.Locked + } + if !objectChanged { + return + } + + // handle adding events and storing state + c.stateMapLock.Lock() + defer c.stateMapLock.Unlock() + if modifying { + c.stateChanges <- watch.Event{Type: watch.Modified, Object: s} + } else { + c.stateChanges <- watch.Event{Type: watch.Added, Object: s} + } + c.stateByKey[key] = s + logrus.Debugf("set state for %s/%s: locked %t, os %p, objectMeta: %v", s.Namespace, s.Name, s.Locked, s.ObjectSet, s.ObjectMeta) +} + +// deleteState deletes anything on the register for a given key +func (c *lockableObjectSetRegisterAndCache) deleteState(key relatedresource.Key) { + s, exists := c.getState(key) + if !exists { + // nothing to add, event was already processed + return + } + c.stateMapLock.Lock() + delete(c.stateByKey, key) + c.stateMapLock.Unlock() + + s.ObjectSet = nil + s.Locked = false + c.stateChanges <- watch.Event{Type: watch.Deleted, Object: s} +} + +// lock adds entries to the register to ensure that resources tracked by this ObjectSet are resolved to this ObjectSet +func (c *lockableObjectSetRegisterAndCache) lock(key relatedresource.Key, os *objectset.ObjectSet) error { + c.keyMapLock.Lock() + defer c.keyMapLock.Unlock() + + if err := c.canLock(key, os); err != nil { + return err + } + + c.removeAllEntries(key) + + objectsByGVK := os.ObjectsByGVK() + + for gvk, objMap := range objectsByGVK { + keyByResourceKey, ok := c.keyByResourceKeyByGVK[gvk] + if !ok { + keyByResourceKey = make(map[relatedresource.Key]relatedresource.Key) + } + for objKey := range objMap { + resourceKey := keyFunc(objKey.Namespace, objKey.Name) + keyByResourceKey[resourceKey] = key + } + c.keyByResourceKeyByGVK[gvk] = keyByResourceKey + + // ensure that we are watching this new GVK + if err := c.gvkWatcher.Watch(gvk); err != nil { + return err + } + } + + return nil +} + +// unlock removes all entries to the register tied to a particular ObjectSet by key +func (c *lockableObjectSetRegisterAndCache) unlock(key relatedresource.Key) { + c.keyMapLock.Lock() + defer c.keyMapLock.Unlock() + + c.removeAllEntries(key) +} + +// canLock returns whether trynig to lock the provided ObjectSet will result in an error +// One of the few reasons why this is possible is if two registered ObjectSets are attempting to track the same resource +func (c *lockableObjectSetRegisterAndCache) canLock(key relatedresource.Key, os *objectset.ObjectSet) error { + objectsByGVK := os.ObjectsByGVK() + for gvk, objMap := range objectsByGVK { + keyByResourceKey, ok := c.keyByResourceKeyByGVK[gvk] + if !ok { + continue + } + for objKey := range objMap { + resourceKey := keyFunc(objKey.Namespace, objKey.Name) + currKey, ok := keyByResourceKey[resourceKey] + if ok && currKey != key { + // object is already associated with another set + return fmt.Errorf("cannot lock objectset for %s: object %s is already associated with key %s", key, objKey, currKey) + } + } + } + return nil +} + +// removeAllEntries removes all entries to the register tied to a particular ObjectSet by key +// Note: This is a thread-unsafe version of +func (c *lockableObjectSetRegisterAndCache) removeAllEntries(key relatedresource.Key) { + for gvk, keyByResourceKey := range c.keyByResourceKeyByGVK { + for resourceKey, currSetKey := range keyByResourceKey { + if key == currSetKey { + delete(keyByResourceKey, resourceKey) + } + } + if len(keyByResourceKey) == 0 { + delete(c.keyByResourceKeyByGVK, gvk) + } else { + c.keyByResourceKeyByGVK[gvk] = keyByResourceKey + } + } +} diff --git a/pkg/helm-locker/objectset/controller.go b/pkg/helm-locker/objectset/controller.go new file mode 100644 index 00000000..7815a631 --- /dev/null +++ b/pkg/helm-locker/objectset/controller.go @@ -0,0 +1,56 @@ +package objectset + +import ( + "context" + "time" + + "github.com/rancher/helm-project-operator/pkg/helm-locker/gvk" + "github.com/rancher/helm-project-operator/pkg/helm-locker/informerfactory" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/apply" + "github.com/rancher/wrangler/pkg/start" + "k8s.io/client-go/discovery" + "k8s.io/client-go/util/workqueue" +) + +// NewLockableRegister returns a starter that starts an ObjectSetController listening to events on ObjectSetStates +// and a LockableRegister that allows you to register new states for ObjectSets in memory +func NewLockableRegister(name string, apply apply.Apply, scf controller.SharedControllerFactory, discovery discovery.DiscoveryInterface, opts *controller.Options) (start.Starter, LockableRegister, *controller.SharedHandler) { + // Define a new cache + apply = apply.WithCacheTypeFactory(informerfactory.New(scf)) + + handler := handler{ + apply: apply, + gvkLister: gvk.NewLister(discovery), + sharedHandler: &controller.SharedHandler{}, + } + + lockableObjectSetRegister, objectSetCache := newLockableObjectSetRegisterAndCache(scf, handler.OnRemove) + + handler.locker = lockableObjectSetRegister + + startCache := func(ctx context.Context) error { + go objectSetCache.Run(ctx.Done()) + return nil + } + + // Define a new controller that responds to events from the cache + objectSetController := controller.New(name, objectSetCache, startCache, &handler, applyDefaultOptions(opts)) + + return wrapStarter(objectSetController), lockableObjectSetRegister, handler.sharedHandler +} + +// applyDefaultOptions applies default controller options if none are provided +func applyDefaultOptions(opts *controller.Options) *controller.Options { + var newOpts controller.Options + if opts != nil { + newOpts = *opts + } + if newOpts.RateLimiter == nil { + newOpts.RateLimiter = workqueue.NewMaxOfRateLimiter( + workqueue.NewItemFastSlowRateLimiter(time.Millisecond, 2*time.Minute, 30), + workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 30*time.Second), + ) + } + return &newOpts +} diff --git a/pkg/helm-locker/objectset/handler.go b/pkg/helm-locker/objectset/handler.go new file mode 100644 index 00000000..217cc03b --- /dev/null +++ b/pkg/helm-locker/objectset/handler.go @@ -0,0 +1,103 @@ +package objectset + +import ( + "fmt" + + "github.com/rancher/helm-project-operator/pkg/helm-locker/gvk" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/apply" + "github.com/rancher/wrangler/pkg/relatedresource" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime" +) + +type handler struct { + apply apply.Apply + gvkLister gvk.Lister + locker Locker + + // allows us to add hooks into triggering certain actions on reconciles, e.g. launching events + sharedHandler *controller.SharedHandler +} + +// configureApply configures the apply object for the provided setID and objectSetState +func (h *handler) configureApply(setID string, oss *objectSetState) apply.Apply { + apply := h.apply. + WithSetID("object-set-applier"). + WithOwnerKey(setID, internalGroupVersion.WithKind("objectSetState")) + + if oss != nil && oss.ObjectSet != nil { + apply = apply.WithGVK(oss.ObjectSet.GVKs()...) + } else { + // if we cannot infer the GVK from the provided object set, include all GVKs in the cache types + gvks, err := h.gvkLister.List() + if err != nil { + logrus.Errorf("unable to list GVKs to apply deletes on objects, objectset %s may require manual cleanup: %s", setID, err) + } else { + apply = apply.WithGVK(gvks...) + } + } + + return apply +} + +// OnChange reconciles the resources tracked by an objectSetState +func (h *handler) OnChange(setID string, obj runtime.Object) error { + logrus.Debugf("on change: %s", setID) + + if obj == nil { + // nothing to do + return nil + } + oss, ok := obj.(*objectSetState) + if !ok { + return fmt.Errorf("expected object of type objectSetState, found %t", obj) + } + + if oss.DeletionTimestamp != nil { + return nil + } + + key := relatedresource.FromString(setID) + h.locker.Unlock(key) // ensure that apply does not trigger locking again + + if !oss.Locked { + // nothing to do + return nil + } + // Run the apply + defer h.locker.Lock(key) + + logrus.Debugf("running apply for %s...", setID) + if err := h.configureApply(setID, oss).Apply(oss.ObjectSet); err != nil { + return fmt.Errorf("failed to apply objectset for %s: %s", setID, err) + } + + logrus.Infof("applied %s", setID) + + go h.sharedHandler.OnChange(setID, obj) + + return nil +} + +// OnRemove cleans up the resources tracked by an objectSetState +func (h *handler) OnRemove(setID string, purge bool) { + logrus.Debugf("on delete: %s", setID) + + key := relatedresource.FromString(setID) + + h.locker.Unlock(key) + + if !purge { + return + } + + logrus.Debugf("running apply for %s...", setID) + if err := h.configureApply(setID, nil).ApplyObjects(); err != nil { + logrus.Errorf("failed to clean up objectset %s: %s", setID, err) + } + + logrus.Infof("applied %s", setID) + + go h.sharedHandler.OnChange(setID, nil) +} diff --git a/pkg/helm-locker/objectset/parser/parse.go b/pkg/helm-locker/objectset/parser/parse.go new file mode 100644 index 00000000..291ea8da --- /dev/null +++ b/pkg/helm-locker/objectset/parser/parse.go @@ -0,0 +1,33 @@ +package parser + +import ( + "bytes" + + "github.com/rancher/wrangler/pkg/objectset" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/yaml" +) + +// Parse parses the runtime.Objects tracked in a Kubernetes manifest (represented as a string) into an ObjectSet +func Parse(manifest string) (*objectset.ObjectSet, error) { + var multierr error + + var u unstructured.Unstructured + decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000) + os := objectset.NewObjectSet() + for { + uCopy := u.DeepCopy() + err := decoder.Decode(uCopy) + if err != nil { + break + } + if uCopy.GetAPIVersion() == "" || uCopy.GetKind() == "" { + // Encountered empty YAML document but successfully decoded, skip + continue + } + os = os.Add(uCopy) + logrus.Debugf("obj: %s, Kind=%s (%s/%s)", uCopy.GetAPIVersion(), uCopy.GetKind(), uCopy.GetName(), uCopy.GetNamespace()) + } + return os, multierr +} diff --git a/pkg/helm-locker/objectset/state.go b/pkg/helm-locker/objectset/state.go new file mode 100644 index 00000000..50b38719 --- /dev/null +++ b/pkg/helm-locker/objectset/state.go @@ -0,0 +1,117 @@ +package objectset + +import ( + "time" + + "github.com/google/uuid" + "github.com/rancher/wrangler/pkg/objectset" + "github.com/rancher/wrangler/pkg/schemes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" +) + +var ( + internalGroupVersion = schema.GroupVersion{Group: "internal.cattle.io", Version: "v1alpha1"} +) + +// init adds the internal type to the default scheme for wrangler.apply to be able to use to add owner key details +func init() { + schemes.Register(addInternalTypes) +} + +// Adds the list of known types to Scheme. +func addInternalTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(internalGroupVersion, &objectSetState{}, &objectSetStateList{}) + metav1.AddToGroupVersion(scheme, internalGroupVersion) + return nil +} + +// newObjectSetState returns a new objectSetState for internal consumption +func newObjectSetState(namespace, name string, obj objectSetState) *objectSetState { + obj.APIVersion, obj.Kind = internalGroupVersion.WithKind("objectSetState").ToAPIVersionAndKind() + obj.Name = name + obj.Namespace = namespace + // set values normally set by the server for an internal object + obj.Generation = 0 + obj.UID = types.UID(uuid.New().String()) + obj.CreationTimestamp = metav1.NewTime(time.Now()) + obj.ResourceVersion = "0" + return &obj +} + +// objectSetState represents the state of an object set. This resource is only intended for +// internal use by this controller +type objectSetState struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // ObjectSet is a pointer to the underlying ObjectSet whose state is being tracked + ObjectSet *objectset.ObjectSet `json:"objectSet,omitempty"` + + // Locked represents whether the ObjectSet should be locked in the cluster or not + Locked bool `json:"locked"` +} + +// DeepCopyInto is a deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *objectSetState) DeepCopyInto(out *objectSetState) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.ObjectSet = in.ObjectSet + out.Locked = in.Locked +} + +// DeepCopy is a deepcopy function, copying the receiver, creating a new objectSetState. +func (in *objectSetState) DeepCopy() *objectSetState { + if in == nil { + return nil + } + out := new(objectSetState) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is a deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *objectSetState) DeepCopyObject() runtime.Object { + return in.DeepCopy() +} + +// objectSetStateList represents a list of objectSetStates +type objectSetStateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + // Items are the objectSetStates tracked by this list + Items []objectSetState `json:"items"` +} + +// DeepCopyInto is a deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *objectSetStateList) DeepCopyInto(out *objectSetStateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]objectSetState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is a deepcopy function, copying the receiver, creating a new objectSetStateList. +func (in *objectSetStateList) DeepCopy() *objectSetStateList { + if in == nil { + return nil + } + out := new(objectSetStateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is a deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *objectSetStateList) DeepCopyObject() runtime.Object { + return in.DeepCopy() +} diff --git a/pkg/helm-locker/objectset/utils.go b/pkg/helm-locker/objectset/utils.go new file mode 100644 index 00000000..da50f505 --- /dev/null +++ b/pkg/helm-locker/objectset/utils.go @@ -0,0 +1,13 @@ +package objectset + +import ( + "github.com/rancher/wrangler/pkg/relatedresource" +) + +// keyFunc is a utility function that returns a relatedresource.Key from a namespace and a name +func keyFunc(namespace, name string) relatedresource.Key { + return relatedresource.Key{ + Namespace: namespace, + Name: name, + } +} diff --git a/pkg/helm-locker/objectset/wrapper.go b/pkg/helm-locker/objectset/wrapper.go new file mode 100644 index 00000000..bd0a34cb --- /dev/null +++ b/pkg/helm-locker/objectset/wrapper.go @@ -0,0 +1,23 @@ +package objectset + +import ( + "context" + + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/start" +) + +// controllerToStarterWrapper wraps the generic controller.Controller interface with a dummy call for Sync +type controllerToStarterWrapper struct { + controller.Controller +} + +// Sync does a noop +func (w controllerToStarterWrapper) Sync(_ context.Context) error { + return nil +} + +// wrapStarter returns a start.Starter around a controller.Controller +func wrapStarter(c controller.Controller) start.Starter { + return controllerToStarterWrapper{c} +} diff --git a/pkg/helm-locker/releases/releases.go b/pkg/helm-locker/releases/releases.go new file mode 100644 index 00000000..a117c7d5 --- /dev/null +++ b/pkg/helm-locker/releases/releases.go @@ -0,0 +1,47 @@ +package releases + +import ( + "sync" + + "github.com/sirupsen/logrus" + rspb "helm.sh/helm/v3/pkg/release" + "helm.sh/helm/v3/pkg/storage" + "helm.sh/helm/v3/pkg/storage/driver" + "k8s.io/client-go/kubernetes" +) + +type HelmReleaseGetter interface { + Last(namespace, name string) (*rspb.Release, error) +} + +func NewHelmReleaseGetter(k8s kubernetes.Interface) HelmReleaseGetter { + return &latestReleaseGetter{ + K8s: k8s, + namespacedStorage: make(map[string]*storage.Storage), + } +} + +type latestReleaseGetter struct { + K8s kubernetes.Interface + + namespacedStorage map[string]*storage.Storage + storageLock sync.Mutex +} + +func (g *latestReleaseGetter) getStore(namespace string) *storage.Storage { + g.storageLock.Lock() + defer g.storageLock.Unlock() + store, ok := g.namespacedStorage[namespace] + if ok && store != nil { + return store + } + store = storage.Init(driver.NewSecrets(g.K8s.CoreV1().Secrets(namespace))) + store.Log = logrus.Debugf + g.namespacedStorage[namespace] = store + return store +} + +func (g *latestReleaseGetter) Last(namespace, name string) (*rspb.Release, error) { + store := g.getStore(namespace) + return store.Last(name) +} From 8ae1135a6df642d57ab1e79b5a4e17179157ac35 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 17:04:48 -0400 Subject: [PATCH 08/45] Add versoin function doc --- pkg/version/version.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/version/version.go b/pkg/version/version.go index 3482d080..028b81b3 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -7,6 +7,7 @@ var ( GitCommit = "HEAD" ) +// FriendlyVersion outputs a version that will be displayed on running --version on the binary func FriendlyVersion() string { return fmt.Sprintf("%s (%s)", Version, GitCommit) } From 6eac673c318acecd2472d333221c3c96b6ab2774 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 17:19:12 -0400 Subject: [PATCH 09/45] make sure `helm-locker` pkg doesn't use `helm-project-operator` repo [squash] merge with "add helm-locker" or "make helm-locker use local" --- cmd/helm-locker/main.go | 4 +- go.mod | 108 +- go.sum | 1311 ++++++++++++++--- .../v1alpha1/zz_generated_register.go | 2 +- pkg/helm-locker/codegen/main.go | 6 +- pkg/helm-locker/controllers/controller.go | 8 +- .../controllers/release/controller.go | 12 +- pkg/helm-locker/controllers/release/info.go | 2 +- pkg/helm-locker/controllers/release/utils.go | 2 +- pkg/helm-locker/crd/crds.go | 2 +- .../controllers/helm.cattle.io/interface.go | 2 +- .../helm.cattle.io/v1alpha1/helmrelease.go | 2 +- .../helm.cattle.io/v1alpha1/interface.go | 2 +- pkg/helm-locker/objectset/cache.go | 2 +- pkg/helm-locker/objectset/controller.go | 4 +- pkg/helm-locker/objectset/handler.go | 2 +- pkg/remove/handler.go | 39 + 17 files changed, 1255 insertions(+), 255 deletions(-) create mode 100644 pkg/remove/handler.go diff --git a/cmd/helm-locker/main.go b/cmd/helm-locker/main.go index b2159c58..3b05cf12 100644 --- a/cmd/helm-locker/main.go +++ b/cmd/helm-locker/main.go @@ -8,8 +8,8 @@ import ( "net/http" _ "net/http/pprof" - "github.com/rancher/helm-project-operator/pkg/helm-locker/controllers" - "github.com/rancher/helm-project-operator/pkg/helm-locker/crd" + "github.com/rancher/prometheus-federator/pkg/helm-locker/controllers" + "github.com/rancher/prometheus-federator/pkg/helm-locker/crd" "github.com/rancher/prometheus-federator/pkg/version" command "github.com/rancher/wrangler-cli" _ "github.com/rancher/wrangler/pkg/generated/controllers/apiextensions.k8s.io" diff --git a/go.mod b/go.mod index c95c1a52..389a5f43 100644 --- a/go.mod +++ b/go.mod @@ -3,94 +3,90 @@ module github.com/rancher/prometheus-federator go 1.22.3 replace ( - k8s.io/api => k8s.io/api v0.25.4 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.25.4 - k8s.io/apimachinery => k8s.io/apimachinery v0.25.4 - k8s.io/client-go => github.com/rancher/client-go v1.25.4-rancher1 - k8s.io/code-generator => k8s.io/code-generator v0.25.4 + k8s.io/api => k8s.io/api v0.22.3 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.22.3 + k8s.io/apimachinery => k8s.io/apimachinery v0.22.3 + k8s.io/client-go => github.com/rancher/client-go v1.22.3-rancher.1 + k8s.io/code-generator => k8s.io/code-generator v0.22.3 ) require ( - github.com/rancher/helm-project-operator v0.3.0 - github.com/rancher/wrangler v1.0.2 - github.com/rancher/wrangler-cli v0.0.0-20220624114648-479c5692ba22 - github.com/spf13/cobra v1.6.1 + github.com/google/uuid v1.2.0 + github.com/hashicorp/go-multierror v1.1.0 + github.com/rancher/helm-project-operator v0.3.1 + github.com/rancher/lasso v0.0.0-20220303220127-8cf5555ec03c + github.com/rancher/wrangler v0.8.11-0.20220217210408-3ecd23dfea3b + github.com/rancher/wrangler-cli v0.0.0-20211112052728-f172e9bf59af + github.com/sirupsen/logrus v1.8.1 + github.com/spf13/cobra v1.4.0 + helm.sh/helm/v3 v3.8.0 + k8s.io/api v0.23.3 + k8s.io/apimachinery v0.23.3 + k8s.io/client-go v0.23.3 ) require ( - github.com/Masterminds/semver/v3 v3.2.0 // indirect - github.com/Masterminds/squirrel v1.5.3 // indirect + github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/Masterminds/squirrel v1.5.2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-gorp/gorp/v3 v3.1.0 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-logr/logr v1.2.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/imdario/mergo v0.3.13 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect - github.com/josharian/intern v1.0.0 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/googleapis/gnostic v0.5.5 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jmoiron/sqlx v1.3.4 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/k3s-io/helm-controller v0.13.1 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/lib/pq v1.10.7 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/lib/pq v1.10.4 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.39.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/rancher/lasso v0.0.0-20221227210133-6ea88ca2fbcc // indirect - github.com/rubenv/sql-migrate v1.3.0 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect + github.com/prometheus/client_golang v1.12.1 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/net v0.7.0 // indirect - golang.org/x/oauth2 v0.4.0 // indirect - golang.org/x/sync v0.1.0 // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/gorp.v1 v1.7.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - helm.sh/helm/v3 v3.11.0 // indirect - k8s.io/api v0.26.1 // indirect - k8s.io/apiextensions-apiserver v0.26.1 // indirect - k8s.io/apimachinery v0.26.1 // indirect - k8s.io/client-go v1.5.2 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + k8s.io/apiextensions-apiserver v0.23.1 // indirect + k8s.io/code-generator v0.23.3 // indirect + k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c // indirect k8s.io/klog v1.0.0 // indirect - k8s.io/klog/v2 v2.90.0 // indirect - k8s.io/kube-openapi v0.0.0-20230127205639-68031ae9242a // indirect - k8s.io/utils v0.0.0-20230115233650-391b47cb4029 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + k8s.io/klog/v2 v2.30.0 // indirect + k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 6c3162e9..8bb21116 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -18,6 +19,15 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -27,6 +37,7 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -37,55 +48,309 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= -github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE= +github.com/Masterminds/squirrel v1.5.2/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= +github.com/Microsoft/hcsshim v0.9.1/go.mod h1:Y/0uV2jUab5kBI7SQgl62at0AVX7uaruzADAVmxm3eM= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= +github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/cgroups v1.0.2/go.mod h1:qpbpJ1jmlqsR9f2IyaLPsdkCdnt0rbDVqIDlhuu5tRY= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684/go.mod h1:UfCu3YXJJCI+IdnqGgYP82dk2+Joxmv+mUTVBES6wac= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -93,49 +358,138 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= -github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= -github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= -github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= -github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= -github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= -github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= -github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc= +github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= +github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= +github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= +github.com/gobuffalo/packr/v2 v2.8.1 h1:tkQpju6i3EtMXJ9uoF5GT6kB+LMTimDWD8Xvbz6zDVA= +github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -149,8 +503,9 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -169,10 +524,14 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= +github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= +github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= -github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -184,15 +543,17 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -204,27 +565,67 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -232,49 +633,82 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w= +github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k3s-io/helm-controller v0.13.1 h1:eG2yZ0QzbtcfMe8GpTVtRtP6HgMDO/Pr9Q1EGbMKKCA= github.com/k3s-io/helm-controller v0.13.1/go.mod h1:f8aOuHQDpkshmUK/GiE+jJCJkUL8vp+EzCjV0uCFcsY= -github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= -github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.15.8 h1:7+rWAZPn9zuRxaIqqT8Ohs2Q2Ac0msBqwRdxNCr2VVs= +github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -284,161 +718,387 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= 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-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= -github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= -github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= -github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/rancher/client-go v1.25.4-rancher1 h1:9MlBC8QbgngUkhNzMR8rZmmCIj6WNRHFOnYiwC2Kty4= -github.com/rancher/client-go v1.25.4-rancher1/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw= -github.com/rancher/helm-project-operator v0.3.0 h1:9JtzgQVUnwgr9btUNFybPUuChAKOL3ye6855ATAtQyM= -github.com/rancher/helm-project-operator v0.3.0/go.mod h1:HkQq2yAWVGoZ0Q6jUlNTJaI2J8mar/PF8Ur2PN9nmYY= -github.com/rancher/lasso v0.0.0-20221227210133-6ea88ca2fbcc h1:29VHrInLV4qSevvcvhBj5UhQWkPShxrxv4AahYg2Scw= -github.com/rancher/lasso v0.0.0-20221227210133-6ea88ca2fbcc/go.mod h1:dEfC9eFQigj95lv/JQ8K5e7+qQCacWs1aIA6nLxKzT8= -github.com/rancher/wrangler v1.0.2 h1:0JGv62gF2OkYUoR0fsr99Za63fquFeKTHE2z9kAFVsE= -github.com/rancher/wrangler v1.0.2/go.mod h1:Blhan9LdaIJjC9w+xGteSrHHEiIFIdPEHEMrtx82dPk= -github.com/rancher/wrangler-cli v0.0.0-20220624114648-479c5692ba22 h1:ADMwgJyVwmLXJBSm/nNobB1XGSmFCTA+TY/otxgIPu4= -github.com/rancher/wrangler-cli v0.0.0-20220624114648-479c5692ba22/go.mod h1:vyO9SU60oplNFa5ZqoEAFWmYKgj2F6remdy8p6H0SgI= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA= +github.com/rancher/client-go v1.22.3-rancher.1 h1:aNVLaIY5YGah1i9wRVXLOGRbLyekohjQAKHXeQm6Cxo= +github.com/rancher/client-go v1.22.3-rancher.1/go.mod h1:ElDjYf8gvZsKDYexmsmnMQ0DYO8W9RwBjfQ1PI53yow= +github.com/rancher/helm-project-operator v0.3.1 h1:yN/DZCTPpCDDfBPTjc4emXuKn4jPGVROiGU5W6Y1m60= +github.com/rancher/helm-project-operator v0.3.1/go.mod h1:HkQq2yAWVGoZ0Q6jUlNTJaI2J8mar/PF8Ur2PN9nmYY= +github.com/rancher/lasso v0.0.0-20210616224652-fc3ebd901c08/go.mod h1:9qZd/S8DqWzfKtjKGgSoHqGEByYmUE3qRaBaaAHwfEM= +github.com/rancher/lasso v0.0.0-20220303220127-8cf5555ec03c h1:TyDYClPPCN2rWM97gd1jkvzlEy6ByYEN9IMK6nUY3dY= +github.com/rancher/lasso v0.0.0-20220303220127-8cf5555ec03c/go.mod h1:T6WoUopOHBWTGjnphruTJAgoZ+dpm6llvn6GDYaa7Kw= +github.com/rancher/wrangler v0.8.8/go.mod h1:dKEaHNB4izxmPUtpq1Hvr3z3Oh+9k5pCZyFO9sUhlaY= +github.com/rancher/wrangler v0.8.11-0.20220217210408-3ecd23dfea3b h1:nFwp2dz+JHH96joqSVRYelOgmTQS9K8DSC3A/d1Gc9I= +github.com/rancher/wrangler v0.8.11-0.20220217210408-3ecd23dfea3b/go.mod h1:Lte9WjPtGYxYacIWeiS9qawvu2R4NujFU9xuXWJvc/0= +github.com/rancher/wrangler-cli v0.0.0-20211112052728-f172e9bf59af h1:Lur2DGPBdBAk5fai1xRQSrx17T3phNwdl8EdCUIzzwc= +github.com/rancher/wrangler-cli v0.0.0-20211112052728-f172e9bf59af/go.mod h1:pNIXhUzl6yYmwFedXHmOz1DF89H2SX/QOQmlwfva2m0= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rubenv/sql-migrate v1.3.0 h1:4/aYosSBTTDYKxRKdftREUV21d9hPc24mfIKZBosMsQ= -github.com/rubenv/sql-migrate v1.3.0/go.mod h1:rmTcbW9Xfv90gWPRV4stgofRrAagqmzlm6bQQzghoz0= +github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc h1:BD7uZqkN8CpjJtN/tScAKiccBikU4dlqe/gNrkRaPY4= +github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc/go.mod h1:HFLT6i9iR4QBOF5rdCyjddC9t59ArqWJV2xx+jwcCMo= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= +github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= +go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= +go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -446,23 +1106,58 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= +go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -498,21 +1193,39 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -523,22 +1236,32 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -553,8 +1276,13 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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= @@ -566,30 +1294,62 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190222072716-a9d3bda3a223/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-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -597,41 +1357,71 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -639,17 +1429,22 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190125232054-d66bd3c5d5a6/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= @@ -659,11 +1454,18 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191017205301-920acffc3e65/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -681,9 +1483,11 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -692,19 +1496,28 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -727,6 +1540,17 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -735,11 +1559,13 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -748,6 +1574,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -756,17 +1583,21 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -775,12 +1606,38 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -796,8 +1653,16 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -810,33 +1675,57 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= +gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -helm.sh/helm/v3 v3.11.0 h1:F+peaCQYbycY1FIqIQ6dAortHd/VzV5FkhMciv4Kf+c= -helm.sh/helm/v3 v3.11.0/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +helm.sh/helm/v3 v3.8.0 h1:vlQQDDQkrH4NECOFbGcwjjKyHL5Sa3xNLjMxXm7fMVo= +helm.sh/helm/v3 v3.8.0/go.mod h1:0nYPSuvuj8TTJDLRSAfbzGGbazPZsayaDpP8s9FfZT8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -844,26 +1733,102 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs= -k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ= -k8s.io/apiextensions-apiserver v0.25.4 h1:7hu9pF+xikxQuQZ7/30z/qxIPZc2J1lFElPtr7f+B6U= -k8s.io/apiextensions-apiserver v0.25.4/go.mod h1:bkSGki5YBoZWdn5pWtNIdGvDrrsRWlmnvl9a+tAw5vQ= -k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= -k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/api v0.22.3 h1:wOoES2GoSkUsdped2RB4zYypPqWtvprGoKCENTOOjP4= +k8s.io/api v0.22.3/go.mod h1:azgiXFiXqiWyLCfI62/eYBOu19rj2LKmIhFPP4+33fs= +k8s.io/apiextensions-apiserver v0.22.3 h1:bKku7MqawIbtTZc084BZoMV4fz0WZuvCnB5E+yrQXGM= +k8s.io/apiextensions-apiserver v0.22.3/go.mod h1:f4plF+CXeqI89jAXL0Ml4LI/kSAZ54JS94+XOX1sae8= +k8s.io/apimachinery v0.22.3 h1:mrvBG5CZnEfwgpVqWcrRKvdsYECTrhAR6cApAgdsflk= +k8s.io/apimachinery v0.22.3/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= +k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/apiserver v0.22.3/go.mod h1:oam7lH/F1Kto/WTamyQYrD68fS0mGUBORAFf6x/9Mxs= +k8s.io/apiserver v0.23.1/go.mod h1:Bqt0gWbeM2NefS8CjWswwd2VNAKN6lUKR85Ft4gippY= +k8s.io/cli-runtime v0.0.0-20191214191754-e6dc6d5c8724/go.mod h1:wzlq80lvjgHW9if6MlE4OIGC86MDKsy5jtl9nxz/IYY= +k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= +k8s.io/cli-runtime v0.23.1/go.mod h1:r9r8H/qfXo9w+69vwUL7LokKlLRKW5D6A8vUKCx+YL0= +k8s.io/code-generator v0.22.3 h1:24xLuKySzFl1XupMarNBkpt10q0N+73R9dF7wzJO/hE= +k8s.io/code-generator v0.22.3/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= +k8s.io/component-base v0.0.0-20191214190519-d868452632e2/go.mod h1:wupxkh1T/oUDqyTtcIjiEfpbmIHGm8By/vqpSKC6z8c= +k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/component-base v0.22.3/go.mod h1:kuybv1miLCMoOk3ebrqF93GbQHQx6W2287FC0YEQY6s= +k8s.io/component-base v0.23.1/go.mod h1:6llmap8QtJIXGDd4uIWJhAq0Op8AtQo6bDW2RrNMTeo= +k8s.io/component-helpers v0.23.1/go.mod h1:ZK24U+2oXnBPcas2KolLigVVN9g5zOzaHLkHiQMFGr0= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c h1:GohjlNKauSai7gN4wsJkeZ3WAJx4Sh+oT/b5IYn5suA= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M= -k8s.io/klog/v2 v2.90.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230127205639-68031ae9242a h1:ucju6V3yLQxqxGFVzFQLDNcfwegGDUXQikcrOqJqrP0= -k8s.io/kube-openapi v0.0.0-20230127205639-68031ae9242a/go.mod h1:/BYxry62FuDzmI+i9B+X2pqfySRmSOW2ARmj5Zbqhj0= -k8s.io/utils v0.0.0-20230115233650-391b47cb4029 h1:L8zDtT4jrxj+TaQYD0k8KNlr556WaVQylDXswKmX+dE= -k8s.io/utils v0.0.0-20230115233650-391b47cb4029/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-aggregator v0.18.0/go.mod h1:ateewQ5QbjMZF/dihEFXwaEwoA4v/mayRvzfmvb6eqI= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd/go.mod h1:9ehGcuUGjXVZh0qbYSB0vvofQw2JQe6c6cO0k4wu/Oo= +k8s.io/kubectl v0.23.1/go.mod h1:Ui7dJKdUludF8yWAOSN7JZEkOuYixX5yF6E6NjoukKE= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM= +k8s.io/metrics v0.23.1/go.mod h1:qXvsM1KANrc+ZZeFwj6Phvf0NLiC+d3RwcsLcdGc+xs= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +oras.land/oras-go v1.1.0/go.mod h1:1A7vR/0KknT2UkJVWh+xMi95I/AhK8ZrxrnUSmXN0bQ= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I= +sigs.k8s.io/cli-utils v0.16.0/go.mod h1:9Jqm9K2W6ShhCxsEuaz6HSRKKOXigPUx3ZfypGgxBLY= +sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= +sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= +sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8= +sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ= +sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io= +sigs.k8s.io/kustomize/kyaml v0.4.0/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw= +sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_register.go b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_register.go index 0597f360..3b9aa95c 100644 --- a/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_register.go +++ b/pkg/helm-locker/apis/helm.cattle.io/v1alpha1/zz_generated_register.go @@ -21,7 +21,7 @@ limitations under the License. package v1alpha1 import ( - helm "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io" + helm "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/helm-locker/codegen/main.go b/pkg/helm-locker/codegen/main.go index cad6df0d..84870058 100644 --- a/pkg/helm-locker/codegen/main.go +++ b/pkg/helm-locker/codegen/main.go @@ -3,8 +3,8 @@ package main import ( "os" - v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" - "github.com/rancher/helm-project-operator/pkg/helm-locker/crd" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-locker/crd" "github.com/sirupsen/logrus" controllergen "github.com/rancher/wrangler/pkg/controller-gen" @@ -25,7 +25,7 @@ func main() { os.Unsetenv("GOPATH") controllergen.Run(args.Options{ - OutputPackage: "github.com/rancher/helm-project-operator/pkg/helm-locker/generated", + OutputPackage: "github.com/rancher/prometheus-federator/pkg/helm-locker/generated", Boilerplate: "gen/boilerplate.go.txt", Groups: map[string]args.Group{ "helm.cattle.io": { diff --git a/pkg/helm-locker/controllers/controller.go b/pkg/helm-locker/controllers/controller.go index ea1b4105..21ec094d 100644 --- a/pkg/helm-locker/controllers/controller.go +++ b/pkg/helm-locker/controllers/controller.go @@ -5,13 +5,13 @@ import ( "errors" "time" - "github.com/rancher/helm-project-operator/pkg/helm-locker/controllers/release" - "github.com/rancher/helm-project-operator/pkg/helm-locker/generated/controllers/helm.cattle.io" - helmcontroller "github.com/rancher/helm-project-operator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" - "github.com/rancher/helm-project-operator/pkg/helm-locker/objectset" "github.com/rancher/lasso/pkg/cache" "github.com/rancher/lasso/pkg/client" "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/prometheus-federator/pkg/helm-locker/controllers/release" + "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io" + helmcontroller "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-locker/objectset" "github.com/rancher/wrangler/pkg/apply" "github.com/rancher/wrangler/pkg/generated/controllers/core" corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" diff --git a/pkg/helm-locker/controllers/release/controller.go b/pkg/helm-locker/controllers/release/controller.go index 5fde8539..3b67016a 100644 --- a/pkg/helm-locker/controllers/release/controller.go +++ b/pkg/helm-locker/controllers/release/controller.go @@ -4,13 +4,13 @@ import ( "context" "fmt" - v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" - helmcontroller "github.com/rancher/helm-project-operator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" - "github.com/rancher/helm-project-operator/pkg/helm-locker/objectset" - "github.com/rancher/helm-project-operator/pkg/helm-locker/objectset/parser" - "github.com/rancher/helm-project-operator/pkg/helm-locker/releases" - "github.com/rancher/helm-project-operator/pkg/remove" "github.com/rancher/lasso/pkg/controller" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + helmcontroller "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-locker/objectset" + "github.com/rancher/prometheus-federator/pkg/helm-locker/objectset/parser" + "github.com/rancher/prometheus-federator/pkg/helm-locker/releases" + "github.com/rancher/prometheus-federator/pkg/remove" corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" "github.com/rancher/wrangler/pkg/relatedresource" "github.com/sirupsen/logrus" diff --git a/pkg/helm-locker/controllers/release/info.go b/pkg/helm-locker/controllers/release/info.go index 02a2e8a2..6b41d421 100644 --- a/pkg/helm-locker/controllers/release/info.go +++ b/pkg/helm-locker/controllers/release/info.go @@ -1,7 +1,7 @@ package release import ( - v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" rspb "helm.sh/helm/v3/pkg/release" ) diff --git a/pkg/helm-locker/controllers/release/utils.go b/pkg/helm-locker/controllers/release/utils.go index b6b3077e..3d353b99 100644 --- a/pkg/helm-locker/controllers/release/utils.go +++ b/pkg/helm-locker/controllers/release/utils.go @@ -3,7 +3,7 @@ package release import ( "fmt" - v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" "github.com/rancher/wrangler/pkg/relatedresource" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/helm-locker/crd/crds.go b/pkg/helm-locker/crd/crds.go index 2d8a11a8..72af73c4 100644 --- a/pkg/helm-locker/crd/crds.go +++ b/pkg/helm-locker/crd/crds.go @@ -6,7 +6,7 @@ import ( "os" "path/filepath" - v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" "github.com/rancher/wrangler/pkg/crd" "github.com/rancher/wrangler/pkg/yaml" "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go index 0ca3c85a..afb52f42 100644 --- a/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go @@ -19,7 +19,7 @@ limitations under the License. package helm import ( - v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/controller" ) diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go index 307da414..a771e63f 100644 --- a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go @@ -22,7 +22,7 @@ import ( "context" "time" - v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/client" "github.com/rancher/lasso/pkg/controller" "github.com/rancher/wrangler/pkg/apply" diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go index 2e911ae3..4f2c8bd3 100644 --- a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go @@ -19,7 +19,7 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/rancher/helm-project-operator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/controller" "github.com/rancher/wrangler/pkg/schemes" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/helm-locker/objectset/cache.go b/pkg/helm-locker/objectset/cache.go index 8a53321d..a3180da2 100644 --- a/pkg/helm-locker/objectset/cache.go +++ b/pkg/helm-locker/objectset/cache.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "github.com/rancher/helm-project-operator/pkg/helm-locker/gvk" "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/prometheus-federator/pkg/helm-locker/gvk" "github.com/rancher/wrangler/pkg/objectset" "github.com/rancher/wrangler/pkg/relatedresource" "github.com/sirupsen/logrus" diff --git a/pkg/helm-locker/objectset/controller.go b/pkg/helm-locker/objectset/controller.go index 7815a631..79df7cbf 100644 --- a/pkg/helm-locker/objectset/controller.go +++ b/pkg/helm-locker/objectset/controller.go @@ -4,9 +4,9 @@ import ( "context" "time" - "github.com/rancher/helm-project-operator/pkg/helm-locker/gvk" - "github.com/rancher/helm-project-operator/pkg/helm-locker/informerfactory" "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/prometheus-federator/pkg/helm-locker/gvk" + "github.com/rancher/prometheus-federator/pkg/helm-locker/informerfactory" "github.com/rancher/wrangler/pkg/apply" "github.com/rancher/wrangler/pkg/start" "k8s.io/client-go/discovery" diff --git a/pkg/helm-locker/objectset/handler.go b/pkg/helm-locker/objectset/handler.go index 217cc03b..65a23fa8 100644 --- a/pkg/helm-locker/objectset/handler.go +++ b/pkg/helm-locker/objectset/handler.go @@ -3,8 +3,8 @@ package objectset import ( "fmt" - "github.com/rancher/helm-project-operator/pkg/helm-locker/gvk" "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/prometheus-federator/pkg/helm-locker/gvk" "github.com/rancher/wrangler/pkg/apply" "github.com/rancher/wrangler/pkg/relatedresource" "github.com/sirupsen/logrus" diff --git a/pkg/remove/handler.go b/pkg/remove/handler.go new file mode 100644 index 00000000..891cfb44 --- /dev/null +++ b/pkg/remove/handler.go @@ -0,0 +1,39 @@ +package remove + +import ( + "context" + + "github.com/rancher/wrangler/pkg/generic" + "k8s.io/apimachinery/pkg/runtime" +) + +// Controller is an interface that allows the ScopedOnRemoveHandler to register a generic RemoveHandler +type Controller interface { + AddGenericHandler(ctx context.Context, name string, handler generic.Handler) + Updater() generic.Updater +} + +// ScopeFunc is a function that determines whether the ScopedOnRemoveHandler should manage the lifecycle of the given object +type ScopeFunc func(key string, obj runtime.Object) (bool, error) + +// RegisterScopedOnRemoveHandler registers a handler that does the same thing as an OnRemove handler but only applies finalizers or sync logic +// to objects that pass the provided scopeFunc; this ensures that finalizers are not added to all resources across an entire cluster but are +// instead only scoped to resources that this controller is meant to watch. +// +// TODO: move this to rancher/wrangler as a generic construct to be used across multiple controllers as part of the auto-generated code +func RegisterScopedOnRemoveHandler(ctx context.Context, controller Controller, name string, scopeFunc ScopeFunc, handler generic.Handler) { + onRemove := generic.NewRemoveHandler(name, controller.Updater(), handler) + controller.AddGenericHandler(ctx, name, func(key string, obj runtime.Object) (runtime.Object, error) { + if obj == nil { + return nil, nil + } + isScoped, err := scopeFunc(key, obj) + if err != nil { + return obj, err + } + if !isScoped { + return obj, nil + } + return onRemove(key, obj) + }) +} From 90916b8c04272955fc45ed2da7967e3bd9e7c81d Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 17:22:44 -0400 Subject: [PATCH 10/45] Ensure go generate works with all local package --- crds/helm-locker/crds.yaml | 85 +++++++++++++++++++ generate.go | 9 ++ go.mod | 1 + .../controllers/helm.cattle.io/interface.go | 2 +- .../helm.cattle.io/v1alpha1/helmrelease.go | 2 +- .../helm.cattle.io/v1alpha1/interface.go | 2 +- 6 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 crds/helm-locker/crds.yaml create mode 100644 generate.go diff --git a/crds/helm-locker/crds.yaml b/crds/helm-locker/crds.yaml new file mode 100644 index 00000000..79588258 --- /dev/null +++ b/crds/helm-locker/crds.yaml @@ -0,0 +1,85 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: helmreleases.helm.cattle.io +spec: + group: helm.cattle.io + names: + kind: HelmRelease + plural: helmreleases + singular: helmrelease + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.release.name + name: Release Name + type: string + - jsonPath: .spec.release.namespace + name: Release Namespace + type: string + - jsonPath: .status.version + name: Version + type: string + - jsonPath: .status.state + name: State + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + release: + properties: + name: + nullable: true + type: string + namespace: + nullable: true + type: string + type: object + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + nullable: true + type: string + lastUpdateTime: + nullable: true + type: string + message: + nullable: true + type: string + reason: + nullable: true + type: string + status: + nullable: true + type: string + type: + nullable: true + type: string + type: object + nullable: true + type: array + description: + nullable: true + type: string + notes: + nullable: true + type: string + state: + nullable: true + type: string + version: + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/generate.go b/generate.go new file mode 100644 index 00000000..12622a80 --- /dev/null +++ b/generate.go @@ -0,0 +1,9 @@ +//go:generate go run pkg/helm-locker/codegen/cleanup/main.go +//go:generate go run pkg/helm-locker/codegen/main.go +//go:generate go run ./pkg/helm-locker/codegen crds ./crds/helm-locker/crds.yaml + +// //go:generate go run pkg/helm-project-operator/codegen/cleanup/main.go +// //go:generate go run pkg/helm-project-operator/codegen/main.go +// //go:generate go run ./pkg/helm-project-operator/codegen crds ./crds/helm-project-operator ./crds/helm-project-operator + +package main diff --git a/go.mod b/go.mod index 389a5f43..1b698838 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/rancher/prometheus-federator go 1.22.3 replace ( + github.com/rancher/helm-project-operator/pkg/helm-locker => ./pkg/helm-locker k8s.io/api => k8s.io/api v0.22.3 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.22.3 k8s.io/apimachinery => k8s.io/apimachinery v0.22.3 diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go index afb52f42..64537811 100644 --- a/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/interface.go @@ -19,8 +19,8 @@ limitations under the License. package helm import ( - v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/controller" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" ) type Interface interface { diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go index a771e63f..a68cb6ef 100644 --- a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/helmrelease.go @@ -22,9 +22,9 @@ import ( "context" "time" - v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/client" "github.com/rancher/lasso/pkg/controller" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" "github.com/rancher/wrangler/pkg/apply" "github.com/rancher/wrangler/pkg/condition" "github.com/rancher/wrangler/pkg/generic" diff --git a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go index 4f2c8bd3..48bfa1d4 100644 --- a/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go +++ b/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1/interface.go @@ -19,8 +19,8 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/controller" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" "github.com/rancher/wrangler/pkg/schemes" "k8s.io/apimachinery/pkg/runtime/schema" ) From 18bb73a725f8ad5ebebaecf9996aba33dbc2d854 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 17:48:06 -0400 Subject: [PATCH 11/45] Add tag for prom-fed cli entry --- cmd/prometheus-federator/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/prometheus-federator/main.go b/cmd/prometheus-federator/main.go index 322839a5..ddcbe1c3 100644 --- a/cmd/prometheus-federator/main.go +++ b/cmd/prometheus-federator/main.go @@ -1,3 +1,5 @@ +//go:build prometheus_federator + package main import ( From b46d13d833bd5ff5892426b97d3cce37299a51a6 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 17:49:36 -0400 Subject: [PATCH 12/45] correct issue with versions being set --- scripts/build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build b/scripts/build index c8606bb4..bcf4436a 100755 --- a/scripts/build +++ b/scripts/build @@ -15,8 +15,8 @@ if [ "$(uname)" = "Linux" ]; then OTHER_LINKFLAGS="-extldflags -static -s" fi -LINKFLAGS="-X github.com/rancher/${BUILD_TARGET}/pkg/version.Version=$VERSION" -LINKFLAGS="-X github.com/rancher/${BUILD_TARGET}/pkg/version.GitCommit=$COMMIT $LINKFLAGS" +LINKFLAGS="-X github.com/rancher/prometheus-federator/pkg/version.Version=$VERSION" +LINKFLAGS="-X github.com/rancher/prometheus-federator/pkg/version.GitCommit=$COMMIT $LINKFLAGS" BIN_DEST="build/bin/${BUILD_TARGET}" ARCHES=( "$ARCH" ) From 3ff084efe3862004d6b1de3c39515900f7be3a18 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 14:37:18 -0400 Subject: [PATCH 13/45] Add helm-locker and helm-project-operator chart packages correct name --- packages/helm-locker/charts/Chart.yaml | 18 ++ packages/helm-locker/charts/README.md | 60 +++++ .../helm-locker/charts/templates/NOTES.txt | 3 + .../helm-locker/charts/templates/_helpers.tpl | 66 +++++ .../charts/templates/clusterrole.yaml | 58 +++++ .../charts/templates/deployment.yaml | 55 +++++ .../charts/templates/hardened.yaml | 121 ++++++++++ .../helm-locker/charts/templates/psp.yaml | 68 ++++++ .../helm-locker/charts/templates/rbac.yaml | 27 +++ packages/helm-locker/charts/values.yaml | 86 +++++++ packages/helm-locker/package.yaml | 2 + .../helm-project-operator/charts/Chart.yaml | 18 ++ .../helm-project-operator/charts/README.md | 77 ++++++ .../charts/app-readme.md | 20 ++ .../charts/questions.yaml | 43 ++++ .../charts/templates/NOTES.txt | 2 + .../charts/templates/_helpers.tpl | 66 +++++ .../charts/templates/cleanup.yaml | 82 +++++++ .../charts/templates/clusterrole.yaml | 57 +++++ .../charts/templates/configmap.yaml | 14 ++ .../charts/templates/deployment.yaml | 126 ++++++++++ .../charts/templates/psp.yaml | 68 ++++++ .../charts/templates/rbac.yaml | 32 +++ .../system-namespaces-configmap.yaml | 62 +++++ .../templates/validate-psp-install.yaml | 7 + .../helm-project-operator/charts/values.yaml | 228 ++++++++++++++++++ packages/helm-project-operator/package.yaml | 2 + 27 files changed, 1468 insertions(+) create mode 100644 packages/helm-locker/charts/Chart.yaml create mode 100644 packages/helm-locker/charts/README.md create mode 100644 packages/helm-locker/charts/templates/NOTES.txt create mode 100644 packages/helm-locker/charts/templates/_helpers.tpl create mode 100644 packages/helm-locker/charts/templates/clusterrole.yaml create mode 100644 packages/helm-locker/charts/templates/deployment.yaml create mode 100644 packages/helm-locker/charts/templates/hardened.yaml create mode 100644 packages/helm-locker/charts/templates/psp.yaml create mode 100644 packages/helm-locker/charts/templates/rbac.yaml create mode 100644 packages/helm-locker/charts/values.yaml create mode 100644 packages/helm-locker/package.yaml create mode 100644 packages/helm-project-operator/charts/Chart.yaml create mode 100644 packages/helm-project-operator/charts/README.md create mode 100644 packages/helm-project-operator/charts/app-readme.md create mode 100644 packages/helm-project-operator/charts/questions.yaml create mode 100644 packages/helm-project-operator/charts/templates/NOTES.txt create mode 100644 packages/helm-project-operator/charts/templates/_helpers.tpl create mode 100644 packages/helm-project-operator/charts/templates/cleanup.yaml create mode 100644 packages/helm-project-operator/charts/templates/clusterrole.yaml create mode 100644 packages/helm-project-operator/charts/templates/configmap.yaml create mode 100644 packages/helm-project-operator/charts/templates/deployment.yaml create mode 100644 packages/helm-project-operator/charts/templates/psp.yaml create mode 100644 packages/helm-project-operator/charts/templates/rbac.yaml create mode 100644 packages/helm-project-operator/charts/templates/system-namespaces-configmap.yaml create mode 100644 packages/helm-project-operator/charts/templates/validate-psp-install.yaml create mode 100644 packages/helm-project-operator/charts/values.yaml create mode 100644 packages/helm-project-operator/package.yaml diff --git a/packages/helm-locker/charts/Chart.yaml b/packages/helm-locker/charts/Chart.yaml new file mode 100644 index 00000000..41f8f569 --- /dev/null +++ b/packages/helm-locker/charts/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: helm-locker +description: Helm Locker +version: 0.0.2 +appVersion: 0.0.2 +annotations: + catalog.cattle.io/certified: rancher + catalog.cattle.io/display-name: Helm Locker + catalog.cattle.io/kube-version: '>=1.16.0-0' + catalog.cattle.io/namespace: cattle-helm-system + catalog.cattle.io/permits-os: linux,windows + catalog.cattle.io/provides-gvr: helm.cattle.io.helmrelease/v1alpha1 + catalog.cattle.io/rancher-version: '>= 2.6.0-0' + catalog.cattle.io/release-name: helm-locker + catalog.cattle.io/os: linux,windows +maintainers: + - email: dan.pock@suse.com + name: Dan Pock diff --git a/packages/helm-locker/charts/README.md b/packages/helm-locker/charts/README.md new file mode 100644 index 00000000..f81c61fe --- /dev/null +++ b/packages/helm-locker/charts/README.md @@ -0,0 +1,60 @@ +helm-locker +======== + +Helm Locker is a Kubernetes operator that prevents resource drift on (i.e. "locks") Kubernetes objects that are tracked by Helm 3 releases. + +Once installed, a user can create a `HelmRelease` CR in the `Helm Release Registration Namespace` (default: `cattle-helm-system`) by providing: +1. The name of a Helm 3 release +2. The namespace that contains the Helm Release Secret (supplied as `--namespace` on the `helm install` command that created the release) + +Once created, the Helm Locker controllers will watch all resources tracked by the Helm Release Secret and automatically revert any changes to the persisted resources that were not made through Helm (e.g. changes that were directly applied via `kubectl` or other controllers). + +## Who needs Helm Locker? + +Anyone who would like to declaratively manage resources deployed by existing Helm chart releases. + +## How is this different from projects like `fluxcd/helm-controller`? + +Projects like [`fluxcd/helm-controller`](https://github.com/fluxcd/helm-controller) allow users to declaratively manage **Helm charts from deployment to release**, whereas this project only allows you lock an **existing** Helm chart release; as a result, the scope of this project is much more narrow than what is offered by `fluxcd/helm-controller` and should be integrable with any solution that produces Helm releases. + +If you are looking for a larger, more opinionated solution that also has features around **how** Helm charts should be deployed onto a cluster (e.g. from a `GitRepository` or `Bucket` or `HelmRepository`), this is not the project for you. + +However, if you are looking for something light-weight that simply guarentees that **Helm is the only way to modify resources tracked by Helm releases**, this is a good solution to use. + +## How does Helm Locker know whether a release was changed by Helm or by another source? + +In order to prevent multiple Helm instances from performing the same upgrade at the same time, Helm 3 will always first update the `info.status` field on a Helm Release Secret from `deployed` to another state (e.g. `pending-upgrade`, `pending-install`, `uninstalling`, etc.) before performing the operation; once the operation is complete, the Helm Release Secret is expected to be reverted back to `deployed`. + +Therefore, if Helm Locker observes a Helm Release Secret tied to a `HelmRelease` has been updated, it will check to see what the current status of the release is; if the release is anything but `deployed`, Helm Locker will not perform any operations on the resources tracked by this release, which will allow upgrades to occur as expected. + +However, once a release is `deployed`, if what is tracked in the Helm secret is different than what is currently installed onto the cluster, Helm Locker will revert all resources back to what was tracked by the Helm release (in case a change was made to the resource tracked by the Helm Release while the release was being modified). + +## Debugging + +### How do I manually inspect the content of the Helm Release Secret to debug a possible Helm Locker issue? + +Identify the release namespace (`RELEASE_NAMESPACE`), release name (`RELEASE_NAME`), and release version (`RELEASE_VERSION`) that identifies the Secret used by Helm to store the release data. Then, with access to your Kubernetes cluster via `kubectl`, run the following command (e.g. run base64 decode, base64 decode, gzip decompress the .data.release of the Secret): + +```bash +RELEASE_NAMESPACE=default +RELEASE_NAME=test +RELEASE_VERSION=v1 + +# Magic one-liner! jq call is optional... +kubectl get secrets -n ${RELEASE_NAMESPACE} sh.helm.release.v1.${RELEASE_NAME}.${RELEASE_VERSION} -o=jsonpath='{ .data.release }' | base64 -d | base64 -d | gunzip -c | jq -r '.' +``` + +## License +Copyright (c) 2020 [Rancher Labs, Inc.](http://rancher.com) + +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](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/packages/helm-locker/charts/templates/NOTES.txt b/packages/helm-locker/charts/templates/NOTES.txt new file mode 100644 index 00000000..cf19a8c6 --- /dev/null +++ b/packages/helm-locker/charts/templates/NOTES.txt @@ -0,0 +1,3 @@ +{{ $.Chart.Name }} has been installed. Check its status by running: + kubectl --namespace {{ template "helm-locker.namespace" . }} get pods -l "release={{ $.Release.Name }}" + diff --git a/packages/helm-locker/charts/templates/_helpers.tpl b/packages/helm-locker/charts/templates/_helpers.tpl new file mode 100644 index 00000000..02f66765 --- /dev/null +++ b/packages/helm-locker/charts/templates/_helpers.tpl @@ -0,0 +1,66 @@ +# Rancher +{{- define "system_default_registry" -}} +{{- if .Values.global.cattle.systemDefaultRegistry -}} +{{- printf "%s/" .Values.global.cattle.systemDefaultRegistry -}} +{{- end -}} +{{- end -}} + +# Windows Support + +{{/* +Windows cluster will add default taint for linux nodes, +add below linux tolerations to workloads could be scheduled to those linux nodes +*/}} + +{{- define "linux-node-tolerations" -}} +- key: "cattle.io/os" + value: "linux" + effect: "NoSchedule" + operator: "Equal" +{{- end -}} + +{{- define "linux-node-selector" -}} +{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion -}} +beta.kubernetes.io/os: linux +{{- else -}} +kubernetes.io/os: linux +{{- end -}} +{{- end -}} + +# Helm Locker + +{{/* vim: set filetype=mustache: */}} +{{/* Expand the name of the chart. This is suffixed with -alertmanager, which means subtract 13 from longest 63 available */}} +{{- define "helm-locker.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 50 | trimSuffix "-" -}} +{{- end }} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "helm-locker.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* Create chart name and version as used by the chart label. */}} +{{- define "helm-locker.chartref" -}} +{{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}} +{{- end }} + +{{/* Generate basic labels */}} +{{- define "helm-locker.labels" }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/version: "{{ replace "+" "_" .Chart.Version }}" +app.kubernetes.io/part-of: {{ template "helm-locker.name" . }} +chart: {{ template "helm-locker.chartref" . }} +release: {{ $.Release.Name | quote }} +heritage: {{ $.Release.Service | quote }} +{{- if .Values.commonLabels}} +{{ toYaml .Values.commonLabels }} +{{- end }} +{{- end }} diff --git a/packages/helm-locker/charts/templates/clusterrole.yaml b/packages/helm-locker/charts/templates/clusterrole.yaml new file mode 100644 index 00000000..b174e4a6 --- /dev/null +++ b/packages/helm-locker/charts/templates/clusterrole.yaml @@ -0,0 +1,58 @@ +{{- if and .Values.global.rbac.create .Values.global.rbac.userRoles.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "helm-locker.name" . }}-admin + labels: {{ include "helm-locker.labels" . | nindent 4 }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + rbac.authorization.k8s.io/aggregate-to-admin: "true" + {{- end }} +rules: +- apiGroups: + - helm.cattle.io + resources: + - helmreleases + - helmreleases/finalizers + - helmreleases/status + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "helm-locker.name" . }}-edit + labels: {{ include "helm-locker.labels" . | nindent 4 }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + rbac.authorization.k8s.io/aggregate-to-edit: "true" + {{- end }} +rules: +- apiGroups: + - helm.cattle.io + resources: + - helmreleases + - helmreleases/status + verbs: + # Since Helm Locker executes with cluster-admin privileges, only an admin gets mutating permissions for them + - 'get' + - 'list' + - 'watch' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "helm-locker.name" . }}-view + labels: {{ include "helm-locker.labels" . | nindent 4 }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + rbac.authorization.k8s.io/aggregate-to-view: "true" + {{- end }} +rules: +- apiGroups: + - helm.cattle.io + resources: + - helmreleases + - helmreleases/status + verbs: + - 'get' + - 'list' + - 'watch' +{{- end }} diff --git a/packages/helm-locker/charts/templates/deployment.yaml b/packages/helm-locker/charts/templates/deployment.yaml new file mode 100644 index 00000000..a21edb16 --- /dev/null +++ b/packages/helm-locker/charts/templates/deployment.yaml @@ -0,0 +1,55 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "helm-locker.name" . }} + namespace: {{ template "helm-locker.namespace" . }} + labels: {{ include "helm-locker.labels" . | nindent 4 }} + app: {{ template "helm-locker.name" . }} +spec: + {{- if .Values.replicas }} + replicas: {{ .Values.replicas }} + {{- end }} + selector: + matchLabels: + app: {{ template "helm-locker.name" . }} + release: {{ $.Release.Name | quote }} + template: + metadata: + labels: {{ include "helm-locker.labels" . | nindent 8 }} + app: {{ template "helm-locker.name" . }} + spec: + containers: + - name: {{ template "helm-locker.name" . }} + image: "{{ template "system_default_registry" . }}{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + args: + - {{ template "helm-locker.name" . }} + - --namespace={{ template "helm-locker.namespace" . }} + - --controller-name={{ template "helm-locker.name" . }} +{{- if .Values.debug }} + - --debug + - --debug-level={{ .Values.debugLevel }} +{{- end }} +{{- if .Values.additionalArgs }} +{{- toYaml .Values.additionalArgs | nindent 10 }} +{{- end }} + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName +{{- if .Values.resources }} + resources: {{ toYaml .Values.resources | nindent 12 }} +{{- end }} + serviceAccountName: {{ template "helm-locker.name" . }} +{{- if .Values.securityContext }} + securityContext: {{ toYaml .Values.securityContext | indent 8 }} +{{- end }} + nodeSelector: {{ include "linux-node-selector" . | nindent 8 }} +{{- if .Values.nodeSelector }} +{{- toYaml .Values.nodeSelector | nindent 8 }} +{{- end }} + tolerations: {{ include "linux-node-tolerations" . | nindent 8 }} +{{- if .Values.tolerations }} +{{- toYaml .Values.tolerations | nindent 8 }} +{{- end }} \ No newline at end of file diff --git a/packages/helm-locker/charts/templates/hardened.yaml b/packages/helm-locker/charts/templates/hardened.yaml new file mode 100644 index 00000000..1abaeecc --- /dev/null +++ b/packages/helm-locker/charts/templates/hardened.yaml @@ -0,0 +1,121 @@ +{{- $namespaces := dict "_0" .Release.Namespace -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "helm-locker.name" . }}-patch-sa + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "helm-locker.name" . }}-patch-sa + annotations: + "helm.sh/hook": post-install, post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded, before-hook-creation +spec: + template: + metadata: + name: {{ template "helm-locker.name" . }}-patch-sa + labels: + app: {{ template "helm-locker.name" . }}-patch-sa + spec: + serviceAccountName: {{ template "helm-locker.name" . }}-patch-sa + securityContext: + runAsNonRoot: true + runAsUser: 1000 + restartPolicy: Never + nodeSelector: {{ include "linux-node-selector" . | nindent 8 }} + tolerations: {{ include "linux-node-tolerations" . | nindent 8 }} + containers: + {{- range $_, $ns := $namespaces }} + - name: patch-sa-{{ $ns }} + image: {{ template "system_default_registry" $ }}{{ $.Values.global.kubectl.repository }}:{{ $.Values.global.kubectl.tag }} + imagePullPolicy: {{ $.Values.global.kubectl.pullPolicy }} + command: ["kubectl", "patch", "serviceaccount", "default", "-p", "{\"automountServiceAccountToken\": false}"] + args: ["-n", "{{ $ns }}"] + {{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "helm-locker.name" . }}-patch-sa + labels: + app: {{ template "helm-locker.name" . }}-patch-sa +rules: +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: ['get', 'patch'] +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "helm-locker.name" . }}-patch-sa +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "helm-locker.name" . }}-patch-sa + labels: + app: {{ template "helm-locker.name" . }}-patch-sa +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "helm-locker.name" . }}-patch-sa +subjects: +- kind: ServiceAccount + name: {{ template "helm-locker.name" . }}-patch-sa + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "helm-locker.name" . }}-patch-sa + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "helm-locker.name" . }}-patch-sa +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "helm-locker.name" . }}-patch-sa + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "helm-locker.name" . }}-patch-sa +spec: + privileged: false + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'MustRunAsNonRoot' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + readOnlyRootFilesystem: false + volumes: + - 'secret' +{{- range $_, $ns := $namespaces }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-allow-all + namespace: {{ $ns }} +spec: + podSelector: {} + ingress: + - {} + egress: + - {} + policyTypes: + - Ingress + - Egress +{{- end }} diff --git a/packages/helm-locker/charts/templates/psp.yaml b/packages/helm-locker/charts/templates/psp.yaml new file mode 100644 index 00000000..16ff49e7 --- /dev/null +++ b/packages/helm-locker/charts/templates/psp.yaml @@ -0,0 +1,68 @@ +{{- if .Values.global.rbac.pspEnabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "helm-locker.name" . }}-psp + namespace: {{ template "helm-locker.namespace" . }} + labels: {{ include "helm-locker.labels" . | nindent 4 }} + app: {{ template "helm-locker.name" . }} +{{- if .Values.global.rbac.pspAnnotations }} + annotations: {{ toYaml .Values.global.rbac.pspAnnotations | nindent 4 }} +{{- end }} +spec: + privileged: false + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Permits the container to run with root privileges as well. + rule: 'RunAsAny' + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + readOnlyRootFilesystem: false +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "helm-locker.name" . }}-psp + labels: {{ include "helm-locker.labels" . | nindent 4 }} + app: {{ template "helm-locker.name" . }} +rules: +{{- if semverCompare "> 1.15.0-0" .Capabilities.KubeVersion.GitVersion }} +- apiGroups: ['policy'] +{{- else }} +- apiGroups: ['extensions'] +{{- end }} + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "helm-locker.name" . }}-psp +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "helm-locker.name" . }}-psp + labels: {{ include "helm-locker.labels" . | nindent 4 }} + app: {{ template "helm-locker.name" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "helm-locker.name" . }}-psp +subjects: + - kind: ServiceAccount + name: {{ template "helm-locker.name" . }} + namespace: {{ template "helm-locker.namespace" . }} +{{- end }} diff --git a/packages/helm-locker/charts/templates/rbac.yaml b/packages/helm-locker/charts/templates/rbac.yaml new file mode 100644 index 00000000..3b55ffb2 --- /dev/null +++ b/packages/helm-locker/charts/templates/rbac.yaml @@ -0,0 +1,27 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "helm-locker.name" . }} + labels: {{ include "helm-locker.labels" . | nindent 4 }} + app: {{ template "helm-locker.name" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + # By default, Helm Locker does not know what it will need permissions to be able to lock, so it is granted a cluster-admin role by default + name: "cluster-admin" +subjects: +- kind: ServiceAccount + name: {{ template "helm-locker.name" . }} + namespace: {{ template "helm-locker.namespace" . }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "helm-locker.name" . }} + namespace: {{ template "helm-locker.namespace" . }} + labels: {{ include "helm-locker.labels" . | nindent 4 }} + app: {{ template "helm-locker.name" . }} +{{- if .Values.global.imagePullSecrets }} +imagePullSecrets: {{ toYaml .Values.global.imagePullSecrets | nindent 2 }} +{{- end }} + diff --git a/packages/helm-locker/charts/values.yaml b/packages/helm-locker/charts/values.yaml new file mode 100644 index 00000000..0742dcc9 --- /dev/null +++ b/packages/helm-locker/charts/values.yaml @@ -0,0 +1,86 @@ +# Default values for helm-locker. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# Helm Locker Configuration + +global: + cattle: + systemDefaultRegistry: "" + + kubectl: + repository: rancher/kubectl + tag: v1.20.2 + pullPolicy: IfNotPresent + + rbac: + ## Create RBAC resources for ServiceAccounts and users + ## + create: true + + userRoles: + ## Create default user ClusterRoles to allow users to interact with HelmReleases + create: true + ## Aggregate default user ClusterRoles into default k8s ClusterRoles + aggregateToDefaultRoles: true + + pspEnabled: true + pspAnnotations: {} + ## Specify pod annotations + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#apparmor + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#sysctl + ## + # seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' + # seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' + # apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' + + ## Reference to one or more secrets to be used when pulling images + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + imagePullSecrets: [] + # - name: "image-pull-secret" + +nameOverride: "" + +namespaceOverride: "" + +replicas: 1 + +image: + repository: ghcr.io/rancher/helm-project-operator/helm-locker + tag: v0.3.0 + pullPolicy: IfNotPresent + +# Additional arguments to be passed into the Helm Locker image +additionalArgs: [] + +## Define which Nodes the Pods are scheduled on. +## ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +## Tolerations for use with node taints +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] +# - key: "key" +# operator: "Equal" +# value: "value" +# effect: "NoSchedule" + +resources: {} + # limits: + # memory: 500Mi + # cpu: 1000m + # requests: + # memory: 100Mi + # cpu: 100m + +securityContext: {} + # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true + +debug: false +debugLevel: 0 + diff --git a/packages/helm-locker/package.yaml b/packages/helm-locker/package.yaml new file mode 100644 index 00000000..23845fcd --- /dev/null +++ b/packages/helm-locker/package.yaml @@ -0,0 +1,2 @@ +url: local +doNotRelease: true diff --git a/packages/helm-project-operator/charts/Chart.yaml b/packages/helm-project-operator/charts/Chart.yaml new file mode 100644 index 00000000..b2f1d559 --- /dev/null +++ b/packages/helm-project-operator/charts/Chart.yaml @@ -0,0 +1,18 @@ +annotations: + catalog.cattle.io/certified: rancher + catalog.cattle.io/display-name: Helm Project Operator + catalog.cattle.io/kube-version: '>=1.16.0-0' + catalog.cattle.io/namespace: cattle-helm-system + catalog.cattle.io/os: linux,windows + catalog.cattle.io/permits-os: linux,windows + catalog.cattle.io/provides-gvr: helm.cattle.io.projecthelmchart/v1alpha1 + catalog.cattle.io/rancher-version: '>= 2.6.0-0' + catalog.cattle.io/release-name: helm-project-operator +apiVersion: v2 +appVersion: v0.3.1 +description: Helm Project Operator +maintainers: +- email: dan.pock@suse.com + name: Dan Pock +name: helm-project-operator +version: 0.3.1 diff --git a/packages/helm-project-operator/charts/README.md b/packages/helm-project-operator/charts/README.md new file mode 100644 index 00000000..9623ff22 --- /dev/null +++ b/packages/helm-project-operator/charts/README.md @@ -0,0 +1,77 @@ +# Helm Project Operator + +## How does the operator work? + +1. On deploying a Helm Project Operator, users can create ProjectHelmCharts CRs with `spec.helmApiVersion` set to `dummy.cattle.io/v1alpha1` in a **Project Registration Namespace (`cattle-project-`)**. +2. On seeing each ProjectHelmChartCR, the operator will automatically deploy the embedded Helm chart on the Project Owner's behalf in the **Project Release Namespace (`cattle-project--dummy`)** based on a HelmChart CR and a HelmRelease CR automatically created by the ProjectHelmChart controller in the **Operator / System Namespace**. +3. RBAC will automatically be assigned in the Project Release Namespace to allow users to based on Role created in the Project Release Namespace with a given set of labels; this will be based on RBAC defined on the Project Registration Namespace against the [default Kubernetes user-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) (see below for more information about configuring RBAC). + +### What is a Project? + +In Helm Project Operator, a Project is a group of namespaces that can be identified by a `metav1.LabelSelector`; by default, the label used to identify projects is `field.cattle.io/projectId`, the label used to identify namespaces that are contained within a given [Rancher](https://rancher.com/) Project. + +### What is a ProjectHelmChart? + +A ProjectHelmChart is an instance of a (project-scoped) Helm chart deployed on behalf of a user who has permissions to create ProjectHelmChart resources in a Project Registration namespace. + +Generally, the best way to think about the ProjectHelmChart model is by comparing it to two other models: +1. Managed Kubernetes providers (EKS, GKE, AKS, etc.): in this model, a user has the ability to say "I want a Kubernetes cluster" but the underlying cloud provider is responsible for provisioning the infrastructure and offering **limited view and access** of the underlying resources created on their behalf; similarly, Helm Project Operator allows a Project Owner to say "I want this Helm chart deployed", but the underlying Operator is responsible for "provisioning" (deploying) the Helm chart and offering **limited view and access** of the underlying Kubernetes resources created on their behalf (based on configuring "least-privilege" Kubernetes RBAC for the Project Owners / Members in the newly created Project Release Namespace). +2. Dynamically-provisioned Persistent Volumes: in this model, a single resource (PersistentVolume) exists that allows you to specify a Storage Class that actually implements provisioning the underlying storage via a Storage Class Provisioner (e.g. Longhorn). Similarly, the ProjectHelmChart exists that allows you to specify a `spec.helmApiVersion` ("storage class") that actually implements deploying the underlying Helm chart via a Helm Project Operator (e.g. [`rancher/prometheus-federator`](https://github.com/rancher/prometheus-federator)). + +### Configuring the Helm release created by a ProjectHelmChart + +The `spec.values` of this ProjectHelmChart resources will correspond to the `values.yaml` override to be supplied to the underlying Helm chart deployed by the operator on the user's behalf; to see the underlying chart's `values.yaml` spec, either: +- View to the chart's definition located at [`rancher/helm-project-operator` under `charts/project-operator-example`](https://github.com/rancher/helm-project-operator/blob/main/charts/project-operator-example) (where the chart version will be tied to the version of this operator) +- Look for the ConfigMap named `dummy.cattle.io.v1alpha1` that is automatically created in each Project Registration Namespace, which will contain both the `values.yaml` and `questions.yaml` that was used to configure the chart (which was embedded directly into the `helm-project-operator` binary). + +### Namespaces + +All Helm Project Operators have three different classifications of namespaces that the operator looks out for: +1. **Operator / System Namespace**: this is the namespace that the operator is deployed into (e.g. `cattle-helm-system`). This namespace will contain all HelmCharts and HelmReleases for all ProjectHelmCharts watched by this operator. **Only Cluster Admins should have access to this namespace.** +2. **Project Registration Namespace (`cattle-project-`)**: this is the set of namespaces that the operator watches for ProjectHelmCharts within. The RoleBindings and ClusterRoleBindings that apply to this namespace will also be the source of truth for the auto-assigned RBAC created in the Project Release Namespace (see more details below). **Project Owners (admin), Project Members (edit), and Read-Only Members (view) should have access to this namespace**. +> Note: Project Registration Namespaces will be auto-generated by the operator and imported into the Project it is tied to if `.Values.global.cattle.projectLabel` is provided (which is set to `field.cattle.io/projectId` by default); this indicates that a Project Registration Namespace should be created by the operator if at least one namespace is observed with that label. The operator will not let these namespaces be deleted unless either all namespaces with that label are gone (e.g. this is the last namespace in that project, in which case the namespace will be marked with the label `"helm.cattle.io/helm-project-operator-orphaned": "true"`, which signals that it can be deleted) or it is no longer watching that project (because the project ID was provided under `.Values.helmProjectOperator.otherSystemProjectLabelValues`, which serves as a denylist for Projects). These namespaces will also never be auto-deleted to avoid destroying user data; it is recommended that users clean up these namespaces manually if desired on creating or deleting a project +> Note: if `.Values.global.cattle.projectLabel` is not provided, the Operator / System Namespace will also be the Project Registration Namespace +3. **Project Release Namespace (`cattle-project--dummy`)**: this is the set of namespaces that the operator deploys Helm charts within on behalf of a ProjectHelmChart; the operator will also automatically assign RBAC to Roles created in this namespace by the Helm charts based on bindings found in the Project Registration Namespace. **Only Cluster Admins should have access to this namespace; Project Owners (admin), Project Members (edit), and Read-Only Members (view) will be assigned limited access to this namespace by the deployed Helm Chart and Helm Project Operator.** +> Note: Project Release Namespaces are automatically deployed and imported into the project whose ID is specified under `.Values.helmProjectOperator.projectReleaseNamespaces.labelValue` (which defaults to the value of `.Values.global.cattle.systemProjectId` if not specified) whenever a ProjectHelmChart is specified in a Project Registration Namespace +> Note: Project Release Namespaces follow the same orphaning conventions as Project Registration Namespaces (see note above) +> Note: if `.Values.projectReleaseNamespaces.enabled` is false, the Project Release Namespace will be the same as the Project Registration Namespace + +### Helm Resources (HelmChart, HelmRelease) + +On deploying a ProjectHelmChart, the Helm Project Operator will automatically create and manage two child custom resources that manage the underlying Helm resources in turn: +- A HelmChart CR (managed via an embedded [k3s-io/helm-contoller](https://github.com/k3s-io/helm-controller) in the operator): this custom resource automatically creates a Job in the same namespace that triggers a `helm install`, `helm upgrade`, or `helm uninstall` depending on the change applied to the HelmChart CR; this CR is automatically updated on changes to the ProjectHelmChart (e.g. modifying the values.yaml) or changes to the underlying Project definition (e.g. adding or removing namespaces from a project). +> **Important Note: If a ProjectHelmChart is not deploying or updating the underlying Project Monitoring Stack for some reason, the Job created by this resource in the Operator / System namespace should be the first place you check to see if there's something wrong with the Helm operation; however, this is generally only accessible by a Cluster Admin.** +- A HelmRelease CR (managed via an embedded [rancher/helm-locker](https://github.com/rancher/helm-locker) in the operator): this custom resource automatically locks a deployed Helm release in place and automatically overwrites updates to underlying resources unless the change happens via a Helm operation (`helm install`, `helm upgrade`, or `helm uninstall` performed by the HelmChart CR). +> Note: HelmRelease CRs emit Kubernetes Events that detect when an underlying Helm release is being modified and locks it back to place; to view these events, you can use `kubectl describe helmrelease -n `; you can also view the logs on this operator to see when changes are detected and which resources were attempted to be modified + +Both of these resources are created for all Helm charts in the Operator / System namespaces to avoid escalation of privileges to underprivileged users. + +### RBAC + +As described in the section on namespaces above, Helm Project Operator expects that Project Owners, Project Members, and other users in the cluster with Project-level permissions (e.g. permissions in a certain set of namespaces identified by a single label selector) have minimal permissions in any namespaces except the Project Registration Namespace (which is imported into the project by default) and those that already comprise their projects. Therefore, in order to allow Project Owners to assign specific chart permissions to other users in their Project namespaces, the Helm Project Operator will automatically watch the following bindings: +- ClusterRoleBindings +- RoleBindings in the Project Release Namespace + +On observing a change to one of those types of bindings, the Helm Project Operator will check whether the `roleRef` that the the binding points to matches a ClusterRole with the name provided under `helmProjectOperator.releaseRoleBindings.clusterRoleRefs.admin`, `helmProjectOperator.releaseRoleBindings.clusterRoleRefs.edit`, or `helmProjectOperator.releaseRoleBindings.clusterRoleRefs.view`; by default, these roleRefs correspond will correspond to `admin`, `edit`, and `view` respectively, which are the [default Kubernetes user-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles). + +> Note: for Rancher RBAC users, these [default Kubernetes user-facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) directly correlate to the `Project Owner`, `Project Member`, and `Read-Only` default Project Role Templates. + +If the `roleRef` matches, the Helm Project Operator will filter the `subjects` of the binding for all Users and Groups and use that to automatically construct a RoleBinding for each Role in the Project Release Namespace with the same name as the role and the following labels: +- `helm.cattle.io/project-helm-chart-role: {{ .Release.Name }}` +- `helm.cattle.io/project-helm-chart-role-aggregate-from: ` + +By default, the `project-operator-example` (the underlying chart deployed by Helm Project Operator) does not create any default roles; however, if a Cluster Admin would like to assign additional permissions to certain users, they can either directly assign RoleBindings in the Project Release Namespace to certain users or created Roles with the above two labels on them to allow Project Owners to control assigning those RBAC roles to users in their Project Registration namespaces. + +### Advanced Helm Project Operator Configuration + +|Value|Configuration| +|---|---------------------------| +|`valuesOverride`| Allows an Operator to override values that are set on each ProjectHelmChart deployment on an operator-level; user-provided options (specified on the `spec.values` of the ProjectHelmChart) are automatically overridden if operator-level values are provided. For an exmaple, see how the default value overrides `federate.targets` (note: when overriding list values like `federate.targets`, user-provided list values will **not** be concatenated) | +|`projectReleaseNamespaces.labelValues`| The value of the Project that all Project Release Namespaces should be auto-imported into (via label and annotation). Not recommended to be overridden on a Rancher setup. | +|`otherSystemProjectLabelValues`| Other namespaces that the operator should treat as a system namespace that should not be monitored. By default, all namespaces that match `global.cattle.systemProjectId` will not be matched. `kube-system` is explicitly marked as a system namespace as well, regardless of label or annotation. | +|`releaseRoleBindings.aggregate`| Whether to automatically create RBAC resources in Project Release namespaces +|`releaseRoleBindings.clusterRoleRefs.`| ClusterRoles to reference to discover subjects to create RoleBindings for in the Project Release Namespace for all corresponding Project Release Roles. See RBAC above for more information | +|`hardenedNamespaces.enabled`| Whether to automatically patch the default ServiceAccount with `automountServiceAccountToken: false` and create a default NetworkPolicy in all managed namespaces in the cluster; the default values ensure that the creation of the namespace does not break a CIS 1.16 hardened scan | +|`hardenedNamespaces.configuration`| The configuration to be supplied to the default ServiceAccount or auto-generated NetworkPolicy on managing a namespace | +|`helmController.enabled`| Whether to enable an embedded k3s-io/helm-controller instance within the Helm Project Operator. Should be disabled for RKE2 clusters since RKE2 clusters already run Helm Controller to manage internal Kubernetes components | +|`helmLocker.enabled`| Whether to enable an embedded rancher/helm-locker instance within the Helm Project Operator. | diff --git a/packages/helm-project-operator/charts/app-readme.md b/packages/helm-project-operator/charts/app-readme.md new file mode 100644 index 00000000..fd551467 --- /dev/null +++ b/packages/helm-project-operator/charts/app-readme.md @@ -0,0 +1,20 @@ +# Helm Project Operator + +This chart installs the example [Helm Project Operator](https://github.com/rancher/helm-project-operator) onto your cluster. + +## Upgrading to Kubernetes v1.25+ + +Starting in Kubernetes v1.25, [Pod Security Policies](https://kubernetes.io/docs/concepts/security/pod-security-policy/) have been removed from the Kubernetes API. + +As a result, **before upgrading to Kubernetes v1.25** (or on a fresh install in a Kubernetes v1.25+ cluster), users are expected to perform an in-place upgrade of this chart with `global.cattle.psp.enabled` set to `false` if it has been previously set to `true`. +​ +> **Note:** +> In this chart release, any previous field that was associated with any PSP resources have been removed in favor of a single global field: `global.cattle.psp.enabled`. + ​ +> **Note:** +> If you upgrade your cluster to Kubernetes v1.25+ before removing PSPs via a `helm upgrade` (even if you manually clean up resources), **it will leave the Helm release in a broken state within the cluster such that further Helm operations will not work (`helm uninstall`, `helm upgrade`, etc.).** +> +> If your charts get stuck in this state, please consult the Rancher docs on how to clean up your Helm release secrets. +Upon setting `global.cattle.psp.enabled` to false, the chart will remove any PSP resources deployed on its behalf from the cluster. This is the default setting for this chart. +​ +As a replacement for PSPs, [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/) should be used. Please consult the Rancher docs for more details on how to configure your chart release namespaces to work with the new Pod Security Admission and apply Pod Security Standards. \ No newline at end of file diff --git a/packages/helm-project-operator/charts/questions.yaml b/packages/helm-project-operator/charts/questions.yaml new file mode 100644 index 00000000..054361a7 --- /dev/null +++ b/packages/helm-project-operator/charts/questions.yaml @@ -0,0 +1,43 @@ +questions: +- variable: global.cattle.psp.enabled + default: "false" + description: "Flag to enable or disable the installation of PodSecurityPolicies by this chart in the target cluster. If the cluster is running Kubernetes 1.25+, you must update this value to false." + label: "Enable PodSecurityPolicies" + type: boolean + group: "Security Settings" +- variable: helmController.enabled + label: Enable Embedded Helm Controller + description: 'Note: If you are running this chart in an RKE2 cluster, this should be disabled.' + type: boolean + group: Helm Controller +- variable: helmLocker.enabled + label: Enable Embedded Helm Locker + type: boolean + group: Helm Locker +- variable: projectReleaseNamespaces.labelValue + label: Project Release Namespace Project ID + description: By default, the System Project is selected. This can be overriden to a different Project (e.g. p-xxxxx) + type: string + required: false + group: Namespaces +- variable: releaseRoleBindings.clusterRoleRefs.admin + label: Admin ClusterRole + description: By default, admin selects Project Owners. This can be overridden to a different ClusterRole (e.g. rt-xxxxx) + type: string + default: admin + required: false + group: RBAC +- variable: releaseRoleBindings.clusterRoleRefs.edit + label: Edit ClusterRole + description: By default, edit selects Project Members. This can be overridden to a different ClusterRole (e.g. rt-xxxxx) + type: string + default: edit + required: false + group: RBAC +- variable: releaseRoleBindings.clusterRoleRefs.view + label: View ClusterRole + description: By default, view selects Read-Only users. This can be overridden to a different ClusterRole (e.g. rt-xxxxx) + type: string + default: view + required: false + group: RBAC diff --git a/packages/helm-project-operator/charts/templates/NOTES.txt b/packages/helm-project-operator/charts/templates/NOTES.txt new file mode 100644 index 00000000..32baeebc --- /dev/null +++ b/packages/helm-project-operator/charts/templates/NOTES.txt @@ -0,0 +1,2 @@ +{{ $.Chart.Name }} has been installed. Check its status by running: + kubectl --namespace {{ template "helm-project-operator.namespace" . }} get pods -l "release={{ $.Release.Name }}" diff --git a/packages/helm-project-operator/charts/templates/_helpers.tpl b/packages/helm-project-operator/charts/templates/_helpers.tpl new file mode 100644 index 00000000..97dd6b36 --- /dev/null +++ b/packages/helm-project-operator/charts/templates/_helpers.tpl @@ -0,0 +1,66 @@ +# Rancher +{{- define "system_default_registry" -}} +{{- if .Values.global.cattle.systemDefaultRegistry -}} +{{- printf "%s/" .Values.global.cattle.systemDefaultRegistry -}} +{{- end -}} +{{- end -}} + +# Windows Support + +{{/* +Windows cluster will add default taint for linux nodes, +add below linux tolerations to workloads could be scheduled to those linux nodes +*/}} + +{{- define "linux-node-tolerations" -}} +- key: "cattle.io/os" + value: "linux" + effect: "NoSchedule" + operator: "Equal" +{{- end -}} + +{{- define "linux-node-selector" -}} +{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion -}} +beta.kubernetes.io/os: linux +{{- else -}} +kubernetes.io/os: linux +{{- end -}} +{{- end -}} + +# Helm Project Operator + +{{/* vim: set filetype=mustache: */}} +{{/* Expand the name of the chart. This is suffixed with -alertmanager, which means subtract 13 from longest 63 available */}} +{{- define "helm-project-operator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 50 | trimSuffix "-" -}} +{{- end }} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "helm-project-operator.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* Create chart name and version as used by the chart label. */}} +{{- define "helm-project-operator.chartref" -}} +{{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}} +{{- end }} + +{{/* Generate basic labels */}} +{{- define "helm-project-operator.labels" -}} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/version: "{{ replace "+" "_" .Chart.Version }}" +app.kubernetes.io/part-of: {{ template "helm-project-operator.name" . }} +chart: {{ template "helm-project-operator.chartref" . }} +release: {{ $.Release.Name | quote }} +heritage: {{ $.Release.Service | quote }} +{{- if .Values.commonLabels}} +{{ toYaml .Values.commonLabels }} +{{- end }} +{{- end -}} diff --git a/packages/helm-project-operator/charts/templates/cleanup.yaml b/packages/helm-project-operator/charts/templates/cleanup.yaml new file mode 100644 index 00000000..98675642 --- /dev/null +++ b/packages/helm-project-operator/charts/templates/cleanup.yaml @@ -0,0 +1,82 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "helm-project-operator.name" . }}-cleanup + namespace: {{ template "helm-project-operator.namespace" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + app: {{ template "helm-project-operator.name" . }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": before-hook-creation, hook-succeeded, hook-failed +spec: + template: + metadata: + name: {{ template "helm-project-operator.name" . }}-cleanup + labels: {{ include "helm-project-operator.labels" . | nindent 8 }} + app: {{ template "helm-project-operator.name" . }} + spec: + serviceAccountName: {{ template "helm-project-operator.name" . }} +{{- if .Values.cleanup.securityContext }} + securityContext: {{ toYaml .Values.cleanup.securityContext | nindent 8 }} +{{- end }} + initContainers: + - name: add-cleanup-annotations + image: {{ template "system_default_registry" . }}{{ .Values.cleanup.image.repository }}:{{ .Values.cleanup.image.tag }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + command: + - /bin/sh + - -c + - > + echo "Labeling all ProjectHelmCharts with helm.cattle.io/helm-project-operator-cleanup=true"; + EXPECTED_HELM_API_VERSION={{ .Values.helmApiVersion }}; + IFS=$'\n'; + for namespace in $(kubectl get namespaces -l helm.cattle.io/helm-project-operated=true --no-headers -o=custom-columns=NAME:.metadata.name); do + for projectHelmChartAndHelmApiVersion in $(kubectl get projecthelmcharts -n ${namespace} --no-headers -o=custom-columns=NAME:.metadata.name,HELMAPIVERSION:.spec.helmApiVersion); do + projectHelmChartAndHelmApiVersion=$(echo ${projectHelmChartAndHelmApiVersion} | xargs); + projectHelmChart=$(echo ${projectHelmChartAndHelmApiVersion} | cut -d' ' -f1); + helmApiVersion=$(echo ${projectHelmChartAndHelmApiVersion} | cut -d' ' -f2); + if [[ ${helmApiVersion} != ${EXPECTED_HELM_API_VERSION} ]]; then + echo "Skipping marking ${namespace}/${projectHelmChart} with cleanup annotation since spec.helmApiVersion: ${helmApiVersion} is not ${EXPECTED_HELM_API_VERSION}"; + continue; + fi; + kubectl label projecthelmcharts -n ${namespace} ${projectHelmChart} helm.cattle.io/helm-project-operator-cleanup=true --overwrite; + done; + done; +{{- if .Values.cleanup.resources }} + resources: {{ toYaml .Values.cleanup.resources | nindent 12 }} +{{- end }} +{{- if .Values.cleanup.containerSecurityContext }} + securityContext: {{ toYaml .Values.cleanup.containerSecurityContext | nindent 12 }} +{{- end }} + containers: + - name: ensure-subresources-deleted + image: {{ template "system_default_registry" . }}{{ .Values.cleanup.image.repository }}:{{ .Values.cleanup.image.tag }} + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - > + SYSTEM_NAMESPACE={{ .Release.Namespace }} + EXPECTED_HELM_API_VERSION={{ .Values.helmApiVersion }}; + HELM_API_VERSION_TRUNCATED=$(echo ${EXPECTED_HELM_API_VERSION} | cut -d'/' -f0); + echo "Ensuring HelmCharts and HelmReleases are deleted from ${SYSTEM_NAMESPACE}..."; + while [[ "$(kubectl get helmcharts,helmreleases -l helm.cattle.io/helm-api-version=${HELM_API_VERSION_TRUNCATED} -n ${SYSTEM_NAMESPACE} 2>&1)" != "No resources found in ${SYSTEM_NAMESPACE} namespace." ]]; do + echo "waiting for HelmCharts and HelmReleases to be deleted from ${SYSTEM_NAMESPACE}... sleeping 3 seconds"; + sleep 3; + done; + echo "Successfully deleted all HelmCharts and HelmReleases in ${SYSTEM_NAMESPACE}!"; +{{- if .Values.cleanup.resources }} + resources: {{ toYaml .Values.cleanup.resources | nindent 12 }} +{{- end }} +{{- if .Values.cleanup.containerSecurityContext }} + securityContext: {{ toYaml .Values.cleanup.containerSecurityContext | nindent 12 }} +{{- end }} + restartPolicy: OnFailure + nodeSelector: {{ include "linux-node-selector" . | nindent 8 }} + {{- if .Values.cleanup.nodeSelector }} + {{- toYaml .Values.cleanup.nodeSelector | nindent 8 }} + {{- end }} + tolerations: {{ include "linux-node-tolerations" . | nindent 8 }} + {{- if .Values.cleanup.tolerations }} + {{- toYaml .Values.cleanup.tolerations | nindent 8 }} + {{- end }} diff --git a/packages/helm-project-operator/charts/templates/clusterrole.yaml b/packages/helm-project-operator/charts/templates/clusterrole.yaml new file mode 100644 index 00000000..60ed263b --- /dev/null +++ b/packages/helm-project-operator/charts/templates/clusterrole.yaml @@ -0,0 +1,57 @@ +{{- if and .Values.global.rbac.create .Values.global.rbac.userRoles.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "helm-project-operator.name" . }}-admin + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + rbac.authorization.k8s.io/aggregate-to-admin: "true" + {{- end }} +rules: +- apiGroups: + - helm.cattle.io + resources: + - projecthelmcharts + - projecthelmcharts/finalizers + - projecthelmcharts/status + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "helm-project-operator.name" . }}-edit + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + rbac.authorization.k8s.io/aggregate-to-edit: "true" + {{- end }} +rules: +- apiGroups: + - helm.cattle.io + resources: + - projecthelmcharts + - projecthelmcharts/status + verbs: + - 'get' + - 'list' + - 'watch' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "helm-project-operator.name" . }}-view + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + rbac.authorization.k8s.io/aggregate-to-view: "true" + {{- end }} +rules: +- apiGroups: + - helm.cattle.io + resources: + - projecthelmcharts + - projecthelmcharts/status + verbs: + - 'get' + - 'list' + - 'watch' +{{- end }} diff --git a/packages/helm-project-operator/charts/templates/configmap.yaml b/packages/helm-project-operator/charts/templates/configmap.yaml new file mode 100644 index 00000000..d4def157 --- /dev/null +++ b/packages/helm-project-operator/charts/templates/configmap.yaml @@ -0,0 +1,14 @@ +## Note: If you add another entry to this ConfigMap, make sure a corresponding env var is set +## in the deployment of the operator to ensure that a Helm upgrade will force the operator +## to reload the values in the ConfigMap and redeploy +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "helm-project-operator.name" . }}-config + namespace: {{ template "helm-project-operator.namespace" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} +data: + hardened.yaml: |- +{{ .Values.hardenedNamespaces.configuration | toYaml | indent 4 }} + values.yaml: |- +{{ .Values.valuesOverride | toYaml | indent 4 }} diff --git a/packages/helm-project-operator/charts/templates/deployment.yaml b/packages/helm-project-operator/charts/templates/deployment.yaml new file mode 100644 index 00000000..33b81e72 --- /dev/null +++ b/packages/helm-project-operator/charts/templates/deployment.yaml @@ -0,0 +1,126 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "helm-project-operator.name" . }} + namespace: {{ template "helm-project-operator.namespace" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + app: {{ template "helm-project-operator.name" . }} +spec: + {{- if .Values.replicas }} + replicas: {{ .Values.replicas }} + {{- end }} + selector: + matchLabels: + app: {{ template "helm-project-operator.name" . }} + release: {{ $.Release.Name | quote }} + template: + metadata: + labels: {{ include "helm-project-operator.labels" . | nindent 8 }} + app: {{ template "helm-project-operator.name" . }} + spec: + containers: + - name: {{ template "helm-project-operator.name" . }} + image: "{{ template "system_default_registry" . }}{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + args: + - {{ template "helm-project-operator.name" . }} + - --namespace={{ template "helm-project-operator.namespace" . }} + - --controller-name={{ template "helm-project-operator.name" . }} + - --values-override-file=/etc/helmprojectoperator/config/values.yaml +{{- if .Values.global.cattle.systemDefaultRegistry }} + - --system-default-registry={{ .Values.global.cattle.systemDefaultRegistry }} +{{- end }} +{{- if .Values.global.cattle.url }} + - --cattle-url={{ .Values.global.cattle.url }} +{{- end }} +{{- if .Values.global.cattle.projectLabel }} + - --project-label={{ .Values.global.cattle.projectLabel }} +{{- end }} +{{- if not .Values.projectReleaseNamespaces.enabled }} + - --system-project-label-values={{ join "," (append .Values.otherSystemProjectLabelValues .Values.global.cattle.systemProjectId) }} +{{- else if and (ne (len .Values.global.cattle.systemProjectId) 0) (ne (len .Values.projectReleaseNamespaces.labelValue) 0) (ne .Values.projectReleaseNamespaces.labelValue .Values.global.cattle.systemProjectId) }} + - --system-project-label-values={{ join "," (append .Values.otherSystemProjectLabelValues .Values.global.cattle.systemProjectId) }} +{{- else if len .Values.otherSystemProjectLabelValues }} + - --system-project-label-values={{ join "," .Values.otherSystemProjectLabelValues }} +{{- end }} +{{- if .Values.projectReleaseNamespaces.enabled }} +{{- if .Values.projectReleaseNamespaces.labelValue }} + - --project-release-label-value={{ .Values.projectReleaseNamespaces.labelValue }} +{{- else if .Values.global.cattle.systemProjectId }} + - --project-release-label-value={{ .Values.global.cattle.systemProjectId }} +{{- end }} +{{- end }} +{{- if .Values.global.cattle.clusterId }} + - --cluster-id={{ .Values.global.cattle.clusterId }} +{{- end }} +{{- if .Values.releaseRoleBindings.aggregate }} +{{- if .Values.releaseRoleBindings.clusterRoleRefs }} +{{- if .Values.releaseRoleBindings.clusterRoleRefs.admin }} + - --admin-cluster-role={{ .Values.releaseRoleBindings.clusterRoleRefs.admin }} +{{- end }} +{{- if .Values.releaseRoleBindings.clusterRoleRefs.edit }} + - --edit-cluster-role={{ .Values.releaseRoleBindings.clusterRoleRefs.edit }} +{{- end }} +{{- if .Values.releaseRoleBindings.clusterRoleRefs.view }} + - --view-cluster-role={{ .Values.releaseRoleBindings.clusterRoleRefs.view }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.hardenedNamespaces.enabled }} + - --hardening-options-file=/etc/helmprojectoperator/config/hardening.yaml +{{- else }} + - --disable-hardening +{{- end }} +{{- if .Values.debug }} + - --debug + - --debug-level={{ .Values.debugLevel }} +{{- end }} +{{- if not .Values.helmController.enabled }} + - --disable-embedded-helm-controller +{{- else }} + - --helm-job-image={{ template "system_default_registry" . }}{{ .Values.helmController.job.image.repository }}:{{ .Values.helmController.job.image.tag }} +{{- end }} +{{- if not .Values.helmLocker.enabled }} + - --disable-embedded-helm-locker +{{- end }} +{{- if .Values.additionalArgs }} +{{- toYaml .Values.additionalArgs | nindent 10 }} +{{- end }} + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + ## Note: The below two values only exist to force Helm to upgrade the deployment on + ## a change to the contents of the ConfigMap during an upgrade. Neither serve + ## any practical purpose and can be removed and replaced with a configmap reloader + ## in a future change if dynamic updates are required. + - name: HARDENING_OPTIONS_SHA_256_HASH + value: {{ .Values.hardenedNamespaces.configuration | toYaml | sha256sum }} + - name: VALUES_OVERRIDE_SHA_256_HASH + value: {{ .Values.valuesOverride | toYaml | sha256sum }} +{{- if .Values.resources }} + resources: {{ toYaml .Values.resources | nindent 12 }} +{{- end }} +{{- if .Values.containerSecurityContext }} + securityContext: {{ toYaml .Values.containerSecurityContext | nindent 12 }} +{{- end }} + volumeMounts: + - name: config + mountPath: "/etc/helmprojectoperator/config" + serviceAccountName: {{ template "helm-project-operator.name" . }} +{{- if .Values.securityContext }} + securityContext: {{ toYaml .Values.securityContext | nindent 8 }} +{{- end }} + nodeSelector: {{ include "linux-node-selector" . | nindent 8 }} +{{- if .Values.nodeSelector }} +{{- toYaml .Values.nodeSelector | nindent 8 }} +{{- end }} + tolerations: {{ include "linux-node-tolerations" . | nindent 8 }} +{{- if .Values.tolerations }} +{{- toYaml .Values.tolerations | nindent 8 }} +{{- end }} + volumes: + - name: config + configMap: + name: {{ template "helm-project-operator.name" . }}-config diff --git a/packages/helm-project-operator/charts/templates/psp.yaml b/packages/helm-project-operator/charts/templates/psp.yaml new file mode 100644 index 00000000..73dcc456 --- /dev/null +++ b/packages/helm-project-operator/charts/templates/psp.yaml @@ -0,0 +1,68 @@ +{{- if .Values.global.cattle.psp.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "helm-project-operator.name" . }}-psp + namespace: {{ template "helm-project-operator.namespace" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + app: {{ template "helm-project-operator.name" . }} +{{- if .Values.global.rbac.pspAnnotations }} + annotations: {{ toYaml .Values.global.rbac.pspAnnotations | nindent 4 }} +{{- end }} +spec: + privileged: false + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Permits the container to run with root privileges as well. + rule: 'RunAsAny' + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 0 + max: 65535 + readOnlyRootFilesystem: false +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "helm-project-operator.name" . }}-psp + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + app: {{ template "helm-project-operator.name" . }} +rules: +{{- if semverCompare "> 1.15.0-0" .Capabilities.KubeVersion.GitVersion }} +- apiGroups: ['policy'] +{{- else }} +- apiGroups: ['extensions'] +{{- end }} + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "helm-project-operator.name" . }}-psp +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "helm-project-operator.name" . }}-psp + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + app: {{ template "helm-project-operator.name" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "helm-project-operator.name" . }}-psp +subjects: + - kind: ServiceAccount + name: {{ template "helm-project-operator.name" . }} + namespace: {{ template "helm-project-operator.namespace" . }} +{{- end }} diff --git a/packages/helm-project-operator/charts/templates/rbac.yaml b/packages/helm-project-operator/charts/templates/rbac.yaml new file mode 100644 index 00000000..b1c40920 --- /dev/null +++ b/packages/helm-project-operator/charts/templates/rbac.yaml @@ -0,0 +1,32 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "helm-project-operator.name" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + app: {{ template "helm-project-operator.name" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: "cluster-admin" # see note below +subjects: +- kind: ServiceAccount + name: {{ template "helm-project-operator.name" . }} + namespace: {{ template "helm-project-operator.namespace" . }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "helm-project-operator.name" . }} + namespace: {{ template "helm-project-operator.namespace" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} + app: {{ template "helm-project-operator.name" . }} +{{- if .Values.global.imagePullSecrets }} +imagePullSecrets: {{ toYaml .Values.global.imagePullSecrets | nindent 2 }} +{{- end }} +# --- +# NOTE: +# As of now, due to the fact that the k3s-io/helm-controller can only deploy jobs that are cluster-bound to the cluster-admin +# ClusterRole, the only way for this operator to be able to perform that binding is if it is also bound to the cluster-admin ClusterRole. +# +# As a result, this ClusterRoleBinding will be left as a work-in-progress until changes are made in k3s-io/helm-controller to allow us to grant +# only scoped down permissions to the Job that is deployed. diff --git a/packages/helm-project-operator/charts/templates/system-namespaces-configmap.yaml b/packages/helm-project-operator/charts/templates/system-namespaces-configmap.yaml new file mode 100644 index 00000000..f4c85254 --- /dev/null +++ b/packages/helm-project-operator/charts/templates/system-namespaces-configmap.yaml @@ -0,0 +1,62 @@ +{{- if .Values.systemNamespacesConfigMap.create }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "helm-project-operator.name" . }}-system-namespaces + namespace: {{ template "helm-project-operator.namespace" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} +data: + system-namespaces.json: |- + { +{{- if .Values.projectReleaseNamespaces.enabled }} +{{- if .Values.projectReleaseNamespaces.labelValue }} + "projectReleaseLabelValue": {{ .Values.projectReleaseNamespaces.labelValue | quote }}, +{{- else if .Values.global.cattle.systemProjectId }} + "projectReleaseLabelValue": {{ .Values.global.cattle.systemProjectId | quote }}, +{{- else }} + "projectReleaseLabelValue": "", +{{- end }} +{{- else }} + "projectReleaseLabelValue": "", +{{- end }} +{{- if not .Values.projectReleaseNamespaces.enabled }} + "systemProjectLabelValues": {{ append .Values.otherSystemProjectLabelValues .Values.global.cattle.systemProjectId | toJson }} +{{- else if and (ne (len .Values.global.cattle.systemProjectId) 0) (ne (len .Values.projectReleaseNamespaces.labelValue) 0) (ne .Values.projectReleaseNamespaces.labelValue .Values.global.cattle.systemProjectId) }} + "systemProjectLabelValues": {{ append .Values.otherSystemProjectLabelValues .Values.global.cattle.systemProjectId | toJson }} +{{- else if len .Values.otherSystemProjectLabelValues }} + "systemProjectLabelValues": {{ .Values.otherSystemProjectLabelValues | toJson }} +{{- else }} + "systemProjectLabelValues": [] +{{- end }} + } +--- +{{- if (and .Values.systemNamespacesConfigMap.rbac.enabled .Values.systemNamespacesConfigMap.rbac.subjects) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "helm-project-operator.name" . }}-system-namespaces + namespace: {{ template "helm-project-operator.namespace" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - "" + resources: + - configmaps + resourceNames: + - "{{ template "helm-project-operator.name" . }}-system-namespaces" + verbs: + - 'get' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "helm-project-operator.name" . }}-system-namespaces + namespace: {{ template "helm-project-operator.namespace" . }} + labels: {{ include "helm-project-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "helm-project-operator.name" . }}-system-namespaces +subjects: {{ .Values.systemNamespacesConfigMap.rbac.subjects | toYaml | nindent 2 }} +{{- end }} +{{- end }} diff --git a/packages/helm-project-operator/charts/templates/validate-psp-install.yaml b/packages/helm-project-operator/charts/templates/validate-psp-install.yaml new file mode 100644 index 00000000..a30c59d3 --- /dev/null +++ b/packages/helm-project-operator/charts/templates/validate-psp-install.yaml @@ -0,0 +1,7 @@ +#{{- if gt (len (lookup "rbac.authorization.k8s.io/v1" "ClusterRole" "" "")) 0 -}} +#{{- if .Values.global.cattle.psp.enabled }} +#{{- if not (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +#{{- fail "The target cluster does not have the PodSecurityPolicy API resource. Please disable PSPs in this chart before proceeding." -}} +#{{- end }} +#{{- end }} +#{{- end }} diff --git a/packages/helm-project-operator/charts/values.yaml b/packages/helm-project-operator/charts/values.yaml new file mode 100644 index 00000000..796bf5f1 --- /dev/null +++ b/packages/helm-project-operator/charts/values.yaml @@ -0,0 +1,228 @@ +# Default values for helm-project-operator. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# Helm Project Operator Configuration + +global: + cattle: + clusterId: "" + psp: + enabled: false + projectLabel: field.cattle.io/projectId + systemDefaultRegistry: "" + systemProjectId: "" + url: "" + rbac: + ## Create RBAC resources for ServiceAccounts and users + ## + create: true + + userRoles: + ## Create default user ClusterRoles to allow users to interact with ProjectHelmCharts + create: true + ## Aggregate default user ClusterRoles into default k8s ClusterRoles + aggregateToDefaultRoles: true + + pspAnnotations: {} + ## Specify pod annotations + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#apparmor + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#sysctl + ## + # seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' + # seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' + # apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' + + ## Reference to one or more secrets to be used when pulling images + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + imagePullSecrets: [] + # - name: "image-pull-secret" + +helmApiVersion: dummy.cattle.io/v1alpha1 + +## valuesOverride overrides values that are set on each ProjectHelmChart deployment on an operator-level +## User-provided values will be overwritten based on the values provided here +valuesOverride: {} + +## projectReleaseNamespaces are auto-generated namespaces that are created to host Helm Releases +## managed by this operator on behalf of a ProjectHelmChart +projectReleaseNamespaces: + ## Enabled determines whether Project Release Namespaces should be created. If false, the underlying + ## Helm release will be deployed in the Project Registration Namespace + enabled: true + ## labelValue is the value of the Project that the projectReleaseNamespace should be created within + ## If empty, this will be set to the value of global.cattle.systemProjectId + ## If global.cattle.systemProjectId is also empty, project release namespaces will be disabled + labelValue: "" + +## otherSystemProjectLabelValues are project labels that identify namespaces as those that should be treated as system projects +## i.e. they will be entirely ignored by the operator +## By default, the global.cattle.systemProjectId will be in this list +otherSystemProjectLabelValues: [] + +## releaseRoleBindings configures RoleBindings automatically created by the Helm Project Operator +## in Project Release Namespaces where underlying Helm charts are deployed +releaseRoleBindings: + ## aggregate enables creating these RoleBindings off aggregating RoleBindings in the + ## Project Registration Namespace or ClusterRoleBindings that bind users to the ClusterRoles + ## specified under clusterRoleRefs + aggregate: true + + ## clusterRoleRefs are the ClusterRoles whose RoleBinding or ClusterRoleBindings should determine + ## the RoleBindings created in the Project Release Namespace + ## + ## By default, these are set to create RoleBindings based on the RoleBindings / ClusterRoleBindings + ## attached to the default K8s user-facing ClusterRoles of admin, edit, and view. + ## ref: https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles + ## + clusterRoleRefs: + admin: admin + edit: edit + view: view + +hardenedNamespaces: + # Whether to automatically manage the configuration of the default ServiceAccount and + # auto-create a NetworkPolicy for each namespace created by this operator + enabled: true + + configuration: + # Values to be applied to each default ServiceAccount created in a managed namespace + serviceAccountSpec: + secrets: [] + imagePullSecrets: [] + automountServiceAccountToken: false + # Values to be applied to each default generated NetworkPolicy created in a managed namespace + networkPolicySpec: + podSelector: {} + egress: [] + ingress: [] + policyTypes: ["Ingress", "Egress"] + +## systemNamespacesConfigMap is a ConfigMap created to allow users to see valid entries +## for registering a ProjectHelmChart for a given Project on the Rancher Dashboard UI. +## It does not need to be enabled for a non-Rancher use case. +systemNamespacesConfigMap: + ## Create indicates whether the system namespaces configmap should be created + ## This is a required value for integration with Rancher Dashboard + create: true + + ## RBAC provides options around the RBAC created to allow users to be able to view + ## the systemNamespacesConfigMap; if not specified, only users with the ability to + ## view ConfigMaps in the namespace where this chart is deployed will be able to + ## properly view the system namespaces on the Rancher Dashboard UI + rbac: + ## enabled indicates that we should deploy a RoleBinding and Role to view this ConfigMap + enabled: true + ## subjects are the subjects that should be bound to this default RoleBinding + ## By default, we allow anyone who is authenticated to the system to be able to view + ## this ConfigMap in the deployment namespace + subjects: + - kind: Group + name: system:authenticated + +nameOverride: "" + +namespaceOverride: "" + +replicas: 1 + +image: + repository: ghcr.io/rancher/helm-project-operator + tag: v0.3.0 + pullPolicy: IfNotPresent + +helmController: + # Note: should be disabled for RKE2 clusters since they already run Helm Controller to manage internal Kubernetes components + enabled: true + + job: + image: + repository: rancher/klipper-helm + tag: v0.7.0-build20220315 + +helmLocker: + enabled: true + +# Additional arguments to be passed into the Helm Project Operator image +additionalArgs: [] + +## Define which Nodes the Pods are scheduled on. +## ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +## Tolerations for use with node taints +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] +# - key: "key" +# operator: "Equal" +# value: "value" +# effect: "NoSchedule" + +resources: {} + # limits: + # memory: 500Mi + # cpu: 1000m + # requests: + # memory: 100Mi + # cpu: 100m + +containerSecurityContext: {} + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - ALL + # privileged: false + # readOnlyRootFilesystem: true + +securityContext: {} + # runAsGroup: 1000 + # runAsUser: 1000 + # supplementalGroups: + # - 1000 + +debug: false +debugLevel: 0 + +cleanup: + image: + repository: rancher/shell + tag: v0.1.19 + pullPolicy: IfNotPresent + + ## Define which Nodes the Pods are scheduled on. + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Tolerations for use with node taints + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal" + # value: "value" + # effect: "NoSchedule" + + containerSecurityContext: {} + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - ALL + # privileged: false + # readOnlyRootFilesystem: true + + securityContext: + runAsNonRoot: false + runAsUser: 0 + + resources: {} + # limits: + # memory: 500Mi + # cpu: 1000m + # requests: + # memory: 100Mi + # cpu: 100m diff --git a/packages/helm-project-operator/package.yaml b/packages/helm-project-operator/package.yaml new file mode 100644 index 00000000..23845fcd --- /dev/null +++ b/packages/helm-project-operator/package.yaml @@ -0,0 +1,2 @@ +url: local +doNotRelease: true From d2a16e51690b65bc8cee5a8b711942dd68b3b2b7 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 14:37:28 -0400 Subject: [PATCH 14/45] Update to use local dependency instead of remote --- .../dependencies/helmProjectOperator/dependency.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/prometheus-federator/generated-changes/dependencies/helmProjectOperator/dependency.yaml b/packages/prometheus-federator/generated-changes/dependencies/helmProjectOperator/dependency.yaml index 41a0fa0c..52a7b730 100644 --- a/packages/prometheus-federator/generated-changes/dependencies/helmProjectOperator/dependency.yaml +++ b/packages/prometheus-federator/generated-changes/dependencies/helmProjectOperator/dependency.yaml @@ -1,2 +1,2 @@ -url: https://github.com/rancher/helm-project-operator/releases/download/v0.3.1/helm-project-operator-0.3.1.tgz -doNotRelease: true \ No newline at end of file +workingDir: "" +url: packages/helm-project-operator From 7fd18f52170d9352d0f3fcd53fd8e6988012bf58 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 21:32:01 -0400 Subject: [PATCH 15/45] Add helm-locker ci workflow --- .github/workflows/hl-ci.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/hl-ci.yaml diff --git a/.github/workflows/hl-ci.yaml b/.github/workflows/hl-ci.yaml new file mode 100644 index 00000000..81c8ed16 --- /dev/null +++ b/.github/workflows/hl-ci.yaml @@ -0,0 +1,27 @@ +name: "[helm-locker] CI" + +on: + workflow_call: + pull_request: + push: + branches: + - main + +jobs: + build: + strategy: + matrix: + arch: + - x64 + - arm64 + runs-on : runs-on,image=ubuntu22-full-${{ matrix.arch }},runner=4cpu-linux-${{ matrix.arch }},run-id=${{ github.run_id }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name : Set up Go + uses : actions/setup-go@v5 + with: + go-version: 1.22 + - name: Run CI + run: BUILD_TARGET=helm-locker make ci From f427e671d8d3809639821927e7b50bc5e6b8b523 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Thu, 10 Oct 2024 21:52:25 -0400 Subject: [PATCH 16/45] enabled make test cmd for helm-locker --- scripts/test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test b/scripts/test index c9183cac..6b342995 100755 --- a/scripts/test +++ b/scripts/test @@ -4,8 +4,8 @@ set -e cd $(dirname $0)/.. declare -r target_test_tags=( - #["helm-locker"]="helm_locker", #["helm-project-operator"]="helm_locker", + ["helm-locker"]="helm_locker", ["prometheus-federator"]="prometheus_federator" ) TARGET_TEST_TAG=${target_test_tags[$BUILD_TARGET]} From 9f59c1df895afa3526f43eed668c9ff4b8c6477d Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 10:54:38 -0400 Subject: [PATCH 17/45] Use env to set bash shebang --- scripts/build | 2 +- scripts/build-chart | 2 +- scripts/charts-build-scripts/rebase | 2 +- scripts/ci | 2 +- scripts/default | 2 +- scripts/e2e-ci | 2 +- scripts/entry | 2 +- scripts/package | 2 +- scripts/pull-scripts | 2 +- scripts/release | 2 +- scripts/test | 2 +- scripts/util-chart | 2 +- scripts/validate | 2 +- scripts/validate-chart | 2 +- scripts/validate-charts | 2 +- scripts/validate-ci | 2 +- scripts/validate-packages | 2 +- scripts/version | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/scripts/build b/scripts/build index bcf4436a..66933d42 100755 --- a/scripts/build +++ b/scripts/build @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e source $(dirname $0)/version diff --git a/scripts/build-chart b/scripts/build-chart index 2d0bb140..ff05626b 100755 --- a/scripts/build-chart +++ b/scripts/build-chart @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e source $(dirname $0)/version diff --git a/scripts/charts-build-scripts/rebase b/scripts/charts-build-scripts/rebase index 75ab2baa..a16988a0 100755 --- a/scripts/charts-build-scripts/rebase +++ b/scripts/charts-build-scripts/rebase @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e diff --git a/scripts/ci b/scripts/ci index ec9fcab0..50e96f58 100755 --- a/scripts/ci +++ b/scripts/ci @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e cd $(dirname $0) diff --git a/scripts/default b/scripts/default index af2ad2db..41963f91 100755 --- a/scripts/default +++ b/scripts/default @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e cd $(dirname $0) diff --git a/scripts/e2e-ci b/scripts/e2e-ci index cfd00086..5753a275 100755 --- a/scripts/e2e-ci +++ b/scripts/e2e-ci @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e source $(dirname $0)/version diff --git a/scripts/entry b/scripts/entry index 78fb5679..82286ee2 100755 --- a/scripts/entry +++ b/scripts/entry @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e mkdir -p bin dist diff --git a/scripts/package b/scripts/package index f3d6d458..d53ecca7 100755 --- a/scripts/package +++ b/scripts/package @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e source $(dirname $0)/version diff --git a/scripts/pull-scripts b/scripts/pull-scripts index 99bb8bdc..eb4c3cd3 100755 --- a/scripts/pull-scripts +++ b/scripts/pull-scripts @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e cd $(dirname $0) diff --git a/scripts/release b/scripts/release index 7af0df35..43425f76 100755 --- a/scripts/release +++ b/scripts/release @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash exec $(dirname $0)/ci diff --git a/scripts/test b/scripts/test index 6b342995..894a9449 100755 --- a/scripts/test +++ b/scripts/test @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e cd $(dirname $0)/.. diff --git a/scripts/util-chart b/scripts/util-chart index cb268f92..39de4bc5 100644 --- a/scripts/util-chart +++ b/scripts/util-chart @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash function edit-charts() { sed -i \ diff --git a/scripts/validate b/scripts/validate index feb8b87d..c6f98c57 100755 --- a/scripts/validate +++ b/scripts/validate @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e cd $(dirname $0)/.. diff --git a/scripts/validate-chart b/scripts/validate-chart index ab08226f..88ceb28b 100755 --- a/scripts/validate-chart +++ b/scripts/validate-chart @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # We don't do this for helm-locker diff --git a/scripts/validate-charts b/scripts/validate-charts index 44b2cfe3..0ddd53fe 100755 --- a/scripts/validate-charts +++ b/scripts/validate-charts @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e cd $(dirname $0)/.. diff --git a/scripts/validate-ci b/scripts/validate-ci index b91da85e..ca46ac88 100755 --- a/scripts/validate-ci +++ b/scripts/validate-ci @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e source $(dirname $0)/version diff --git a/scripts/validate-packages b/scripts/validate-packages index 509720e2..64644be6 100755 --- a/scripts/validate-packages +++ b/scripts/validate-packages @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e echo "Validating packages..." diff --git a/scripts/version b/scripts/version index be0311ab..73aa7a8c 100755 --- a/scripts/version +++ b/scripts/version @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash CHARTS_BUILD_SCRIPTS_REPO=https://github.com/rancher/charts-build-scripts.git CHARTS_BUILD_SCRIPT_VERSION=v0.9.2 From fcbd8e10a95ecadb87db6e9c570726936e4b5c4b Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 10:54:55 -0400 Subject: [PATCH 18/45] Enable tests for `helm-locker` --- scripts/test | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/test b/scripts/test index 894a9449..0b1988f2 100755 --- a/scripts/test +++ b/scripts/test @@ -3,12 +3,14 @@ set -e cd $(dirname $0)/.. -declare -r target_test_tags=( - #["helm-project-operator"]="helm_locker", - ["helm-locker"]="helm_locker", - ["prometheus-federator"]="prometheus_federator" +echo "Starating tests" + +declare -rA target_test_tags=( + #[helm-project-operator]="helm_project_operator", + ["helm-locker"]="helm_locker", + ["prometheus-federator"]="prometheus_federator" ) -TARGET_TEST_TAG=${target_test_tags[$BUILD_TARGET]} +TARGET_TEST_TAG="${target_test_tags["$BUILD_TARGET"]}" echo "Running tests for ${TARGET_TEST_TAG}" go test -cover -tags="test,${TARGET_TEST_TAG}" ./... From b58581e2814f52669a41e30505c68e1ed2f3fffa Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 11:06:08 -0400 Subject: [PATCH 19/45] remove bash array usage --- scripts/build-chart | 11 ++++++----- scripts/test | 8 ++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/scripts/build-chart b/scripts/build-chart index ff05626b..71f0ec0f 100755 --- a/scripts/build-chart +++ b/scripts/build-chart @@ -11,12 +11,13 @@ fi cd $(dirname $0)/.. -declare -r chart_options=( - #["helm-project-operator"]="project-operator-example", - ["prometheus-federator"]="rancher-project-monitoring" -) +if [ "$BUILD_TARGET" == "prometheus-federator" ]; then + DEFAULT_CHART_TARGET="rancher-project-monitoring" +elif [ "$BUILD_TARGET" == "helm-project-operator" ]; then + DEFAULT_CHART_TARGET="project-operator-example" +fi -CHART=${CHART:-${chart_options[$BUILD_TARGET]}} +CHART=${CHART:-${DEFAULT_CHART_TARGET}} CHART_VERSION=${EMBEDDED_CHART_VERSION:-$(find ./charts/${CHART} -maxdepth 1 -mindepth 1 -type d | tr - \~ | sort -rV | tr \~ - | head -n1 | cut -d'/' -f4)} # Prepare and package charts diff --git a/scripts/test b/scripts/test index 0b1988f2..08b188af 100755 --- a/scripts/test +++ b/scripts/test @@ -1,16 +1,12 @@ #!/usr/bin/env bash set -e +source $(dirname $0)/version cd $(dirname $0)/.. echo "Starating tests" -declare -rA target_test_tags=( - #[helm-project-operator]="helm_project_operator", - ["helm-locker"]="helm_locker", - ["prometheus-federator"]="prometheus_federator" -) -TARGET_TEST_TAG="${target_test_tags["$BUILD_TARGET"]}" +TARGET_TEST_TAG="${BUILD_TARGET/-/_}" echo "Running tests for ${TARGET_TEST_TAG}" go test -cover -tags="test,${TARGET_TEST_TAG}" ./... From 5c1d356a537dfb1c8f84d74d05a63bda3ba7afff Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 11:13:31 -0400 Subject: [PATCH 20/45] Correct helm-locker docker file bin path --- package/Dockerfile-helm-locker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/Dockerfile-helm-locker b/package/Dockerfile-helm-locker index 3dd3d266..bcb89b1f 100644 --- a/package/Dockerfile-helm-locker +++ b/package/Dockerfile-helm-locker @@ -11,6 +11,6 @@ RUN echo 'helmlocker:x:1000:1000::/home/helmlocker:/bin/bash' >> /etc/passwd && echo 'helmlocker:x:1000:' >> /etc/group && \ mkdir /home/helmlocker && \ chown -R helmlocker:helmlocker /home/helmlocker -COPY --from=builder /usr/src/app/bin/helm-locker /usr/bin/ +COPY --from=builder /usr/src/app/build/bin/helm-locker /usr/bin/ USER helmlocker CMD ["helm-locker"] From 3ee5104493f4e391531e83db3b268d9b175f3337 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 11:19:21 -0400 Subject: [PATCH 21/45] fix helm-project-operator yq arch and bin path --- package/Dockerfile-helm-project-operator | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/Dockerfile-helm-project-operator b/package/Dockerfile-helm-project-operator index c76d44f0..0a11b552 100644 --- a/package/Dockerfile-helm-project-operator +++ b/package/Dockerfile-helm-project-operator @@ -26,7 +26,7 @@ WORKDIR /usr/src/app ENV YQ_VERSION=v4.25.1 RUN zypper -n install git curl wget make RUN export ARCH=$(go env GOHOSTARCH) -RUN wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64 -O /usr/bin/yq && chmod +x /usr/bin/yq; +RUN wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${TARGETARCH} -O /usr/bin/yq && chmod +x /usr/bin/yq; COPY --from=helm /helm/bin/helm /usr/local/bin/ COPY go.mod go.sum ./ RUN go mod download @@ -39,6 +39,6 @@ RUN echo 'helmprojectoperator:x:1000:1000::/home/helmprojectoperator:/bin/bash' echo 'helmprojectoperator:x:1000:' >> /etc/group && \ mkdir /home/helmprojectoperator && \ chown -R helmprojectoperator:helmprojectoperator /home/helmprojectoperator -COPY --from=builder /usr/src/app/bin/helm-project-operator /usr/bin/ +COPY --from=builder /usr/src/app/build/bin/helm-project-operator /usr/bin/ USER helmprojectoperator CMD ["helm-project-operator"] From 0604fb04910248097af1afa2160e35d537364a9b Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 12:23:52 -0400 Subject: [PATCH 22/45] Update helm-locker chart values --- packages/helm-locker/charts/values.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/helm-locker/charts/values.yaml b/packages/helm-locker/charts/values.yaml index 0742dcc9..935c35ed 100644 --- a/packages/helm-locker/charts/values.yaml +++ b/packages/helm-locker/charts/values.yaml @@ -10,7 +10,7 @@ global: kubectl: repository: rancher/kubectl - tag: v1.20.2 + tag: v1.25.16 pullPolicy: IfNotPresent rbac: @@ -48,9 +48,9 @@ namespaceOverride: "" replicas: 1 image: - repository: ghcr.io/rancher/helm-project-operator/helm-locker - tag: v0.3.0 - pullPolicy: IfNotPresent + repository: ghcr.io/rancher/prometheus-federator/helm-locker + tag: head + pullPolicy: Always # Additional arguments to be passed into the Helm Locker image additionalArgs: [] From 1e4f7abbed331cdab5ee49b60124a47ce4f78e40 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 13:34:33 -0400 Subject: [PATCH 23/45] Add helm-locker e2e tests --- .github/workflows/hl-e2e.yaml | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/hl-e2e.yaml diff --git a/.github/workflows/hl-e2e.yaml b/.github/workflows/hl-e2e.yaml new file mode 100644 index 00000000..d616e9e5 --- /dev/null +++ b/.github/workflows/hl-e2e.yaml @@ -0,0 +1,53 @@ +name: "[helm-locker] CI-e2e" + +on: + pull_request: + push: + branches: + - main + paths: + - 'go.mod' + - 'charts/helm-locker*/**' + - 'crds/helm-locker/**' + - 'package/Dockerfile-helm-locker' + - 'cmd/helm-locker/**' + - 'pkg/helm-locker/**' + +env: + CLUSTER_NAME : e2e-ci-helm-locker + K3S_VERSION : v1.27.9-k3s1 + +jobs: + build: + strategy: + matrix: + arch: + - x64 + - arm64 + runs-on : runs-on,image=ubuntu22-full-${{ matrix.arch }},runner=4cpu-linux-${{ matrix.arch }},run-id=${{ github.run_id }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name : Set up Go + uses : actions/setup-go@v5 + with: + go-version: 1.22 + - uses: azure/setup-kubectl@v3 + - uses: azure/setup-helm@v3 + - name: build + run: BUILD_TARGET=helm-locker make build + - name : Install k3d + run : ./.github/workflows/e2e/scripts/install-k3d.sh + - name : Setup k3d cluster + run : K3S_VERSION=${{ env.K3S_VERSION }} ./.github/workflows/e2e/scripts/setup-cluster.sh + # temporary hack to run the helm-locker controller in the k3d cluster + - name : run helm-locker + run : | + kubectl create ns cattle-helm-system + ./build/bin/helm-locker & + - name : run e2e tests + run: | + k3d kubeconfig get ${{ env.CLUSTER_NAME }} > kubeconfig.yaml + export KUBECONFIG=$(pwd)/kubeconfig.yaml + cd tests && KUBECONFIG=$KUBECONFIG go test -v -race -timeout 30m ./... From 2ef477ea6c1f1a0f52be810cb4e91f824a639829 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 14:31:55 -0400 Subject: [PATCH 24/45] import helm-project-operator codebase --- .gitignore | 2 + cmd/helm-project-operator/fs/.gitkeep | 0 cmd/helm-project-operator/main.go | 79 ++++ cmd/prometheus-federator/main.go | 4 +- .../apis/helm.cattle.io/v1alpha1/doc.go | 21 + .../helm.cattle.io/v1alpha1/genericmap.go | 28 ++ .../apis/helm.cattle.io/v1alpha1/project.go | 64 +++ .../v1alpha1/zz_generated_deepcopy.go | 142 +++++++ .../v1alpha1/zz_generated_list_types.go | 42 ++ .../v1alpha1/zz_generated_register.go | 60 +++ .../helm.cattle.io/zz_generated_register.go | 24 ++ .../applier/applyinator.go | 142 +++++++ .../codegen/cleanup/main.go | 20 + pkg/helm-project-operator/codegen/main.go | 39 ++ .../controllers/common/formats.go | 8 + .../controllers/common/hardening.go | 53 +++ .../controllers/common/operator.go | 50 +++ .../controllers/common/operatorlabels.go | 96 +++++ .../controllers/common/options.go | 40 ++ .../controllers/common/rbac.go | 53 +++ .../controllers/common/runtime.go | 159 ++++++++ .../controllers/common/userlabels.go | 49 +++ .../controllers/controllers.go | 367 +++++++++++++++++ .../controllers/hardened/controller.go | 69 ++++ .../controllers/hardened/resolvers.go | 67 ++++ .../controllers/hardened/resources.go | 68 ++++ .../controllers/namespace/controller.go | 375 +++++++++++++++++ .../controllers/namespace/getter.go | 160 ++++++++ .../controllers/namespace/indexers.go | 41 ++ .../controllers/namespace/namespaces.go | 48 +++ .../controllers/namespace/reconcilers.go | 59 +++ .../controllers/namespace/resolvers.go | 45 +++ .../controllers/namespace/resources.go | 48 +++ .../controllers/namespace/tracker.go | 72 ++++ .../controllers/namespace/utils.go | 45 +++ .../controllers/parse.go | 64 +++ .../controllers/project/cleanup.go | 53 +++ .../controllers/project/controller.go | 346 ++++++++++++++++ .../controllers/project/indexers.go | 141 +++++++ .../controllers/project/merge.go | 65 +++ .../controllers/project/registrationdata.go | 80 ++++ .../controllers/project/releasedata.go | 114 ++++++ .../controllers/project/resolvers.go | 244 ++++++++++++ .../controllers/project/resources.go | 111 ++++++ .../controllers/project/status.go | 68 ++++ .../controllers/project/utils.go | 69 ++++ .../controllers/project/values.go | 41 ++ pkg/helm-project-operator/crd/crds.go | 180 +++++++++ .../controllers/helm.cattle.io/factory.go | 67 ++++ .../controllers/helm.cattle.io/interface.go | 43 ++ .../helm.cattle.io/v1alpha1/interface.go | 48 +++ .../v1alpha1/projecthelmchart.go | 376 ++++++++++++++++++ pkg/helm-project-operator/operator/init.go | 34 ++ 53 files changed, 4781 insertions(+), 2 deletions(-) create mode 100644 cmd/helm-project-operator/fs/.gitkeep create mode 100644 cmd/helm-project-operator/main.go create mode 100644 pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/doc.go create mode 100644 pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/genericmap.go create mode 100644 pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/project.go create mode 100644 pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_deepcopy.go create mode 100644 pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_list_types.go create mode 100644 pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_register.go create mode 100644 pkg/helm-project-operator/apis/helm.cattle.io/zz_generated_register.go create mode 100644 pkg/helm-project-operator/applier/applyinator.go create mode 100644 pkg/helm-project-operator/codegen/cleanup/main.go create mode 100644 pkg/helm-project-operator/codegen/main.go create mode 100644 pkg/helm-project-operator/controllers/common/formats.go create mode 100644 pkg/helm-project-operator/controllers/common/hardening.go create mode 100644 pkg/helm-project-operator/controllers/common/operator.go create mode 100644 pkg/helm-project-operator/controllers/common/operatorlabels.go create mode 100644 pkg/helm-project-operator/controllers/common/options.go create mode 100644 pkg/helm-project-operator/controllers/common/rbac.go create mode 100644 pkg/helm-project-operator/controllers/common/runtime.go create mode 100644 pkg/helm-project-operator/controllers/common/userlabels.go create mode 100644 pkg/helm-project-operator/controllers/controllers.go create mode 100644 pkg/helm-project-operator/controllers/hardened/controller.go create mode 100644 pkg/helm-project-operator/controllers/hardened/resolvers.go create mode 100644 pkg/helm-project-operator/controllers/hardened/resources.go create mode 100644 pkg/helm-project-operator/controllers/namespace/controller.go create mode 100644 pkg/helm-project-operator/controllers/namespace/getter.go create mode 100644 pkg/helm-project-operator/controllers/namespace/indexers.go create mode 100644 pkg/helm-project-operator/controllers/namespace/namespaces.go create mode 100644 pkg/helm-project-operator/controllers/namespace/reconcilers.go create mode 100644 pkg/helm-project-operator/controllers/namespace/resolvers.go create mode 100644 pkg/helm-project-operator/controllers/namespace/resources.go create mode 100644 pkg/helm-project-operator/controllers/namespace/tracker.go create mode 100644 pkg/helm-project-operator/controllers/namespace/utils.go create mode 100644 pkg/helm-project-operator/controllers/parse.go create mode 100644 pkg/helm-project-operator/controllers/project/cleanup.go create mode 100644 pkg/helm-project-operator/controllers/project/controller.go create mode 100644 pkg/helm-project-operator/controllers/project/indexers.go create mode 100644 pkg/helm-project-operator/controllers/project/merge.go create mode 100644 pkg/helm-project-operator/controllers/project/registrationdata.go create mode 100644 pkg/helm-project-operator/controllers/project/releasedata.go create mode 100644 pkg/helm-project-operator/controllers/project/resolvers.go create mode 100644 pkg/helm-project-operator/controllers/project/resources.go create mode 100644 pkg/helm-project-operator/controllers/project/status.go create mode 100644 pkg/helm-project-operator/controllers/project/utils.go create mode 100644 pkg/helm-project-operator/controllers/project/values.go create mode 100644 pkg/helm-project-operator/crd/crds.go create mode 100644 pkg/helm-project-operator/generated/controllers/helm.cattle.io/factory.go create mode 100644 pkg/helm-project-operator/generated/controllers/helm.cattle.io/interface.go create mode 100644 pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/interface.go create mode 100644 pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/projecthelmchart.go create mode 100644 pkg/helm-project-operator/operator/init.go diff --git a/.gitignore b/.gitignore index efa3c3c5..06395193 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ /build !/cmd/prometheus-federator/fs/.gitkeep /cmd/prometheus-federator/fs/* +!/cmd/helm-project-operator/fs/.gitkeep +/cmd/helm-project-operator/fs/* /dist *.swp .idea diff --git a/cmd/helm-project-operator/fs/.gitkeep b/cmd/helm-project-operator/fs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/cmd/helm-project-operator/main.go b/cmd/helm-project-operator/main.go new file mode 100644 index 00000000..22bf2645 --- /dev/null +++ b/cmd/helm-project-operator/main.go @@ -0,0 +1,79 @@ +//go:build helm_project_operator + +package main + +import ( + _ "embed" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/operator" + "log" + "net/http" + _ "net/http/pprof" + + "github.com/rancher/prometheus-federator/pkg/version" + command "github.com/rancher/wrangler-cli" + _ "github.com/rancher/wrangler/pkg/generated/controllers/apiextensions.k8s.io" + _ "github.com/rancher/wrangler/pkg/generated/controllers/networking.k8s.io" + "github.com/rancher/wrangler/pkg/kubeconfig" + "github.com/spf13/cobra" +) + +const ( + // DummyHelmAPIVersion is the spec.helmApiVersion corresponding to the dummy project-operator-example chart + DummyHelmAPIVersion = "dummy.cattle.io/v1alpha1" + + // DummyReleaseName is the release name corresponding to the operator that deploys the dummy project-operator-example chart + DummyReleaseName = "dummy" +) + +var ( + // DummySystemNamespaces is the system namespaces scoped for the dummy project-operator-example chart. + DummySystemNamespaces = []string{"kube-system"} + + //go:embed fs/project-operator-example.tgz.base64 + base64TgzChart string + + debugConfig command.DebugConfig +) + +type DummyOperator struct { + // Note: all Project Operator are expected to provide these RuntimeOptions + common.RuntimeOptions + + Kubeconfig string `usage:"Kubeconfig file"` +} + +func (o *DummyOperator) Run(cmd *cobra.Command, _ []string) error { + go func() { + log.Println(http.ListenAndServe("localhost:6060", nil)) + }() + debugConfig.MustSetupDebug() + + cfg := kubeconfig.GetNonInteractiveClientConfig(o.Kubeconfig) + + ctx := cmd.Context() + + if err := operator.Init(ctx, o.Namespace, cfg, common.Options{ + OperatorOptions: common.OperatorOptions{ + HelmAPIVersion: DummyHelmAPIVersion, + ReleaseName: DummyReleaseName, + SystemNamespaces: DummySystemNamespaces, + ChartContent: base64TgzChart, + Singleton: false, + }, + RuntimeOptions: o.RuntimeOptions, + }); err != nil { + return err + } + + <-cmd.Context().Done() + return nil +} + +func main() { + cmd := command.Command(&DummyOperator{}, cobra.Command{ + Version: version.FriendlyVersion(), + }) + cmd = command.AddDebug(cmd, &debugConfig) + command.Main(cmd) +} diff --git a/cmd/prometheus-federator/main.go b/cmd/prometheus-federator/main.go index ddcbe1c3..804dce44 100644 --- a/cmd/prometheus-federator/main.go +++ b/cmd/prometheus-federator/main.go @@ -8,8 +8,8 @@ import ( "net/http" _ "net/http/pprof" - "github.com/rancher/helm-project-operator/pkg/controllers/common" - "github.com/rancher/helm-project-operator/pkg/operator" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/operator" "github.com/rancher/prometheus-federator/pkg/version" command "github.com/rancher/wrangler-cli" _ "github.com/rancher/wrangler/pkg/generated/controllers/apiextensions.k8s.io" diff --git a/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/doc.go b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/doc.go new file mode 100644 index 00000000..20da9d22 --- /dev/null +++ b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +// +k8s:deepcopy-gen=package +// +groupName=helm.cattle.io +package v1alpha1 diff --git a/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/genericmap.go b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/genericmap.go new file mode 100644 index 00000000..07e0a1f7 --- /dev/null +++ b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/genericmap.go @@ -0,0 +1,28 @@ +package v1alpha1 + +import ( + "gopkg.in/yaml.v2" + "k8s.io/apimachinery/pkg/runtime" +) + +// +kubebuilder:pruning:PreserveUnknownFields +// +kubebuilder:validation:EmbeddedResource + +// GenericMap is a wrapper on arbitrary JSON / YAML resources +type GenericMap map[string]interface{} + +func (in *GenericMap) DeepCopy() *GenericMap { + if in == nil { + return nil + } + out := new(GenericMap) + *out = runtime.DeepCopyJSON(*in) + return out +} + +func (in *GenericMap) ToYAML() ([]byte, error) { + if in == nil { + return []byte{}, nil + } + return yaml.Marshal(in) +} diff --git a/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/project.go b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/project.go new file mode 100644 index 00000000..e7f35853 --- /dev/null +++ b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/project.go @@ -0,0 +1,64 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ProjectHelmChart specifies a managed Helm chart that should be deployed for a "Project" (defined as any set +// of namespaces that can be targeted by a label selector) and be updated automatically on changing definitions +// of that project (e.g. namespaces added or removed). It is a parent object that creates HelmCharts and HelmReleases +// under the hood via wrangler.Apply and relatedresource.Watch +type ProjectHelmChart struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec ProjectHelmChartSpec `json:"spec"` + Status ProjectHelmChartStatus `json:"status"` +} + +// ProjectHelmChartSpec defines the spec of a ProjectHelmChart +type ProjectHelmChartSpec struct { + // HelmAPIVersion identifies whether a particular rendition of the Helm Project Operator + // should watch ProjectHelmChart of this type. e.g. monitoring.cattle.io/v1alpha1 is watched by Prometheus Federator + HelmAPIVersion string `json:"helmApiVersion"` + + // ProjectNamespaceSelector is a namespaceSelector that identifies the project this underlying chart should be targeting + // If a project label is provided as part of the Operator's runtime options, this field will be ignored since ProjectHelmCharts + // will be created in dedicated project namespaces with a pre-defined project namespace selector + ProjectNamespaceSelector *metav1.LabelSelector `json:"projectNamespaceSelector"` + + // Values is a generic map (e.g. generic yaml) representing the values.yaml used to configure the underlying Helm chart that + // will be deployed for this + Values GenericMap `json:"values"` +} + +type ProjectHelmChartStatus struct { + // DashboardValues are values provided to the ProjectHelmChart from ConfigMaps in the Project Release namespace + // tagged with 'helm.cattle.io/dashboard-values-configmap': '{{ .Release.Name }}' + DashboardValues GenericMap `json:"dashboardValues"` + + // Status is the current status of this ProjectHelmChart + // Please see pkg/controllers/project/status.go for possible states + Status string `json:"status"` + + // StatusMessage is a detailed message explaining the current status of the ProjectHelmChart + // Please see pkg/controllers/project/status.go for possible state messages + StatusMessage string `json:"statusMessage"` + + // SystemNamespace is the namespace where HelmCharts and HelmReleases will be deployed + SystemNamespace string `json:"systemNamespace"` + + // ReleaseNamespace is the namespace where the underlying Helm chart will be deployed + // Also known as the Project Release Namespace + ReleaseNamespace string `json:"releaseNamespace"` + + // ReleaseName is the name of the Helm Release contained in the Project Release Namespace + ReleaseName string `json:"releaseName"` + + // TargetNamespaces are the current set of namespaces targeted by the namespaceSelector + // that this ProjectHelmChart was configured with. As noted above, this will correspond + // to the Project Registration Namespace's selector if project label is provided + TargetNamespaces []string `json:"targetNamespaces"` +} diff --git a/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_deepcopy.go b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_deepcopy.go new file mode 100644 index 00000000..24b0d001 --- /dev/null +++ b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_deepcopy.go @@ -0,0 +1,142 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in GenericMap) DeepCopyInto(out *GenericMap) { + { + in := &in + clone := in.DeepCopy() + *out = *clone + return + } +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectHelmChart) DeepCopyInto(out *ProjectHelmChart) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectHelmChart. +func (in *ProjectHelmChart) DeepCopy() *ProjectHelmChart { + if in == nil { + return nil + } + out := new(ProjectHelmChart) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProjectHelmChart) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectHelmChartList) DeepCopyInto(out *ProjectHelmChartList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ProjectHelmChart, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectHelmChartList. +func (in *ProjectHelmChartList) DeepCopy() *ProjectHelmChartList { + if in == nil { + return nil + } + out := new(ProjectHelmChartList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProjectHelmChartList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectHelmChartSpec) DeepCopyInto(out *ProjectHelmChartSpec) { + *out = *in + if in.ProjectNamespaceSelector != nil { + in, out := &in.ProjectNamespaceSelector, &out.ProjectNamespaceSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + in.Values.DeepCopyInto(&out.Values) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectHelmChartSpec. +func (in *ProjectHelmChartSpec) DeepCopy() *ProjectHelmChartSpec { + if in == nil { + return nil + } + out := new(ProjectHelmChartSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectHelmChartStatus) DeepCopyInto(out *ProjectHelmChartStatus) { + *out = *in + in.DashboardValues.DeepCopyInto(&out.DashboardValues) + if in.TargetNamespaces != nil { + in, out := &in.TargetNamespaces, &out.TargetNamespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectHelmChartStatus. +func (in *ProjectHelmChartStatus) DeepCopy() *ProjectHelmChartStatus { + if in == nil { + return nil + } + out := new(ProjectHelmChartStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_list_types.go b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_list_types.go new file mode 100644 index 00000000..1dcc3adc --- /dev/null +++ b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_list_types.go @@ -0,0 +1,42 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +// +k8s:deepcopy-gen=package +// +groupName=helm.cattle.io +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ProjectHelmChartList is a list of ProjectHelmChart resources +type ProjectHelmChartList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []ProjectHelmChart `json:"items"` +} + +func NewProjectHelmChart(namespace, name string, obj ProjectHelmChart) *ProjectHelmChart { + obj.APIVersion, obj.Kind = SchemeGroupVersion.WithKind("ProjectHelmChart").ToAPIVersionAndKind() + obj.Name = name + obj.Namespace = namespace + return &obj +} diff --git a/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_register.go b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_register.go new file mode 100644 index 00000000..d43d4a0e --- /dev/null +++ b/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1/zz_generated_register.go @@ -0,0 +1,60 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +// +k8s:deepcopy-gen=package +// +groupName=helm.cattle.io +package v1alpha1 + +import ( + helm "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + ProjectHelmChartResourceName = "projecthelmcharts" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: helm.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &ProjectHelmChart{}, + &ProjectHelmChartList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/helm-project-operator/apis/helm.cattle.io/zz_generated_register.go b/pkg/helm-project-operator/apis/helm.cattle.io/zz_generated_register.go new file mode 100644 index 00000000..11c331c8 --- /dev/null +++ b/pkg/helm-project-operator/apis/helm.cattle.io/zz_generated_register.go @@ -0,0 +1,24 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package helm + +const ( + // Package-wide consts from generator "zz_generated_register". + GroupName = "helm.cattle.io" +) diff --git a/pkg/helm-project-operator/applier/applyinator.go b/pkg/helm-project-operator/applier/applyinator.go new file mode 100644 index 00000000..7b7a58ea --- /dev/null +++ b/pkg/helm-project-operator/applier/applyinator.go @@ -0,0 +1,142 @@ +package applier + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/workqueue" +) + +var ( + defaultRateLimiter = workqueue.NewMaxOfRateLimiter( + workqueue.NewItemFastSlowRateLimiter(time.Millisecond, 2*time.Minute, 30), + workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 30*time.Second), + ) +) + +// ApplyFunc is a func that needs to be applied on seeing a particular key be passed to an Applyinator +type ApplyFunc func(key string) error + +// Options are options that can be specified to configure a desired Applyinator +type Options struct { + RateLimiter workqueue.RateLimiter +} + +// Applyinator is an interface that eventually ensures that a requested action, identified by some key, +// is applied. Any object that implements Applyinator should provide the same guarantees as the +// k8s.io/client-go/util/workqueue implementation, namely: +// +// * Fair: items processed in the order in which they are added. +// * Stingy: a single item will not be processed multiple times concurrently, +// and if an item is added multiple times before it can be processed, it +// will only be processed once. +// * Multiple consumers and producers. In particular, it is allowed for an +// item to be reenqueued while it is being processed. +type Applyinator interface { + Apply(key string) + Run(ctx context.Context, workers int) +} + +// NewApplyinator allows you to register a function that applies an action based on whether a particular +// key is enqueued via a call to Apply. It implements k8s.io/client-go/util/workqueue under the hood, which +// allows us to ensure that the apply function is called with the following guarantees (provided by workqueues): +// +// * Fair: items processed in the order in which they are added. +// * Stingy: a single item will not be processed multiple times concurrently, +// and if an item is added multiple times before it can be processed, it +// will only be processed once. +// * Multiple consumers and producers. In particular, it is allowed for an +// item to be reenqueued while it is being processed. +func NewApplyinator(name string, applyFunc ApplyFunc, opts *Options) Applyinator { + opts = applyDefaultOptions(opts) + return &applyinator{ + workqueue: workqueue.NewNamedRateLimitingQueue(opts.RateLimiter, name), + apply: applyFunc, + } +} + +func applyDefaultOptions(opts *Options) *Options { + var newOpts Options + if opts != nil { + newOpts = *opts + } + if newOpts.RateLimiter == nil { + newOpts.RateLimiter = defaultRateLimiter + logrus.Debug("No rate limiter supplied, using default rate limiter.") + } + return &newOpts +} + +type applyinator struct { + workqueue workqueue.RateLimitingInterface + apply ApplyFunc +} + +// Apply triggers the Applyinator to run the provided apply func on the given key +// whenever the workqueue processes the next item +func (a *applyinator) Apply(key string) { + a.workqueue.Add(key) +} + +// Run allows the applyinator to start processing items added to its workqueue +func (a *applyinator) Run(ctx context.Context, workers int) { + + logrus.Debugf("Adding items to applyinator work queue. Workers: %d", workers) + go func() { + <-ctx.Done() + a.workqueue.ShutDown() + }() + for i := 0; i < workers; i++ { + go wait.Until(a.runWorker, time.Second, ctx.Done()) + } +} + +func (a *applyinator) runWorker() { + for a.processNextWorkItem() { + } +} + +func (a *applyinator) processNextWorkItem() bool { + obj, shutdown := a.workqueue.Get() + + if shutdown { + logrus.Debug("ProcessNextWorkItem called during shutdown. Exiting function.") + return false + } + + if err := a.processSingleItem(obj); err != nil { + if !strings.Contains(err.Error(), "please apply your changes to the latest version and try again") { + logrus.Errorf("%v", err) + } + return true + } + + return true +} + +func (a *applyinator) processSingleItem(obj interface{}) error { + var ( + key string + ok bool + ) + + defer a.workqueue.Done(obj) + + if key, ok = obj.(string); !ok { + a.workqueue.Forget(obj) + logrus.Errorf("expected string in workqueue but got %#v", obj) + return nil + } + if err := a.apply(key); err != nil { + a.workqueue.AddRateLimited(key) + return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error()) + } + + logrus.Debugf("Call to processSingleItem was successful for key: %s", key) + a.workqueue.Forget(obj) + return nil +} diff --git a/pkg/helm-project-operator/codegen/cleanup/main.go b/pkg/helm-project-operator/codegen/cleanup/main.go new file mode 100644 index 00000000..3c62756e --- /dev/null +++ b/pkg/helm-project-operator/codegen/cleanup/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "os" + + "github.com/rancher/wrangler/pkg/cleanup" + "github.com/sirupsen/logrus" +) + +func main() { + if err := cleanup.Cleanup("./pkg/helm-project-operator/apis"); err != nil { + logrus.Fatal(err) + } + if err := os.RemoveAll("./pkg/helm-project-operator/generated"); err != nil { + logrus.Fatal(err) + } + if err := os.RemoveAll("./crds/helm-project-operator"); err != nil { + logrus.Fatal(err) + } +} diff --git a/pkg/helm-project-operator/codegen/main.go b/pkg/helm-project-operator/codegen/main.go new file mode 100644 index 00000000..f162d891 --- /dev/null +++ b/pkg/helm-project-operator/codegen/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/crd" + "os" + + "github.com/sirupsen/logrus" + + controllergen "github.com/rancher/wrangler/pkg/controller-gen" + "github.com/rancher/wrangler/pkg/controller-gen/args" +) + +func main() { + if len(os.Args) > 3 && os.Args[1] == "crds" { + if len(os.Args) != 4 { + logrus.Fatal("usage: ./codegen crds ") + } + logrus.Infof("Writing CRDs to %s and %s", os.Args[2], os.Args[3]) + if err := crd.WriteFiles(os.Args[2], os.Args[3]); err != nil { + panic(err) + } + return + } + + os.Unsetenv("GOPATH") + controllergen.Run(args.Options{ + OutputPackage: "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated", + Boilerplate: "gen/boilerplate.go.txt", + Groups: map[string]args.Group{ + "helm.cattle.io": { + Types: []interface{}{ + v1alpha1.ProjectHelmChart{}, + }, + GenerateTypes: true, + }, + }, + }) +} diff --git a/pkg/helm-project-operator/controllers/common/formats.go b/pkg/helm-project-operator/controllers/common/formats.go new file mode 100644 index 00000000..5bf37336 --- /dev/null +++ b/pkg/helm-project-operator/controllers/common/formats.go @@ -0,0 +1,8 @@ +package common + +const ( + // ProjectRegistrationNamespaceFmt is the format used in order to create project registration namespaces if ProjectLabel is provided + // If SystemProjectLabel is also provided, the project release namespace will be this namespace with `-` suffixed, where + // ReleaseName is provided by the Project Operator that implements Helm Project Operator + ProjectRegistrationNamespaceFmt = "cattle-project-%s" +) diff --git a/pkg/helm-project-operator/controllers/common/hardening.go b/pkg/helm-project-operator/controllers/common/hardening.go new file mode 100644 index 00000000..7b29e12e --- /dev/null +++ b/pkg/helm-project-operator/controllers/common/hardening.go @@ -0,0 +1,53 @@ +package common + +import ( + "os" + "path/filepath" + + "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" +) + +// HardeningOptions are options that can be provided to override the default hardening resources applied to all namespaces +// created by this Project Operator. To disable this, specify DisableHardening in the RuntimeOptions. +type HardeningOptions struct { + // ServiceAccount represents the overrides to be supplied to the default service account patched by the hardening controller + ServiceAccount *DefaultServiceAccountOptions `yaml:"serviceAccountSpec"` + // NetworkPolicy represents the overrides to be supplied to the generated NetworkPolicy created by the hardening controller + NetworkPolicy *DefaultNetworkPolicyOptions `yaml:"networkPolicySpec"` +} + +// DefaultServiceAccountOptions represents the overrides to be supplied to the default Service Account's fields +// Note: the values of these fields is identical to what is defined on the corev1.ServiceAccount object +type DefaultServiceAccountOptions struct { + Secrets []corev1.ObjectReference `yaml:"secrets,omitempty"` + ImagePullSecrets []corev1.LocalObjectReference `yaml:"imagePullSecrets,omitempty"` + AutomountServiceAccountToken *bool `yaml:"automountServiceAccountToken,omitEmpty"` +} + +// DefaultNetworkPolicyOptions is the NetworkPolicySpec specified for generated NetworkPolicy created by the hardening controller +type DefaultNetworkPolicyOptions networkingv1.NetworkPolicySpec + +// LoadHardeningOptionsFromFile unmarshalls the struct found at the file to YAML and reads it into memory +func LoadHardeningOptionsFromFile(path string) (HardeningOptions, error) { + var hardeningOptions HardeningOptions + wd, err := os.Getwd() + if err != nil { + return HardeningOptions{}, err + } + abspath := filepath.Join(wd, path) + _, err = os.Stat(abspath) + if err != nil { + if os.IsNotExist(err) { + // we just assume the default is used + err = nil + } + return HardeningOptions{}, err + } + hardeningOptionsBytes, err := os.ReadFile(abspath) + if err != nil { + return hardeningOptions, err + } + return hardeningOptions, yaml.UnmarshalStrict(hardeningOptionsBytes, &hardeningOptions) +} diff --git a/pkg/helm-project-operator/controllers/common/operator.go b/pkg/helm-project-operator/controllers/common/operator.go new file mode 100644 index 00000000..df17662e --- /dev/null +++ b/pkg/helm-project-operator/controllers/common/operator.go @@ -0,0 +1,50 @@ +package common + +import ( + "errors" + + "github.com/sirupsen/logrus" +) + +// OperatorOptions are options provided by an operator that is implementing Helm Project Operator +type OperatorOptions struct { + // HelmAPIVersion is the unique API version marking ProjectHelmCharts that this Helm Project Operator should watch for + HelmAPIVersion string + + // ReleaseName is a name that identifies releases created for this operator + ReleaseName string + + // SystemNamespaces are additional operator namespaces to treat as if they are system namespaces whether or not + // they are marked via some sort of annotation + SystemNamespaces []string + + // ChartContent is the base64 tgz contents of the folder containing the Helm chart that needs to be deployed + ChartContent string + + // Singleton marks whether only a single ProjectHelmChart can exist per registration namespace + // If enabled, it will ensure that releases are named based on the registration namespace rather than + // the name provided on the ProjectHelmChart, which is what triggers an UnableToCreateHelmRelease status + // on the ProjectHelmChart created after this one + Singleton bool +} + +// Validate validates the provided OperatorOptions +func (opts OperatorOptions) Validate() error { + if len(opts.HelmAPIVersion) == 0 { + return errors.New("must provide a spec.helmApiVersion that this project operator is being initialized for") + } + + if len(opts.ReleaseName) == 0 { + return errors.New("must provide name of Helm release that this project operator should deploy") + } + + if len(opts.SystemNamespaces) > 0 { + logrus.Infof("Marking the following namespaces as system namespaces: %s", opts.SystemNamespaces) + } + + if len(opts.ChartContent) == 0 { + return errors.New("cannot instantiate Project Operator without bundling a Helm chart to provide for the HelmChart's spec.ChartContent") + } + + return nil +} diff --git a/pkg/helm-project-operator/controllers/common/operatorlabels.go b/pkg/helm-project-operator/controllers/common/operatorlabels.go new file mode 100644 index 00000000..38a0d667 --- /dev/null +++ b/pkg/helm-project-operator/controllers/common/operatorlabels.go @@ -0,0 +1,96 @@ +package common + +import ( + "fmt" + "strings" +) + +// Operator Labels +// Note: These labels are automatically applied by the operator to mark resources that are created for a given ProjectHelmChart and Project Operator + +// Common + +const ( + // HelmProjectOperatedLabel marks all HelmCharts, HelmReleases, and namespaces created by this operator + HelmProjectOperatedLabel = "helm.cattle.io/helm-project-operated" + + // HelmProjectOperatorProjectLabel is applied to the Project Registration Namespace, the ProjectReleaseNamespace, and + // (only if both ProjectLabel and ProjectReleaseLabelValue are provided) to all Project namespaces + // + // If ProjectLabel and ProjectReleaseLabelValue are supplied, this label will be supplied to the global.cattle.projectNamespaceSelector + // to identify all namespaces tied to a given project + HelmProjectOperatorProjectLabel = "helm.cattle.io/projectId" +) + +// HasHelmProjectOperatedLabel returns whether a ProjectHelmChart has the Helm Project Operated label +func HasHelmProjectOperatedLabel(labels map[string]string) bool { + if labels == nil { + return false + } + _, ok := labels[HelmProjectOperatedLabel] + return ok +} + +// GetCommonLabels returns all common labels added to all generated resources +func GetCommonLabels(projectID string) map[string]string { + labels := map[string]string{ + HelmProjectOperatedLabel: "true", + } + if len(projectID) != 0 { + labels[HelmProjectOperatorProjectLabel] = projectID + } + return labels +} + +// Project Namespaces + +const ( + // HelmProjectOperatedNamespaceOrphanedLabel marks all auto-generated namespaces that no longer have resources tracked + // by this operator; if a namespace has this label, it is safe to delete + HelmProjectOperatedNamespaceOrphanedLabel = "helm.cattle.io/helm-project-operator-orphaned" +) + +// GetProjectNamespaceLabels returns the labels to be added to all Project Namespaces +func GetProjectNamespaceLabels(projectID, projectLabel, projectLabelValue string, isOrphaned bool) map[string]string { + labels := GetCommonLabels(projectID) + if isOrphaned { + labels[HelmProjectOperatedNamespaceOrphanedLabel] = "true" + } + labels[projectLabel] = projectLabelValue + return labels +} + +// GetProjectNamespaceAnnotations returns the annotations to be added to all Project Namespaces +// Note: annotations allow integration with Rancher Projects since they handle importing namespaces into Projects +func GetProjectNamespaceAnnotations(projectID, projectLabel, clusterID string) map[string]string { + projectIDWithClusterID := projectID + if len(clusterID) > 0 { + projectIDWithClusterID = fmt.Sprintf("%s:%s", clusterID, projectID) + } + return map[string]string{ + projectLabel: projectIDWithClusterID, + } +} + +// Helm Resources (HelmCharts and HelmReleases) + +const ( + // HelmProjectOperatorHelmAPIVersionLabel is a label that identifies the HelmAPIVersion that a HelmChart or HelmRelease is tied to + // This is used to identify whether a HelmChart or HelmRelease should be deleted from the cluster on uninstall + HelmProjectOperatorHelmAPIVersionLabel = "helm.cattle.io/helm-api-version" +) + +// GetHelmResourceLabels returns the labels to be added to all generated Helm resources (HelmCharts, HelmReleases) +func GetHelmResourceLabels(projectID, helmAPIVersion string) map[string]string { + labels := GetCommonLabels(projectID) + labels[HelmProjectOperatorHelmAPIVersionLabel] = strings.SplitN(helmAPIVersion, "/", 2)[0] + return labels +} + +// RoleBindings (created for Default K8s ClusterRole RBAC aggregation) + +const ( + // HelmProjectOperatorProjectHelmChartRoleBindingLabel is a label that identifies a RoleBinding as one that has been created in response to a ProjectHelmChart role + // The value of this label will be the release name of the Helm chart, which will be used to identify which ProjectHelmChart's enqueue should resynchronize this. + HelmProjectOperatorProjectHelmChartRoleBindingLabel = "helm.cattle.io/project-helm-chart-role-binding" +) diff --git a/pkg/helm-project-operator/controllers/common/options.go b/pkg/helm-project-operator/controllers/common/options.go new file mode 100644 index 00000000..f7ae2163 --- /dev/null +++ b/pkg/helm-project-operator/controllers/common/options.go @@ -0,0 +1,40 @@ +package common + +import ( + "github.com/sirupsen/logrus" +) + +// Options defines options that can be set on initializing the HelmProjectOperator +type Options struct { + RuntimeOptions + OperatorOptions +} + +// Validate validates the provided Options +func (opts Options) Validate() error { + if err := opts.OperatorOptions.Validate(); err != nil { + return err + } + + if err := opts.RuntimeOptions.Validate(); err != nil { + return err + } + + // Cross option checks + + if opts.Singleton { + logrus.Infof("Note: Operator only supports a single ProjectHelmChart per project registration namespace") + if len(opts.ProjectLabel) == 0 { + logrus.Warnf("It is only recommended to run a singleton Project Operator when --project-label is provided (currently not set). The current configuration of this operator would only allow a single ProjectHelmChart to be managed by this Operator.") + } + } + + for subjectRole, defaultClusterRoleName := range GetDefaultClusterRoles(opts) { + logrus.Infof("RoleBindings will automatically be created for Roles in the Project Release Namespace marked with '%s': '' "+ + "and '%s': '%s' based on ClusterRoleBindings or RoleBindings in the Project Registration namespace tied to ClusterRole %s", + HelmProjectOperatorProjectHelmChartRoleLabel, HelmProjectOperatorProjectHelmChartRoleAggregateFromLabel, subjectRole, defaultClusterRoleName, + ) + } + + return nil +} diff --git a/pkg/helm-project-operator/controllers/common/rbac.go b/pkg/helm-project-operator/controllers/common/rbac.go new file mode 100644 index 00000000..6f3dc261 --- /dev/null +++ b/pkg/helm-project-operator/controllers/common/rbac.go @@ -0,0 +1,53 @@ +package common + +import ( + rbacv1 "k8s.io/api/rbac/v1" +) + +// GetDefaultClusterRoles returns the default ClusterRoles that this operator was started with +func GetDefaultClusterRoles(opts Options) map[string]string { + clusterRoles := make(map[string]string) + if len(opts.AdminClusterRole) > 0 { + clusterRoles["admin"] = opts.AdminClusterRole + } + if len(opts.EditClusterRole) > 0 { + clusterRoles["edit"] = opts.EditClusterRole + } + if len(opts.ViewClusterRole) > 0 { + clusterRoles["view"] = opts.ViewClusterRole + } + return clusterRoles +} + +// IsDefaultClusterRoleRef returns whether the provided name is a default ClusterRole ref that this operator was +// started with (e.g. the values provided to AdminClusterRole, EditClusterRole, or ViewClusterRole in RuntimeOptions) +func IsDefaultClusterRoleRef(opts Options, roleRefName string) (string, bool) { + for subjectRole, defaultClusterRoleName := range GetDefaultClusterRoles(opts) { + if roleRefName == defaultClusterRoleName { + return subjectRole, true + } + } + return "", false +} + +// FilterToUsersAndGroups returns a subset of the provided subjects that are only Users and Groups +// i.e. it filters out ServiceAccount subjects +func FilterToUsersAndGroups(subjects []rbacv1.Subject) []rbacv1.Subject { + var filtered []rbacv1.Subject + for _, subject := range subjects { + if subject.APIGroup != rbacv1.GroupName { + continue + } + if subject.Kind != rbacv1.UserKind && subject.Kind != rbacv1.GroupKind { + // we do not automatically bind service accounts, only users and groups + continue + } + // note: we are purposefully omitting namespace here since it is not necessary even if set + filtered = append(filtered, rbacv1.Subject{ + APIGroup: subject.APIGroup, + Kind: subject.Kind, + Name: subject.Name, + }) + } + return filtered +} diff --git a/pkg/helm-project-operator/controllers/common/runtime.go b/pkg/helm-project-operator/controllers/common/runtime.go new file mode 100644 index 00000000..4598423f --- /dev/null +++ b/pkg/helm-project-operator/controllers/common/runtime.go @@ -0,0 +1,159 @@ +package common + +import ( + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "os" + "path/filepath" + + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v2" +) + +type RuntimeOptions struct { + // Namespace is the systemNamespace to create HelmCharts and HelmReleases in + // It's generally expected that this namespace is not widely accessible by all users in your cluster; it's recommended that it is placed + // in something akin to a System Project that is locked down in terms of permissions since resources like HelmCharts and HelmReleases are deployed there + Namespace string `usage:"Namespace to create HelmCharts and HelmReleases; if ProjectLabel is not provided, this will also be the namespace to watch ProjectHelmCharts" default:"cattle-helm-system" env:"NAMESPACE"` + + // NodeName is the name of the node running the operator; it adds additional information to events about where they were generated from + NodeName string `usage:"Name of the node this controller is running on" env:"NODE_NAME"` + + // ControllerName is the name of the controller that identifies this operator; this ensures that all HelmCharts and HelmReleases have the correct managed-by annotation + // so that multiple iterations of this operator in the same namespace do not try to manage the same HelmChart and HelmRelease objects + ControllerName string `usage:"Unique name to identify this controller that is added to all HelmCharts tracked by this controller" default:"helm-project-operator" env:"CONTROLLER_NAME"` + + // HelmJobImage is the job image to use to run the HelmChart job (default rancher/klipper-helm:v0.7.0-build20220315) + // Generally, this HelmJobImage can be left undefined, but may be necessary to be set if you are running with a non-default image + HelmJobImage string `usage:"Job image to use to perform helm operations on HelmChart creation" env:"HELM_JOB_IMAGE"` + + // ClusterID identifies the cluster that the operator is being operated frmo within; it adds an additional annotation to project registration + // namespaces that indicates the projectID with the cluster label. + // + // Note: primarily used for integration with Rancher Projects + ClusterID string `usage:"Identifies the cluster this controller is running on. Ignored if --project-label is not provided." env:"CLUSTER_ID"` + + // SystemDefaultRegistry is the prefix to be added to all images deployed by the HelmChart embedded into the Project Operator + // to point at the right set of images that need to be deployed. This is usually provided in Rancher as global.cattle.systemDefaultRegistry + SystemDefaultRegistry string `usage:"Default system registry to use for Docker images deployed by underlying Helm Chart. Provided as global.cattle.systemDefaultRegistry in the Helm Chart" env:"SYSTEM_DEFAULT_REGISTRY"` + + // CattleURL is the Rancher URL that this chart has been deployed onto. This is usually provided in Rancher Helm charts as global.cattle.url + CattleURL string `usage:"Default Rancher URL to provide to the Helm chart under global.cattle.url" env:"CATTLE_URL"` + + // ProjectLabel is the label that identifies projects + // Note: this field is optional and ensures that ProjectHelmCharts auto-infer their spec.projectNamespaceSelector + // If provided, any spec.projectNamespaceSelector provided will be ignored + // example: field.cattle.io/projectId + ProjectLabel string `usage:"Label on namespaces to create Project Registration Namespaces and watch for ProjectHelmCharts" env:"PROJECT_LABEL"` + + // SystemProjectLabelValues are values of ProjectLabel that identify system namespaces. Does nothing if ProjectLabel is not provided + // example: p-ranch + // If both this and the ProjectLabel example are provided, any namespaces with label 'field.cattle.io/projectId: ' + // will be treated as a systemNamespace, which means that no ProjectHelmChart will be allowed to select it + SystemProjectLabelValues []string `usage:"Values on project label on namespaces that marks it as a system namespace" env:"SYSTEM_PROJECT_LABEL_VALUE"` + + // ProjectReleaseLabelValue is the value of the ProjectLabel that should be added to Project Release Namespaces. Does nothing if ProjectLabel is not provided + // example: p-ranch + // If provided, dedicated Project Release namespaces will be created in the cluster for each ProjectHelmChart that needs a Helm Release + // The created Project Release namespaces will also automatically be identified as a System Project Namespaces based on this label, so other + // namespaces with this label value will be treated as a system namespace as well + ProjectReleaseLabelValue string `usage:"Value on project label on namespaces that marks it as a system namespace" env:"SYSTEM_PROJECT_LABEL_VALUE"` + + // AdminClusterRole configures the operator to automaticaly create RoleBindings on Roles in the Project Release Namespace marked with + // 'helm.cattle.io/project-helm-chart-role': '' and 'helm.cattle.io/project-helm-chart-role-aggregate-from': 'admin' + // based on ClusterRoleBindings or RoleBindings in the Project Registration namespace tied to the provided ClusterRole, if it exists + AdminClusterRole string `usage:"ClusterRole tied to admin users who should have permissions in the Project Release Namespace" env:"ADMIN_CLUSTER_ROLE"` + + // EditClusterRole configures the operator to automaticaly create RoleBindings on Roles in the Project Release Namespace marked with + // 'helm.cattle.io/project-helm-chart-role': '' and 'helm.cattle.io/project-helm-chart-role-aggregate-from': 'edit' + // based on ClusterRoleBindings or RoleBindings in the Project Registration namespace tied to the provided ClusterRole, if it exists + EditClusterRole string `usage:"ClusterRole tied to edit users who should have permissions in the Project Release Namespace" env:"EDIT_CLUSTER_ROLE"` + + // ViewClusterRole configures the operator to automaticaly create RoleBindings on Roles in the Project Release Namespace marked with + // 'helm.cattle.io/project-helm-chart-role': '' and 'helm.cattle.io/project-helm-chart-role-aggregate-from': 'view' + // based on ClusterRoleBindings or RoleBindings in the Project Registration namespace tied to the provided ClusterRole, if it exists + ViewClusterRole string `usage:"ClusterRole tied to view users who should have permissions in the Project Release Namespace" env:"VIEW_CLUSTER_ROLE"` + + // DisableHardening turns off the controller that manages the default service account and a default NetworkPolicy deployed on all + // namespaces marked with the Helm Project Operated Label to prevent generated namespaces from breaking a CIS 1.16 Hardened Scan by patching + // the default ServiceAccount and creating a default secure NetworkPolicy. + // + // ref: https://docs.rke2.io/security/cis_self_assessment16/#515 + // ref: https://docs.rke2.io/security/cis_self_assessment16/#532 + // + // To configure the default ServiceAccount and NetworkPolicy across all generated namespaces, you can provide overrides in the HardeningOptionsFile + // If you need to configure the default ServiceAccount and NetworkPolicy on a per-namespace basis, it is recommended that you disable this + DisableHardening bool `usage:"Path to file that contains the configuration for the default ServiceAccount and NetworkPolicy deployed on operated namespaces" env:"HARDENING_OPTIONS_FILE"` + + // HardeningOptionsFile is the path to the file that contains the configuration for the default ServiceAccount and NetworkPolicy deployed on operated namespaces + // By default, the default service account of the namespace is patched to disable automountServiceAccountToken + // By default, a default NetworkPolicy is deployed in the namespace that selects all pods in the namespace and limits all ingress and egress + HardeningOptionsFile string `usage:"Path to file that contains the configuration for the default ServiceAccount and NetworkPolicy deployed on operated namespaces" default:"hardening.yaml" env:"HARDENING_OPTIONS_FILE"` + + // ValuesOverrideFile is the path to the file that contains operated-provided overrides on the values.yaml that should be applied for each ProjectHelmChart + ValuesOverrideFile string `usage:"Path to file that contains values.yaml overrides supplied by the operator" default:"values.yaml" env:"VALUES_OVERRIDE_FILE"` + + // DisableEmbeddedHelmLocker determines whether to disable embedded Helm Locker controller in favor of external Helm Locker + DisableEmbeddedHelmLocker bool `usage:"Whether to disable embedded Helm Locker controller in favor of external Helm Locker" env:"DISABLE_EMBEDDED_HELM_LOCKER"` + + // DisableEmbeddedHelmController determines whether to disable embedded Helm Controller controller in favor of external Helm Controller + // This should be the default in most RKE2 clusters since the RKE2 server binary already embeds a Helm Controller instance that manages HelmCharts + DisableEmbeddedHelmController bool `usage:"Whether to disable embedded Helm Controller controller in favor of external Helm Controller (recommended for RKE2 clusters)" env:"DISABLE_EMBEDDED_HELM_CONTROLLER"` +} + +// Validate validates the provided RuntimeOptions +func (opts RuntimeOptions) Validate() error { + if len(opts.ProjectLabel) > 0 { + logrus.Infof("Creating dedicated project registration namespaces to discover ProjectHelmCharts based on the value found for the project label '%s' on all namespaces in the cluster, excluding system namespaces; these namespaces will need to be manually cleaned up if they have the label '%s': 'true'", opts.ProjectLabel, HelmProjectOperatedNamespaceOrphanedLabel) + if len(opts.SystemProjectLabelValues) > 0 { + for _, systemProjectLabel := range opts.SystemProjectLabelValues { + logrus.Infof("Assuming namespaces tagged with %s=%s are also system namespaces", opts.ProjectLabel, systemProjectLabel) + } + } + if len(opts.ProjectReleaseLabelValue) > 0 { + logrus.Infof("Assuming namespaces tagged with %s=%s are also system namespaces", opts.ProjectLabel, opts.ProjectReleaseLabelValue) + logrus.Infof("Creating dedicated project release namespaces for ProjectHelmCharts with label '%s': '%s'; these namespaces will need to be manually cleaned up if they have the label '%s': 'true'", opts.ProjectLabel, opts.ProjectReleaseLabelValue, HelmProjectOperatedNamespaceOrphanedLabel) + } + if len(opts.ClusterID) > 0 { + logrus.Infof("Marking project registration namespaces with %s=%s:", opts.ProjectLabel, opts.ClusterID) + } + } + + if len(opts.HelmJobImage) > 0 { + logrus.Infof("Using %s as spec.JobImage on all generated HelmChart resources", opts.HelmJobImage) + } + + if len(opts.NodeName) > 0 { + logrus.Infof("Marking events as being sourced from node %s", opts.NodeName) + } + + if opts.DisableHardening { + logrus.Info("Hardening is disabled") + } else { + logrus.Info("Managing the configuration of the default ServiceAccount and an auto-generated NetworkPolicy in all namespaces managed by this Project Operator") + } + + return nil +} + +// LoadValuesOverrideFromFile unmarshalls the struct found at the file to YAML and reads it into memory +func LoadValuesOverrideFromFile(path string) (v1alpha1.GenericMap, error) { + var valuesOverride v1alpha1.GenericMap + wd, err := os.Getwd() + if err != nil { + return nil, err + } + abspath := filepath.Join(wd, path) + _, err = os.Stat(abspath) + if err != nil { + if os.IsNotExist(err) { + // we just assume the default is used + err = nil + } + return nil, err + } + valuesOverrideBytes, err := os.ReadFile(abspath) + if err != nil { + return nil, err + } + return valuesOverride, yaml.Unmarshal(valuesOverrideBytes, &valuesOverride) +} diff --git a/pkg/helm-project-operator/controllers/common/userlabels.go b/pkg/helm-project-operator/controllers/common/userlabels.go new file mode 100644 index 00000000..b69f5c90 --- /dev/null +++ b/pkg/helm-project-operator/controllers/common/userlabels.go @@ -0,0 +1,49 @@ +package common + +import ( + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" +) + +// User-Applied Labels +// Note: These labels are expected to be applied by users (or by Jobs, in the case of cleanup), to mark a resources as one that needs +// some special logic to be applied by the Helm Project Operator on changes + +// ProjectHelmCharts + +const ( + // HelmProjectOperatedCleanupLabel is a label attached to ProjectHelmCharts to facilitate cleanup; all ProjectHelmCharts + // with this label will have their HelmCharts and HelmReleases cleaned up until the next time the Operator is deployed; + // on redeploying the operator, this label will automatically be removed from all ProjectHelmCharts deployed in the cluster. + HelmProjectOperatedCleanupLabel = "helm.cattle.io/helm-project-operator-cleanup" +) + +// HasCleanupLabel returns whether a ProjectHelmChart has the cleanup label +func HasCleanupLabel(projectHelmChart *v1alpha1.ProjectHelmChart) bool { + if projectHelmChart.Labels == nil { + return false + } + value, shouldCleanup := projectHelmChart.Labels[HelmProjectOperatedCleanupLabel] + return shouldCleanup && value == "true" +} + +// Project Release Namespace ConfigMaps + +const ( + // HelmProjectOperatorDashboardValuesConfigMapLabel is a label that identifies a ConfigMap that should be merged into status.dashboardValues when available + // The value of this label will be the release name of the Helm chart, which will be used to identify which ProjectHelmChart's status needs to be updated. + HelmProjectOperatorDashboardValuesConfigMapLabel = "helm.cattle.io/dashboard-values-configmap" +) + +// Project Release Namespace Roles + +const ( + // HelmProjectOperatorProjectHelmChartRoleLabel is a label that identifies a Role as one that needs RoleBindings to be managed by the Helm Project Operator + // The value of this label will be the release name of the Helm chart, which will be used to identify which ProjectHelmChart's enqueue should resynchronize this. + HelmProjectOperatorProjectHelmChartRoleLabel = "helm.cattle.io/project-helm-chart-role" + + // HelmProjectOperatorProjectHelmChartRoleAggregateFromLabel is a label that identifies which subjects should be bound to the Project Helm Chart Role + // The value of this label will be the name of the default k8s ClusterRoles (cluster-admin, admin, edit, view). For the provided ClusterRole, + // the operator will automatically create a RoleBinding in the Project Release Namespace binding all subjects who have that permission across all namespaces in the project + // to the Role that contains this label. This label will only be viewed if the Role has HelmProjectOperatorProjectHelmChartRoleLabel set as well + HelmProjectOperatorProjectHelmChartRoleAggregateFromLabel = "helm.cattle.io/project-helm-chart-role-aggregate-from" +) diff --git a/pkg/helm-project-operator/controllers/controllers.go b/pkg/helm-project-operator/controllers/controllers.go new file mode 100644 index 00000000..c6435697 --- /dev/null +++ b/pkg/helm-project-operator/controllers/controllers.go @@ -0,0 +1,367 @@ +package controllers + +import ( + "context" + "errors" + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/hardened" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/namespace" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/project" + helmproject "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io" + helmprojectcontroller "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1" + "time" + + "github.com/k3s-io/helm-controller/pkg/controllers/chart" + k3shelm "github.com/k3s-io/helm-controller/pkg/generated/controllers/helm.cattle.io" + k3shelmcontroller "github.com/k3s-io/helm-controller/pkg/generated/controllers/helm.cattle.io/v1" + "github.com/rancher/lasso/pkg/cache" + "github.com/rancher/lasso/pkg/client" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/prometheus-federator/pkg/helm-locker/controllers/release" + helmlocker "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io" + helmlockercontroller "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-locker/objectset" + "github.com/rancher/wrangler/pkg/apply" + batch "github.com/rancher/wrangler/pkg/generated/controllers/batch" + batchcontroller "github.com/rancher/wrangler/pkg/generated/controllers/batch/v1" + "github.com/rancher/wrangler/pkg/generated/controllers/core" + corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" + "github.com/rancher/wrangler/pkg/generated/controllers/networking.k8s.io" + networkingcontroller "github.com/rancher/wrangler/pkg/generated/controllers/networking.k8s.io/v1" + rbac "github.com/rancher/wrangler/pkg/generated/controllers/rbac" + rbaccontroller "github.com/rancher/wrangler/pkg/generated/controllers/rbac/v1" + "github.com/rancher/wrangler/pkg/generic" + "github.com/rancher/wrangler/pkg/leader" + "github.com/rancher/wrangler/pkg/ratelimit" + "github.com/rancher/wrangler/pkg/schemes" + "github.com/rancher/wrangler/pkg/start" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + typedv1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" +) + +type appContext struct { + helmprojectcontroller.Interface + + Dynamic dynamic.Interface + K8s kubernetes.Interface + Core corecontroller.Interface + Networking networkingcontroller.Interface + + HelmLocker helmlockercontroller.Interface + ObjectSetRegister objectset.LockableRegister + ObjectSetHandler *controller.SharedHandler + + HelmController k3shelmcontroller.Interface + Batch batchcontroller.Interface + RBAC rbaccontroller.Interface + + Apply apply.Apply + EventBroadcaster record.EventBroadcaster + + ClientConfig clientcmd.ClientConfig + starters []start.Starter +} + +func (a *appContext) start(ctx context.Context) error { + return start.All(ctx, 50, a.starters...) +} + +// Register registers all controllers for the Helm Project Operator based on the provided options +func Register(ctx context.Context, systemNamespace string, cfg clientcmd.ClientConfig, opts common.Options) error { + if len(systemNamespace) == 0 { + return errors.New("cannot start controllers on system namespace: system namespace not provided") + } + // always add the systemNamespace to the systemNamespaces provided + opts.SystemNamespaces = append(opts.SystemNamespaces, systemNamespace) + + // parse values.yaml and questions.yaml from file + valuesYaml, questionsYaml, err := parseValuesAndQuestions(opts.ChartContent) + if err != nil { + logrus.Fatal(err) + } + + appCtx, err := newContext(cfg, systemNamespace, opts) + if err != nil { + return err + } + + appCtx.EventBroadcaster.StartLogging(logrus.Debugf) + appCtx.EventBroadcaster.StartRecordingToSink(&typedv1.EventSinkImpl{ + Interface: appCtx.K8s.CoreV1().Events(systemNamespace), + }) + recorder := appCtx.EventBroadcaster.NewRecorder(schemes.All, corev1.EventSource{ + Component: "helm-project-operator", + Host: opts.NodeName, + }) + + if !opts.DisableHardening { + hardeningOpts, err := common.LoadHardeningOptionsFromFile(opts.HardeningOptionsFile) + if err != nil { + return err + } + hardened.Register(ctx, + appCtx.Apply, + hardeningOpts, + // watches + appCtx.Core.Namespace(), + appCtx.Core.Namespace().Cache(), + // generates + appCtx.Core.ServiceAccount(), + appCtx.Networking.NetworkPolicy(), + ) + } + + projectGetter := namespace.Register(ctx, + appCtx.Apply, + systemNamespace, + valuesYaml, + questionsYaml, + opts, + // watches and generates + appCtx.Core.Namespace(), + appCtx.Core.Namespace().Cache(), + appCtx.Core.ConfigMap(), + // enqueues + appCtx.ProjectHelmChart(), + appCtx.ProjectHelmChart().Cache(), + appCtx.Dynamic, + ) + + if len(opts.ControllerName) == 0 { + opts.ControllerName = "helm-project-operator" + } + + valuesOverride, err := common.LoadValuesOverrideFromFile(opts.ValuesOverrideFile) + if err != nil { + return err + } + project.Register(ctx, + systemNamespace, + opts, + valuesOverride, + appCtx.Apply, + // watches + appCtx.ProjectHelmChart(), + appCtx.ProjectHelmChart().Cache(), + appCtx.Core.ConfigMap(), + appCtx.Core.ConfigMap().Cache(), + appCtx.RBAC.Role(), + appCtx.RBAC.Role().Cache(), + appCtx.RBAC.ClusterRoleBinding(), + appCtx.RBAC.ClusterRoleBinding().Cache(), + // watches and generates + appCtx.HelmController.HelmChart(), + appCtx.HelmLocker.HelmRelease(), + appCtx.Core.Namespace(), + appCtx.Core.Namespace().Cache(), + appCtx.RBAC.RoleBinding(), + appCtx.RBAC.RoleBinding().Cache(), + projectGetter, + ) + + if !opts.DisableEmbeddedHelmLocker { + logrus.Infof("Registering embedded Helm Locker...") + release.Register(ctx, + systemNamespace, + opts.ControllerName, + appCtx.HelmLocker.HelmRelease(), + appCtx.HelmLocker.HelmRelease().Cache(), + appCtx.Core.Secret(), + appCtx.Core.Secret().Cache(), + appCtx.K8s, + appCtx.ObjectSetRegister, + appCtx.ObjectSetHandler, + recorder, + ) + } + + if !opts.DisableEmbeddedHelmController { + logrus.Infof("Registering embedded Helm Controller...") + chart.Register(ctx, + systemNamespace, + opts.ControllerName, + appCtx.K8s, + appCtx.Apply, + recorder, + appCtx.HelmController.HelmChart(), + appCtx.HelmController.HelmChart().Cache(), + appCtx.HelmController.HelmChartConfig(), + appCtx.HelmController.HelmChartConfig().Cache(), + appCtx.Batch.Job(), + appCtx.Batch.Job().Cache(), + appCtx.RBAC.ClusterRoleBinding(), + appCtx.Core.ServiceAccount(), + appCtx.Core.ConfigMap()) + } + + leader.RunOrDie(ctx, systemNamespace, fmt.Sprintf("helm-project-operator-%s-lock", opts.ReleaseName), appCtx.K8s, func(ctx context.Context) { + if err := appCtx.start(ctx); err != nil { + logrus.Fatal(err) + } + logrus.Info("All controllers have been started") + }) + + return nil +} + +func controllerFactory(rest *rest.Config) (controller.SharedControllerFactory, error) { + rateLimit := workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 60*time.Second) + clientFactory, err := client.NewSharedClientFactory(rest, nil) + if err != nil { + return nil, err + } + + cacheFactory := cache.NewSharedCachedFactory(clientFactory, nil) + return controller.NewSharedControllerFactory(cacheFactory, &controller.SharedControllerFactoryOptions{ + DefaultRateLimiter: rateLimit, + DefaultWorkers: 50, + }), nil +} + +func newContext(cfg clientcmd.ClientConfig, systemNamespace string, opts common.Options) (*appContext, error) { + client, err := cfg.ClientConfig() + if err != nil { + return nil, err + } + client.RateLimiter = ratelimit.None + + dynamic, err := dynamic.NewForConfig(client) + if err != nil { + return nil, err + } + + k8s, err := kubernetes.NewForConfig(client) + if err != nil { + return nil, err + } + + discovery, err := discovery.NewDiscoveryClientForConfig(client) + if err != nil { + return nil, err + } + + apply := apply.New(discovery, apply.NewClientFactory(client)) + + scf, err := controllerFactory(client) + if err != nil { + return nil, err + } + + // Shared Controllers + + core, err := core.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + SharedControllerFactory: scf, + }) + if err != nil { + return nil, err + } + corev := core.Core().V1() + + networking, err := networking.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + SharedControllerFactory: scf, + }) + if err != nil { + return nil, err + } + networkingv := networking.Networking().V1() + + // Helm Project Controller + + var namespace string // by default, this is unset so we watch everything + if len(opts.ProjectLabel) == 0 { + // we only need to watch the systemNamespace + namespace = systemNamespace + } + + helmproject, err := helmproject.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + SharedControllerFactory: scf, + Namespace: namespace, + }) + if err != nil { + return nil, err + } + helmprojectv := helmproject.Helm().V1alpha1() + + // Helm Locker Controllers - should be scoped to the system namespace only + + objectSet, objectSetRegister, objectSetHandler := objectset.NewLockableRegister("object-set-register", apply, scf, discovery, nil) + + helmlocker, err := helmlocker.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + SharedControllerFactory: scf, + Namespace: systemNamespace, + }) + if err != nil { + return nil, err + } + helmlockerv := helmlocker.Helm().V1alpha1() + + // Helm Controllers - should be scoped to the system namespace only + + helm, err := k3shelm.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + SharedControllerFactory: scf, + Namespace: systemNamespace, + }) + if err != nil { + return nil, err + } + helmv := helm.Helm().V1() + + batch, err := batch.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + SharedControllerFactory: scf, + Namespace: systemNamespace, + }) + if err != nil { + return nil, err + } + batchv := batch.Batch().V1() + + rbac, err := rbac.NewFactoryFromConfigWithOptions(client, &generic.FactoryOptions{ + SharedControllerFactory: scf, + Namespace: systemNamespace, + }) + if err != nil { + return nil, err + } + rbacv := rbac.Rbac().V1() + + return &appContext{ + Interface: helmprojectv, + + Dynamic: dynamic, + K8s: k8s, + Core: corev, + Networking: networkingv, + + HelmLocker: helmlockerv, + ObjectSetRegister: objectSetRegister, + ObjectSetHandler: objectSetHandler, + + HelmController: helmv, + Batch: batchv, + RBAC: rbacv, + + Apply: apply.WithSetOwnerReference(false, false), + EventBroadcaster: record.NewBroadcaster(), + + ClientConfig: cfg, + starters: []start.Starter{ + core, + networking, + batch, + rbac, + helm, + objectSet, + helmlocker, + helmproject, + }, + }, nil +} diff --git a/pkg/helm-project-operator/controllers/hardened/controller.go b/pkg/helm-project-operator/controllers/hardened/controller.go new file mode 100644 index 00000000..2e6a6dbe --- /dev/null +++ b/pkg/helm-project-operator/controllers/hardened/controller.go @@ -0,0 +1,69 @@ +package hardened + +import ( + "context" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + + "github.com/rancher/wrangler/pkg/apply" + corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" + networkingcontroller "github.com/rancher/wrangler/pkg/generated/controllers/networking.k8s.io/v1" + corev1 "k8s.io/api/core/v1" +) + +type handler struct { + apply apply.Apply + + opts common2.HardeningOptions + + namespaces corecontroller.NamespaceController + namespaceCache corecontroller.NamespaceCache + serviceaccounts corecontroller.ServiceAccountController + networkpolicies networkingcontroller.NetworkPolicyController +} + +func Register( + ctx context.Context, + apply apply.Apply, + _ common2.HardeningOptions, + namespaces corecontroller.NamespaceController, + namespaceCache corecontroller.NamespaceCache, + serviceaccounts corecontroller.ServiceAccountController, + networkpolicies networkingcontroller.NetworkPolicyController, +) { + + apply = apply. + WithSetID("hardened-hpo-operated-namespace"). + WithCacheTypes(serviceaccounts, networkpolicies) + + h := &handler{ + apply: apply, + namespaces: namespaces, + namespaceCache: namespaceCache, + serviceaccounts: serviceaccounts, + networkpolicies: networkpolicies, + } + + h.initResolvers(ctx) + + namespaces.OnChange(ctx, "harden-hpo-operated-namespace", h.OnChange) +} + +func (h *handler) OnChange(_ string, namespace *corev1.Namespace) (*corev1.Namespace, error) { + if namespace == nil { + return namespace, nil + } + if namespace.DeletionTimestamp != nil { + // When a namespace gets deleted, all resources deployed to harden that namespace should also get deleted + // Therefore, we do not need to apply anything in this situation to avoid spamming logs with trying to apply + // a resource to a namespace that is being terminated + return namespace, nil + } + if !common2.HasHelmProjectOperatedLabel(namespace.Labels) { + // only harden operated namespaces + return namespace, nil + } + return namespace, h.apply.WithOwner(namespace).ApplyObjects( + h.getDefaultServiceAccount(namespace), + h.getNetworkPolicy(namespace), + ) +} diff --git a/pkg/helm-project-operator/controllers/hardened/resolvers.go b/pkg/helm-project-operator/controllers/hardened/resolvers.go new file mode 100644 index 00000000..e13366b4 --- /dev/null +++ b/pkg/helm-project-operator/controllers/hardened/resolvers.go @@ -0,0 +1,67 @@ +package hardened + +import ( + "context" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + + "github.com/rancher/wrangler/pkg/relatedresource" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Note: each resource created in resources.go should have a resolver handler here +// The only exception is namespaces since those are handled by the main controller OnChange + +// initResolvers initializes resolvers that need to be set to watch child resources of Helm Project Operated Namespaces +func (h *handler) initResolvers(ctx context.Context) { + relatedresource.WatchClusterScoped( + ctx, "watch-hardened-hpo-operated-namespace", h.resolveHardenedProjectRegistrationNamespaceData, h.namespaces, + h.serviceaccounts, h.networkpolicies, + ) +} + +func (h *handler) resolveHardenedProjectRegistrationNamespaceData(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) { + if obj == nil { + return nil, nil + } + ns, err := h.namespaceCache.Get(namespace) + if err != nil { + return nil, err + } + if ns == nil { + // namespace is probably being deleted, which means we don't need to resolve anything + return nil, nil + } + if !common.HasHelmProjectOperatedLabel(ns.Labels) { + // only care about service accounts and network policies in an operated namespace + return nil, nil + } + if serviceAccount, ok := obj.(*corev1.ServiceAccount); ok { + return h.resolveServiceAccount(namespace, name, serviceAccount) + } + if networkPolicy, ok := obj.(*networkingv1.NetworkPolicy); ok { + return h.resolveNetworkPolicy(namespace, name, networkPolicy) + } + return nil, nil +} + +func (h *handler) resolveServiceAccount(namespace, name string, _ *corev1.ServiceAccount) ([]relatedresource.Key, error) { + // check if name matches + if name == defaultServiceAccountName { + return []relatedresource.Key{{ + Name: namespace, + }}, nil + } + return nil, nil +} + +func (h *handler) resolveNetworkPolicy(namespace, name string, _ *networkingv1.NetworkPolicy) ([]relatedresource.Key, error) { + // check if name matches + if name == defaultNetworkPolicyName { + return []relatedresource.Key{{ + Name: namespace, + }}, nil + } + return nil, nil +} diff --git a/pkg/helm-project-operator/controllers/hardened/resources.go b/pkg/helm-project-operator/controllers/hardened/resources.go new file mode 100644 index 00000000..009733ba --- /dev/null +++ b/pkg/helm-project-operator/controllers/hardened/resources.go @@ -0,0 +1,68 @@ +package hardened + +import ( + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Note: each resource created here should have a resolver set in resolvers.go +// The only exception is namespaces since those are handled by the main controller OnChange + +var ( + defaultServiceAccountName = "default" + defaultAutomountServiceAccountToken = false // ensures that all pods need to have service account attached to get permissions + + defaultNetworkPolicyName = "hpo-generated-default" + defaultNetworkPolicySpec = networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, // select all pods + Ingress: []networkingv1.NetworkPolicyIngressRule{}, // networking policy limits all ingress + Egress: []networkingv1.NetworkPolicyEgressRule{}, // network limits all egress + PolicyTypes: []networkingv1.PolicyType{"Ingress", "Egress"}, // applies to both ingress and egress + } +) + +// getDefaultServiceAccount returns the default service account configured for this Helm Project Operated namespace +func (h *handler) getDefaultServiceAccount(namespace *corev1.Namespace) *corev1.ServiceAccount { + serviceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: defaultServiceAccountName, + Namespace: namespace.Name, + Labels: map[string]string{ + common.HelmProjectOperatedLabel: "true", + }, + }, + AutomountServiceAccountToken: &defaultAutomountServiceAccountToken, + } + if h.opts.ServiceAccount != nil { + if h.opts.ServiceAccount.Secrets != nil { + serviceAccount.Secrets = h.opts.ServiceAccount.Secrets + } + if h.opts.ServiceAccount.ImagePullSecrets != nil { + serviceAccount.ImagePullSecrets = h.opts.ServiceAccount.ImagePullSecrets + } + if h.opts.ServiceAccount.AutomountServiceAccountToken != nil { + serviceAccount.AutomountServiceAccountToken = h.opts.ServiceAccount.AutomountServiceAccountToken + } + } + return serviceAccount +} + +// getNetworkPolicy returns the default Helm Project Operator generated NetworkPolicy configured for this Helm Project Operated namespace +func (h *handler) getNetworkPolicy(namespace *corev1.Namespace) *networkingv1.NetworkPolicy { + networkPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: defaultNetworkPolicyName, + Namespace: namespace.Name, + Labels: map[string]string{ + common.HelmProjectOperatedLabel: "true", + }, + }, + Spec: defaultNetworkPolicySpec, + } + if h.opts.NetworkPolicy != nil { + networkPolicy.Spec = networkingv1.NetworkPolicySpec(*h.opts.NetworkPolicy) + } + return networkPolicy +} diff --git a/pkg/helm-project-operator/controllers/namespace/controller.go b/pkg/helm-project-operator/controllers/namespace/controller.go new file mode 100644 index 00000000..fd4e499a --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/controller.go @@ -0,0 +1,375 @@ +package namespace + +import ( + "context" + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/applier" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + helmprojectcontroller "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1" + + "github.com/rancher/wrangler/pkg/apply" + corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/dynamic" +) + +type handler struct { + namespaceApply apply.Apply + apply apply.Apply + + systemNamespace string + valuesYaml string + questionsYaml string + opts common2.Options + + systemNamespaceTracker Tracker + projectRegistrationNamespaceTracker Tracker + + namespaces corecontroller.NamespaceController + namespaceCache corecontroller.NamespaceCache + configmaps corecontroller.ConfigMapController + projectHelmCharts helmprojectcontroller.ProjectHelmChartController + projectHelmChartCache helmprojectcontroller.ProjectHelmChartCache + + projectRegistrationNamespaceApplyinator applier.Applyinator +} + +func Register( + ctx context.Context, + apply apply.Apply, + systemNamespace, valuesYaml, questionsYaml string, + opts common2.Options, + namespaces corecontroller.NamespaceController, + namespaceCache corecontroller.NamespaceCache, + configmaps corecontroller.ConfigMapController, + projectHelmCharts helmprojectcontroller.ProjectHelmChartController, + projectHelmChartCache helmprojectcontroller.ProjectHelmChartCache, + dynamic dynamic.Interface, +) ProjectGetter { + + apply = apply.WithCacheTypes(configmaps) + + h := &handler{ + apply: apply, + systemNamespace: systemNamespace, + valuesYaml: valuesYaml, + questionsYaml: questionsYaml, + opts: opts, + systemNamespaceTracker: NewTracker(), + projectRegistrationNamespaceTracker: NewTracker(), + namespaces: namespaces, + namespaceCache: namespaceCache, + configmaps: configmaps, + projectHelmCharts: projectHelmCharts, + projectHelmChartCache: projectHelmChartCache, + } + + // note: this implements a workqueue that ensures that applies only happen once at a time even if a bunch of namespaces in a project + // are all re-enqueued at the exact same time + h.projectRegistrationNamespaceApplyinator = applier.NewApplyinator("project-registration-namespace-applyinator", h.applyProjectRegistrationNamespace, nil) + h.projectRegistrationNamespaceApplyinator.Run(ctx, 2) + + h.apply = h.addReconcilers(h.apply, dynamic) + + h.initResolvers(ctx) + + h.initIndexers() + + if len(opts.ProjectLabel) == 0 { + namespaces.OnChange(ctx, "on-namespace-change", h.OnSingleNamespaceChange) + + return NewSingleNamespaceProjectGetter(systemNamespace, opts.SystemNamespaces, namespaces) + } + + // the namespaceApply is only needed in a multi-namespace setup + // note: we never delete namespaces that are created since it's possible that the user may want to leave them around + // on remove, we only output a log that says that the user should clean it up and add an annotation that it is orphaned + h.namespaceApply = apply. + WithSetID("project-registration-namespace-applier"). + WithCacheTypes(namespaces). + WithNoDeleteGVK(namespaces.GroupVersionKind()) + + namespaces.OnChange(ctx, "on-namespace-change", h.OnMultiNamespaceChange) + + h.initSystemNamespaces(h.opts.SystemNamespaces, h.systemNamespaceTracker) + + err := h.initProjectRegistrationNamespaces() + if err != nil { + logrus.Fatal(err) + } + + return NewLabelBasedProjectGetter(h.opts.ProjectLabel, h.isProjectRegistrationNamespace, h.isSystemNamespace, h.namespaces) +} + +// Single Namespace Handler + +func (h *handler) OnSingleNamespaceChange(_ string, namespace *corev1.Namespace) (*corev1.Namespace, error) { + if namespace.Name != h.systemNamespace { + // enqueue system namespace to ensure that rolebindings are updated + + logrus.Debugf("Enqueue system namespace to ensure that rolebindings are updated in OnSingleNamespaceChange: %s", h.systemNamespace) + h.namespaces.Enqueue(h.systemNamespace) + return namespace, nil + } + if namespace.DeletionTimestamp != nil { + // When a namespace gets deleted, the ConfigMap deployed in that namespace should also get deleted + // Therefore, we do not need to apply anything in this situation to avoid spamming logs with trying to apply + // a resource to a namespace that is being terminated + logrus.Debugf("OnSingleNamespaceChange %s has deletion timestamp of %v", namespace, namespace.DeletionTimestamp) + return namespace, nil + } + // Trigger applying the data for this projectRegistrationNamespace + var objs []runtime.Object + objs = append(objs, h.getConfigMap("", namespace)) + return namespace, h.configureApplyForNamespace(namespace).ApplyObjects(objs...) +} + +// Multiple Namespaces Handler + +func (h *handler) OnMultiNamespaceChange(_ string, namespace *corev1.Namespace) (*corev1.Namespace, error) { + if namespace == nil { + logrus.Debugf("OnMultiNamespaceChange() called with no namespace.") + return namespace, nil + } + + switch { + // note: the check for a project registration namespace must happen before + // we check for whether it is a system namespace to address the scenario where + // the 'projectLabel: systemProjectLabelValue' is added to the project registration + // namespace, which will cause it to be ignored and left in the System Project unless + // we apply the ProjectRegistrationNamespace logic first. + case h.isProjectRegistrationNamespace(namespace): + err := h.enqueueProjectNamespaces(namespace) + if err != nil { + logrus.Debugf("Error in call to isProjectRegistrationNamespace() while enqueuing project namespace %s: %s", namespace, err) + return namespace, err + } + if namespace.DeletionTimestamp != nil { + logrus.Debugf("%s has deletion timestamp %v in isProjectRegistrationNamespace()", namespace, namespace.DeletionTimestamp) + h.projectRegistrationNamespaceTracker.Delete(namespace) + } + return namespace, nil + case h.isSystemNamespace(namespace): + // nothing to do, we always ignore system namespaces + logrus.Debugf("Ignoring system namespace: %s", namespace) + return namespace, nil + default: + err := h.applyProjectRegistrationNamespaceForNamespace(namespace) + if err != nil { + logrus.Debugf("Default error in isProjectRegistrationNamespace() %s: %s", namespace, err) + return namespace, err + } + return namespace, nil + } +} + +func (h *handler) enqueueProjectNamespaces(projectRegistrationNamespace *corev1.Namespace) error { + if projectRegistrationNamespace == nil { + return nil + } + // ensure that we are working with the projectRegistrationNamespace that we expect, not the one we found + expectedNamespace, exists := h.projectRegistrationNamespaceTracker.Get(projectRegistrationNamespace.Name) + if !exists { + // we no longer expect this namespace to exist, so don't enqueue any namespaces + return nil + } + // projectRegistrationNamespace was modified or removed, so we should re-enqueue any namespaces tied to it + projectID, ok := expectedNamespace.Labels[h.opts.ProjectLabel] + if !ok { + return fmt.Errorf("could not find project that projectRegistrationNamespace %s is tied to", projectRegistrationNamespace.Name) + } + projectNamespaces, err := h.namespaceCache.GetByIndex(NamespacesByProjectExcludingRegistrationID, projectID) + if err != nil { + return err + } + for _, ns := range projectNamespaces { + h.namespaces.Enqueue(ns.Name) + } + logrus.Debugf("ProjectRegistrationNamespace %s was modified or removed in call to enqueueProjectNamespaces(). Reenqueiing any namepsaced tied to it.", projectRegistrationNamespace.Name) + return nil +} + +func (h *handler) applyProjectRegistrationNamespaceForNamespace(namespace *corev1.Namespace) error { + // get the project ID and generate the namespace object to be applied + projectID, inProject := h.getProjectIDFromNamespaceLabels(namespace) + + // update the namespace with the appropriate label on it + err := h.updateNamespaceWithHelmOperatorProjectLabel(namespace, projectID, inProject) + if err != nil { + logrus.Debugf("Error updating namespace %s with %s labels", namespace, projectID) + return nil + } + if !inProject { + return nil + } + + logrus.Infof("Calling projectRegistrationNamespaceApplyinator for project %s", projectID) + // Note: why do we use an Applyinator.Apply here instead of just directly + // running h.applyProjectRegistrationNamespace? + // + // If we ran the logic for applying a Project Registration Namespace here, + // on every time a Project Namespace was re-enqueued, that would result in projects + // with a lot of namespaces all trying to run the exact same apply operation + // at the exact same time; however, the client-go workqueue implementation + // (which lasso controllers use under the hood as well) allow us to add the registration + // namespace to the queue with certain guarantees, namely this one that we need: + // + // * Stingy: a single item will not be processed multiple times concurrently, + // and if an item is added multiple times before it can be processed, it + // will only be processed once. + // + // This ensures that the actual application of a project registration namespace + // will only happen once, regardless of how many enqueues, which prevents us + // from hammering wrangler.Apply operations and forcing wrangler.Apply to engage + // in rate limiting (and output noisy logs) + h.projectRegistrationNamespaceApplyinator.Apply(projectID) + + return nil +} + +func (h *handler) applyProjectRegistrationNamespace(projectID string) error { + // Calculate whether to add the orphaned label + var isOrphaned bool + projectNamespaces, err := h.namespaceCache.GetByIndex(NamespacesByProjectExcludingRegistrationID, projectID) + if err != nil { + return err + } + var numNamespaces int + for _, ns := range projectNamespaces { + if ns.DeletionTimestamp != nil { + // ignore namespaces that are being deleted + continue + } + numNamespaces++ + } + if numNamespaces == 0 { + // add orphaned label and trigger a warning + isOrphaned = true + } + + // get the resources and validate them + projectRegistrationNamespace := h.getProjectRegistrationNamespace(projectID, isOrphaned) + // ensure that the projectRegistrationNamespace created from this projectID is valid + if len(projectRegistrationNamespace.Name) > 63 { + // ensure that we don't try to create a namespace with too big of a name + logrus.Errorf("could not apply namespace with name %s: name is above 63 characters", projectRegistrationNamespace.Name) + return nil + } + + // Trigger the apply and set the projectRegistrationNamespace + err = h.namespaceApply.ApplyObjects(projectRegistrationNamespace) + if err != nil { + return err + } + + // get the projectRegistrationNamespace after applying to get a valid object to pass in as the owner of the next apply + projectRegistrationNamespace, err = h.namespaces.Get(projectRegistrationNamespace.Name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("unable to get project registration namespace from cache after create: %s", err) + } + h.projectRegistrationNamespaceTracker.Set(projectRegistrationNamespace) + + if projectRegistrationNamespace.DeletionTimestamp != nil { + // When a namespace gets deleted, the ConfigMap deployed in that namespace and all ProjectHelmCharts should also get deleted + // Therefore, we do not need to apply anything in this situation to avoid spamming logs with trying to apply + // a resource to a namespace that is being terminated + // + // We expect this to be recalled when the project registration namespace is recreated anyways + return nil + } + + // Trigger applying the data for this projectRegistrationNamespace + var objs []runtime.Object + objs = append(objs, h.getConfigMap(projectID, projectRegistrationNamespace)) + err = h.configureApplyForNamespace(projectRegistrationNamespace).ApplyObjects(objs...) + if err != nil { + return err + } + + // ensure that all ProjectHelmCharts are re-enqueued within this projectRegistrationNamespace + err = h.enqueueProjectHelmChartsForNamespace(projectRegistrationNamespace) + if err != nil { + return fmt.Errorf("unable to re-enqueue ProjectHelmCharts on reconciling change to namespaces in project %s: %s", projectID, err) + } + + return nil +} + +func (h *handler) updateNamespaceWithHelmOperatorProjectLabel(namespace *corev1.Namespace, projectID string, inProject bool) error { + if namespace.DeletionTimestamp != nil { + // no need to update a namespace about to be deleted + return nil + } + if len(h.opts.ProjectReleaseLabelValue) == 0 { + // do nothing, this annotation is irrelevant unless we create release namespaces + return nil + } + if len(projectID) == 0 || !inProject { + // ensure that the HelmProjectOperatorProjectLabel is removed if added + if namespace.Labels == nil { + return nil + } + if _, ok := namespace.Labels[common2.HelmProjectOperatorProjectLabel]; !ok { + return nil + } + namespaceCopy := namespace.DeepCopy() + delete(namespaceCopy.Labels, common2.HelmProjectOperatorProjectLabel) + _, err := h.namespaces.Update(namespaceCopy) + if err != nil { + return err + } + } + + namespaceCopy := namespace.DeepCopy() + if namespaceCopy.Labels == nil { + namespaceCopy.Labels = map[string]string{} + } + currLabel, ok := namespaceCopy.Labels[common2.HelmProjectOperatorProjectLabel] + if !ok || currLabel != projectID { + namespaceCopy.Labels[common2.HelmProjectOperatorProjectLabel] = projectID + } + _, err := h.namespaces.Update(namespaceCopy) + if err != nil { + return err + } + return nil +} + +func (h *handler) isProjectRegistrationNamespace(namespace *corev1.Namespace) bool { + if namespace == nil { + return false + } + return h.projectRegistrationNamespaceTracker.Has(namespace.Name) +} + +func (h *handler) isSystemNamespace(namespace *corev1.Namespace) bool { + if namespace == nil { + return false + } + isTrackedSystemNamespace := h.systemNamespaceTracker.Has(namespace.Name) + if isTrackedSystemNamespace { + return true + } + + var systemProjectLabelValues []string + if len(h.opts.SystemProjectLabelValues) != 0 { + systemProjectLabelValues = append(systemProjectLabelValues, h.opts.SystemProjectLabelValues...) + } + if len(h.opts.ProjectReleaseLabelValue) != 0 { + systemProjectLabelValues = append(systemProjectLabelValues, h.opts.ProjectReleaseLabelValue) + } + projectID, inProject := h.getProjectIDFromNamespaceLabels(namespace) + if !inProject { + return false + } + for _, systemProjectLabelValue := range systemProjectLabelValues { + // check if labels indicate this is a system project + if projectID == systemProjectLabelValue { + return true + } + } + return false +} diff --git a/pkg/helm-project-operator/controllers/namespace/getter.go b/pkg/helm-project-operator/controllers/namespace/getter.go new file mode 100644 index 00000000..2c098c49 --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/getter.go @@ -0,0 +1,160 @@ +package namespace + +import ( + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "sort" + + corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" +) + +// ProjectGetter allows you to get target namespaces based on a project and identify namespaces as special namespaces in a project +type ProjectGetter interface { + // IsProjectRegistrationNamespace returns whether to watch for ProjectHelmCharts in the provided namespace + IsProjectRegistrationNamespace(namespace *corev1.Namespace) bool + + // IsSystemNamespace returns whether the provided namespace is considered a system namespace + IsSystemNamespace(namespace *corev1.Namespace) bool + + // GetTargetProjectNamespaces returns the list of namespaces that should be targeted for a given ProjectHelmChart + // Any namespace returned by this should not be a project registration namespace or a system namespace + GetTargetProjectNamespaces(projectHelmChart *v1alpha1.ProjectHelmChart) ([]string, error) +} + +// Checker is a function that checks a namespace object and returns true or false +type Checker func(namespace *corev1.Namespace) bool + +// NewLabelBasedProjectGetter returns a ProjectGetter that gets target project namespaces that meet the following criteria: +// 1) Must have the same projectLabel value as the namespace where the ProjectHelmChart lives in +// 2) Must not be a project registration namespace +// 3) Must not be a system namespace +func NewLabelBasedProjectGetter( + projectLabel string, + isProjectRegistrationNamespace Checker, + isSystemNamespace Checker, + namespaces corecontroller.NamespaceController, +) ProjectGetter { + return &projectGetter{ + namespaces: namespaces, + + isProjectRegistrationNamespace: isProjectRegistrationNamespace, + isSystemNamespace: isSystemNamespace, + + getProjectNamespaces: func(projectHelmChart *v1alpha1.ProjectHelmChart) (*corev1.NamespaceList, error) { + // source of truth is the projectLabel pair that exists on the namespace that the ProjectHelmChart lives within + namespace, err := namespaces.Get(projectHelmChart.Namespace, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + // The projectHelmChart is not in a namespace that exists anymore, this implies it may have been deleted + // Therefore, there are no project namespaces associated with this ProjectHelmChart + return nil, nil + } + return nil, err + } + projectLabelValue, ok := namespace.Labels[projectLabel] + if !ok { + return nil, fmt.Errorf("could not find value of label %s in namespace %s", projectLabel, namespace.Name) + } + return namespaces.List(metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", projectLabel, projectLabelValue), + }) + }, + } +} + +// NewSingleNamespaceProjectGetter returns a ProjectGetter that gets target project namespaces that meet the following criteria: +// 1) Must match the labels provided on spec.projectNamespaceSelector of the projectHelmChart in question +// 2) Must not be the registration namespace +// 3) Must not be part of the provided systemNamespaces +func NewSingleNamespaceProjectGetter( + registrationNamespace string, + systemNamespaces []string, + namespaces corecontroller.NamespaceController, +) ProjectGetter { + isSystemNamespace := make(map[string]bool) + for _, ns := range systemNamespaces { + isSystemNamespace[ns] = true + } + return &projectGetter{ + namespaces: namespaces, + + isProjectRegistrationNamespace: func(namespace *corev1.Namespace) bool { + // only one registrationNamespace exists + return namespace.Name == registrationNamespace + }, + isSystemNamespace: func(namespace *corev1.Namespace) bool { + // only track explicit systemNamespaces + return isSystemNamespace[namespace.Name] + }, + + getProjectNamespaces: func(projectHelmChart *v1alpha1.ProjectHelmChart) (*corev1.NamespaceList, error) { + // source of truth is the ProjectHelmChart spec.projectNamespaceSelector + selector, err := metav1.LabelSelectorAsSelector(projectHelmChart.Spec.ProjectNamespaceSelector) + if err != nil { + return nil, err + } + // List does not support the ability to ask for specific namespaces + // based on a metav1.LabelSelector, so we get everything and then filter + namespaceList, err := namespaces.List(metav1.ListOptions{}) + if err != nil { + return nil, nil + } + if namespaceList == nil { + return nil, nil + } + var namespaces []corev1.Namespace + for _, ns := range namespaceList.Items { + if !selector.Matches(labels.Set(ns.Labels)) { + continue + } + namespaces = append(namespaces, ns) + } + namespaceList.Items = namespaces + return namespaceList, nil + }, + } +} + +type projectGetter struct { + namespaces corecontroller.NamespaceController + + isProjectRegistrationNamespace Checker + isSystemNamespace Checker + + getProjectNamespaces func(projectHelmChart *v1alpha1.ProjectHelmChart) (*corev1.NamespaceList, error) +} + +// IsProjectRegistrationNamespace returns whether to watch for ProjectHelmCharts in the provided namespace +func (g *projectGetter) IsProjectRegistrationNamespace(namespace *corev1.Namespace) bool { + return g.isProjectRegistrationNamespace(namespace) +} + +// IsSystemNamespace returns whether the provided namespace is considered a system namespace +func (g *projectGetter) IsSystemNamespace(namespace *corev1.Namespace) bool { + return g.isSystemNamespace(namespace) +} + +// GetTargetProjectNamespaces returns the list of namespaces that should be targeted for a given ProjectHelmChart +// Any namespace returned by this should not be a project registration namespace or a system namespace +func (g *projectGetter) GetTargetProjectNamespaces(projectHelmChart *v1alpha1.ProjectHelmChart) ([]string, error) { + projectNamespaceList, err := g.getProjectNamespaces(projectHelmChart) + if err != nil { + return nil, err + } + if projectNamespaceList == nil { + return nil, nil + } + var namespaces []string + for _, ns := range projectNamespaceList.Items { + if g.isProjectRegistrationNamespace(&ns) || g.isSystemNamespace(&ns) { + continue + } + namespaces = append(namespaces, ns.Name) + } + sort.Strings(namespaces) + return namespaces, nil +} diff --git a/pkg/helm-project-operator/controllers/namespace/indexers.go b/pkg/helm-project-operator/controllers/namespace/indexers.go new file mode 100644 index 00000000..bca637e2 --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/indexers.go @@ -0,0 +1,41 @@ +package namespace + +import ( + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + corev1 "k8s.io/api/core/v1" +) + +const ( + // NamespacesByProjectExcludingRegistrationID is an index mapping namespaces to project that they belong into + // The index will omit any namespaces considered to be the Project Registration namespace or a system namespace + NamespacesByProjectExcludingRegistrationID = "helm.cattle.io/namespaces-by-project-id-excluding-registration" +) + +// initIndexers initializes indexers that allow for more efficient computations on related resources without relying on additional +// calls to be made to the Kubernetes API by referencing the cache instead +func (h *handler) initIndexers() { + h.namespaceCache.AddIndexer(NamespacesByProjectExcludingRegistrationID, h.namespaceToProjectIDExcludingRegistration) +} + +func (h *handler) namespaceToProjectIDExcludingRegistration(namespace *corev1.Namespace) ([]string, error) { + if namespace == nil { + return nil, nil + } + if h.isSystemNamespace(namespace) { + return nil, nil + } + if h.isProjectRegistrationNamespace(namespace) { + return nil, nil + } + if namespace.Labels[common.HelmProjectOperatedLabel] == "true" { + // always ignore Helm Project Operated namespaces since those are only + // to be scoped to namespaces that are project registration namespaces + return nil, nil + } + projectID, inProject := h.getProjectIDFromNamespaceLabels(namespace) + if !inProject { + // nothing to do + return nil, nil + } + return []string{projectID}, nil +} diff --git a/pkg/helm-project-operator/controllers/namespace/namespaces.go b/pkg/helm-project-operator/controllers/namespace/namespaces.go new file mode 100644 index 00000000..217f5639 --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/namespaces.go @@ -0,0 +1,48 @@ +package namespace + +import ( + "fmt" + + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// initSystemNamespaces initializes all System Namespaces on the Tracker +func (h *handler) initSystemNamespaces(systemNamespaceList []string, systemNamespaceTracker Tracker) { + for _, namespace := range systemNamespaceList { + systemNamespaceTracker.Set(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}) + } +} + +// initProjectRegistrationNamespaces initializes all Project Registration Namespaces on the Tracker +// It also automatically triggers the creation of the Project Registration Namespaces if necessary +func (h *handler) initProjectRegistrationNamespaces() error { + namespaceList, err := h.namespaces.List(metav1.ListOptions{}) + if err != nil { + return fmt.Errorf("unable to list namespaces to enqueue all Helm charts: %s", err) + } + if namespaceList != nil { + logrus.Infof("Identifying and registering projectRegistrationNamespaces...") + // trigger the OnChange events for all namespaces before returning on a register + // + // this ensures that registration will create projectRegistrationNamespaces and + // have isProjectRegistration and isSystemNamespace up to sync before it provides + // the ProjectGetter interface to other controllers that need it. + // + // Q: Why don't we use Enqueue here? + // + // Enqueue will add it to the workqueue but there's no guarantee the namespace's processing + // will happen before this function exits, which is what we need to guarantee here. + // As a result, we explicitly call OnChange here to force the apply to happen and wait for it to finish + for _, ns := range namespaceList.Items { + _, err := h.OnMultiNamespaceChange(ns.Name, &ns) + if err != nil { + // encountered some error, just fail to start + // Possible TODO: Perhaps we should add a backoff retry here? + return fmt.Errorf("unable to initialize projectRegistrationNamespaces before starting other handlers that utilize ProjectGetter: %s", err) + } + } + } + return nil +} diff --git a/pkg/helm-project-operator/controllers/namespace/reconcilers.go b/pkg/helm-project-operator/controllers/namespace/reconcilers.go new file mode 100644 index 00000000..8d124064 --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/reconcilers.go @@ -0,0 +1,59 @@ +package namespace + +import ( + "context" + + "github.com/rancher/wrangler/pkg/apply" + "github.com/rancher/wrangler/pkg/unstructured" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/dynamic" +) + +// addReconcilers registers reconcilers on the apply object that configure how it reconciles changes to specific resources +func (h *handler) addReconcilers(apply apply.Apply, dynamic dynamic.Interface) apply.Apply { + // force recreate configmaps since configmaps can have errors on updates + // for example, if a configmap has been modified to have immutable set to true, it will encounter an error + // another example is if a user tries to switch a key from data to binaryData or vice versa; in this case, + // the k8s API will throw an error due to trying to move a field across locations + r := forceRecreator{ + NamespaceableResourceInterface: dynamic.Resource(corev1.SchemeGroupVersion.WithResource("configmaps")), + } + apply = apply.WithReconciler(corev1.SchemeGroupVersion.WithKind("ConfigMap"), r.deleteAndReplace) + + logrus.Infof("Adding reconcilers on the apply object %s", apply) + return apply +} + +// forceRecreator is a wrapper on the dynamic.NamespaceableResourceInterface that implements an apply.Reconciler +// that uses the interface to delete and recreate a dynamic object on reconcile +type forceRecreator struct { + dynamic.NamespaceableResourceInterface + + deleteOptions metav1.DeleteOptions + createOptions metav1.CreateOptions +} + +func (r *forceRecreator) deleteAndReplace(oldObj runtime.Object, newObj runtime.Object) (bool, error) { + meta, err := meta.Accessor(oldObj) + if err != nil { + return false, err + } + nsed := r.NamespaceableResourceInterface.Namespace(meta.GetNamespace()) + // convert newObj to unstructured + uNewObj, err := unstructured.ToUnstructured(newObj) + if err != nil { + return false, err + } + // perform delete and recreate + if err := nsed.Delete(context.TODO(), meta.GetName(), r.deleteOptions); err != nil { + return false, err + } + if _, err := nsed.Create(context.TODO(), uNewObj, r.createOptions); err != nil { + return false, err + } + return true, nil +} diff --git a/pkg/helm-project-operator/controllers/namespace/resolvers.go b/pkg/helm-project-operator/controllers/namespace/resolvers.go new file mode 100644 index 00000000..9ab2e9fd --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/resolvers.go @@ -0,0 +1,45 @@ +package namespace + +import ( + "context" + + "github.com/rancher/wrangler/pkg/relatedresource" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Note: each resource created in resources.go should have a resolver handler here +// The only exception is namespaces since those are handled by the main controller OnChange + +// initResolvers initializes resolvers that need to be set to watch child resources of Project Registration Namespaces +func (h *handler) initResolvers(ctx context.Context) { + relatedresource.WatchClusterScoped( + ctx, "watch-project-registration-namespace-data", h.resolveProjectRegistrationNamespaceData, h.namespaces, + h.configmaps, + ) +} + +func (h *handler) resolveProjectRegistrationNamespaceData(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) { + if !h.projectRegistrationNamespaceTracker.Has(namespace) { + // no longer need to watch for changes to resources in this namespace since it is no longer tracked + // if the namespace ever becomes unorphaned, we can track it again + return nil, nil + } + if obj == nil { + return nil, nil + } + if configmap, ok := obj.(*corev1.ConfigMap); ok { + return h.resolveConfigMap(namespace, name, configmap) + } + return nil, nil +} + +func (h *handler) resolveConfigMap(namespace, name string, _ *corev1.ConfigMap) ([]relatedresource.Key, error) { + // check if name matches + if name == h.getConfigMapName() { + return []relatedresource.Key{{ + Name: namespace, + }}, nil + } + return nil, nil +} diff --git a/pkg/helm-project-operator/controllers/namespace/resources.go b/pkg/helm-project-operator/controllers/namespace/resources.go new file mode 100644 index 00000000..a9bcd7be --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/resources.go @@ -0,0 +1,48 @@ +package namespace + +import ( + "fmt" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Note: each resource created here should have a resolver set in resolvers.go +// The only exception is namespaces since those are handled by the main controller OnChange + +// getProjectRegistrationNamespace returns the namespace created on behalf of a new Project that has been identified based on +// unique values observed for all namespaces with the label h.opts.ProjectLabel +func (h *handler) getProjectRegistrationNamespace(projectID string, isOrphaned bool) *corev1.Namespace { + if len(h.opts.ProjectLabel) == 0 { + return nil + } + return &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf(common2.ProjectRegistrationNamespaceFmt, projectID), + Annotations: common2.GetProjectNamespaceAnnotations(projectID, h.opts.ProjectLabel, h.opts.ClusterID), + Labels: common2.GetProjectNamespaceLabels(projectID, h.opts.ProjectLabel, projectID, isOrphaned), + }, + } +} + +// getConfigMap returns the values.yaml and questions.yaml ConfigMap that is expected to be created in all Project Registration Namespaces +func (h *handler) getConfigMap(projectID string, namespace *corev1.Namespace) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: h.getConfigMapName(), + Namespace: namespace.Name, + Labels: common2.GetCommonLabels(projectID), + }, + Data: map[string]string{ + "values.yaml": h.valuesYaml, + "questions.yaml": h.questionsYaml, + }, + } +} + +// getConfigMap name returns the name of the ConfigMap to be deployed in all Project Registration Namespaces +func (h *handler) getConfigMapName() string { + return strings.ReplaceAll(h.opts.HelmAPIVersion, "/", ".") +} diff --git a/pkg/helm-project-operator/controllers/namespace/tracker.go b/pkg/helm-project-operator/controllers/namespace/tracker.go new file mode 100644 index 00000000..4df1558d --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/tracker.go @@ -0,0 +1,72 @@ +package namespace + +import ( + "sync" + + corev1 "k8s.io/api/core/v1" +) + +// Getter gets a namespace that has been stored in a register +type Getter interface { + // Has implies that the namespace has been registered + Has(name string) bool + + // Get retrieves a registered namespace + Get(name string) (*corev1.Namespace, bool) +} + +// Tracker can store namespace references and get them +type Tracker interface { + Getter + + // Set registers a namespace + Set(namespace *corev1.Namespace) + + // Delete unregisters a namespace + Delete(namespace *corev1.Namespace) +} + +// NewTracker returns a new tracker that can track and get namespaces +func NewTracker() Tracker { + return &namespaceTracker{ + namespaceMap: make(map[string]*corev1.Namespace), + } +} + +type namespaceTracker struct { + namespaceMap map[string]*corev1.Namespace + mapLock sync.RWMutex +} + +// Has implies that the namespace has been registered +func (r *namespaceTracker) Has(name string) bool { + r.mapLock.RLock() + defer r.mapLock.RUnlock() + _, exists := r.namespaceMap[name] + return exists +} + +// Get retrieves a registered namespace +func (r *namespaceTracker) Get(name string) (*corev1.Namespace, bool) { + r.mapLock.RLock() + defer r.mapLock.RUnlock() + ns, exists := r.namespaceMap[name] + if !exists { + return nil, false + } + return ns, true +} + +// Set registers a namespace +func (r *namespaceTracker) Set(namespace *corev1.Namespace) { + r.mapLock.Lock() + defer r.mapLock.Unlock() + r.namespaceMap[namespace.Name] = namespace +} + +// Delete unregisters a namespace +func (r *namespaceTracker) Delete(namespace *corev1.Namespace) { + r.mapLock.Lock() + defer r.mapLock.Unlock() + delete(r.namespaceMap, namespace.Name) +} diff --git a/pkg/helm-project-operator/controllers/namespace/utils.go b/pkg/helm-project-operator/controllers/namespace/utils.go new file mode 100644 index 00000000..1f958184 --- /dev/null +++ b/pkg/helm-project-operator/controllers/namespace/utils.go @@ -0,0 +1,45 @@ +package namespace + +import ( + "fmt" + + "github.com/rancher/wrangler/pkg/apply" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" +) + +// configureApplyForNamespace configures the apply to treat the provided namespace as an owner +func (h *handler) configureApplyForNamespace(namespace *corev1.Namespace) apply.Apply { + return h.apply. + WithOwner(namespace). + // Why do we need the release name? + // To ensure that we don't override the set created by another instance of the Project Operator + // running under a different release name operating on the same project registration namespace + WithSetID(fmt.Sprintf("%s-%s-data", namespace.Name, h.opts.ReleaseName)) +} + +// getProjectIDFromNamespaceLabels returns projectIDs based on the label on the project +func (h *handler) getProjectIDFromNamespaceLabels(namespace *corev1.Namespace) (string, bool) { + if len(h.opts.ProjectLabel) == 0 { + // nothing to do, namespaces are not project scoped + return "", false + } + labels := namespace.GetLabels() + if labels == nil { + return "", false + } + projectID, namespaceInProject := labels[h.opts.ProjectLabel] + return projectID, namespaceInProject +} + +// enqueueProjectHelmChartsForNamespace simply enqueues all ProjectHelmCharts in a namespace +func (h *handler) enqueueProjectHelmChartsForNamespace(namespace *corev1.Namespace) error { + projectHelmCharts, err := h.projectHelmChartCache.List(namespace.Name, labels.Everything()) + if err != nil { + return err + } + for _, projectHelmChart := range projectHelmCharts { + h.projectHelmCharts.Enqueue(projectHelmChart.Namespace, projectHelmChart.Name) + } + return nil +} diff --git a/pkg/helm-project-operator/controllers/parse.go b/pkg/helm-project-operator/controllers/parse.go new file mode 100644 index 00000000..ee2347ab --- /dev/null +++ b/pkg/helm-project-operator/controllers/parse.go @@ -0,0 +1,64 @@ +package controllers + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "encoding/base64" + "errors" + "fmt" + "io" + "os" + "strings" +) + +// parseValuesAndQuestions parses the base64TgzChart and emits the values.yaml and questions.yaml contained within it +// If values.yaml or questions.yaml are not specified, it will return an empty string for each +func parseValuesAndQuestions(base64TgzChart string) (string, string, error) { + tgzChartBytes, err := base64.StdEncoding.DecodeString(base64TgzChart) + if err != nil { + return "", "", fmt.Errorf("unable to decode base64TgzChart to tgzChart: %s", err) + } + gzipReader, err := gzip.NewReader(bytes.NewReader(tgzChartBytes)) + if err != nil { + return "", "", fmt.Errorf("unable to create gzipReader to read from base64TgzChart: %s", err) + } + defer gzipReader.Close() + tarReader := tar.NewReader(gzipReader) + var valuesYamlBuffer, questionsYamlBuffer bytes.Buffer + var foundValuesYaml, foundQuestionsYaml bool + for { + h, err := tarReader.Next() + if err == io.EOF { + break + } + if err != nil { + return "", "", err + } + if h.Typeflag != tar.TypeReg { + continue + } + splitName := strings.SplitN(h.Name, string(os.PathSeparator), 2) + nameWithoutRootDir := splitName[0] + if len(splitName) > 1 { + nameWithoutRootDir = splitName[1] + } + if nameWithoutRootDir == "values.yaml" || nameWithoutRootDir == "values.yml" { + if foundValuesYaml { + // multiple values.yaml + return "", "", errors.New("multiple values.yaml or values.yml found in base64TgzChart provided") + } + foundValuesYaml = true + io.Copy(&valuesYamlBuffer, tarReader) + } + if nameWithoutRootDir == "questions.yaml" || nameWithoutRootDir == "questions.yml" { + if foundQuestionsYaml { + // multiple values.yaml + return "", "", errors.New("multiple questions.yaml or questions.yml found in base64TgzChart provided") + } + foundQuestionsYaml = true + io.Copy(&questionsYamlBuffer, tarReader) + } + } + return valuesYamlBuffer.String(), questionsYamlBuffer.String(), nil +} diff --git a/pkg/helm-project-operator/controllers/project/cleanup.go b/pkg/helm-project-operator/controllers/project/cleanup.go new file mode 100644 index 00000000..3f12500a --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/cleanup.go @@ -0,0 +1,53 @@ +package project + +import ( + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// initRemoveCleanupLabels removes cleanup labels from all ProjectHelmCharts targeted by this operator +// This gets applied once on startup +func (h *handler) initRemoveCleanupLabels() error { + namespaceList, err := h.namespaces.List(metav1.ListOptions{}) + if err != nil { + return fmt.Errorf("unable to list namespaces to remove cleanup label from all ProjectHelmCharts") + } + if namespaceList == nil { + return nil + } + logrus.Infof("Removing cleanup label from all registered ProjectHelmCharts...") + // ensure all ProjectHelmCharts in every namespace no longer have the cleanup label on them + for _, ns := range namespaceList.Items { + projectHelmChartList, err := h.projectHelmCharts.List(ns.Name, metav1.ListOptions{}) + if err != nil { + return fmt.Errorf("unable to list ProjectHelmCharts in namespace %s to remove cleanup label", ns.Name) + } + if projectHelmChartList == nil { + continue + } + for _, projectHelmChart := range projectHelmChartList.Items { + shouldManage := h.shouldManage(&projectHelmChart) + if !shouldManage { + // not a valid ProjectHelmChart for this operator + continue + } + if projectHelmChart.Labels == nil { + continue + } + _, ok := projectHelmChart.Labels[common.HelmProjectOperatedCleanupLabel] + if !ok { + continue + } + projectHelmChartCopy := projectHelmChart.DeepCopy() + delete(projectHelmChartCopy.Labels, common.HelmProjectOperatedCleanupLabel) + _, err = h.projectHelmCharts.Update(projectHelmChartCopy) + if err != nil { + return fmt.Errorf("unable to remove cleanup label from ProjectHelmCharts %s/%s", projectHelmChart.Namespace, projectHelmChart.Name) + } + } + } + return nil +} diff --git a/pkg/helm-project-operator/controllers/project/controller.go b/pkg/helm-project-operator/controllers/project/controller.go new file mode 100644 index 00000000..ec93a195 --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/controller.go @@ -0,0 +1,346 @@ +package project + +import ( + "context" + "fmt" + v1alpha2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/namespace" + helmprojectcontroller "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1" + + "github.com/k3s-io/helm-controller/pkg/controllers/chart" + k3shelmcontroller "github.com/k3s-io/helm-controller/pkg/generated/controllers/helm.cattle.io/v1" + helmlockercontroller "github.com/rancher/prometheus-federator/pkg/helm-locker/generated/controllers/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/remove" + "github.com/rancher/wrangler/pkg/apply" + corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" + rbaccontroller "github.com/rancher/wrangler/pkg/generated/controllers/rbac/v1" + "github.com/rancher/wrangler/pkg/generic" + "github.com/sirupsen/logrus" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + DefaultJobImage = chart.DefaultJobImage +) + +type handler struct { + systemNamespace string + opts common2.Options + valuesOverride v1alpha2.GenericMap + apply apply.Apply + projectHelmCharts helmprojectcontroller.ProjectHelmChartController + projectHelmChartCache helmprojectcontroller.ProjectHelmChartCache + configmaps corecontroller.ConfigMapController + configmapCache corecontroller.ConfigMapCache + roles rbaccontroller.RoleController + roleCache rbaccontroller.RoleCache + clusterrolebindings rbaccontroller.ClusterRoleBindingController + clusterrolebindingCache rbaccontroller.ClusterRoleBindingCache + helmCharts k3shelmcontroller.HelmChartController + helmReleases helmlockercontroller.HelmReleaseController + namespaces corecontroller.NamespaceController + namespaceCache corecontroller.NamespaceCache + rolebindings rbaccontroller.RoleBindingController + rolebindingCache rbaccontroller.RoleBindingCache + projectGetter namespace.ProjectGetter +} + +func Register( + ctx context.Context, + systemNamespace string, + opts common2.Options, + valuesOverride v1alpha2.GenericMap, + apply apply.Apply, + projectHelmCharts helmprojectcontroller.ProjectHelmChartController, + projectHelmChartCache helmprojectcontroller.ProjectHelmChartCache, + configmaps corecontroller.ConfigMapController, + configmapCache corecontroller.ConfigMapCache, + roles rbaccontroller.RoleController, + roleCache rbaccontroller.RoleCache, + clusterrolebindings rbaccontroller.ClusterRoleBindingController, + clusterrolebindingCache rbaccontroller.ClusterRoleBindingCache, + helmCharts k3shelmcontroller.HelmChartController, + helmReleases helmlockercontroller.HelmReleaseController, + namespaces corecontroller.NamespaceController, + namespaceCache corecontroller.NamespaceCache, + rolebindings rbaccontroller.RoleBindingController, + rolebindingCache rbaccontroller.RoleBindingCache, + projectGetter namespace.ProjectGetter, +) { + + apply = apply. + // Why do we need the release name? + // To ensure that we don't override the set created by another instance of the Project Operator + // running under a different release name operating on the same project registration namespace + WithSetID(fmt.Sprintf("%s-project-helm-chart-applier", opts.ReleaseName)). + WithCacheTypes( + helmCharts, + helmReleases, + namespaces, + rolebindings). + WithNoDeleteGVK(namespaces.GroupVersionKind()) + + h := &handler{ + systemNamespace: systemNamespace, + opts: opts, + valuesOverride: valuesOverride, + apply: apply, + projectHelmCharts: projectHelmCharts, + projectHelmChartCache: projectHelmChartCache, + configmaps: configmaps, + configmapCache: configmapCache, + roles: roles, + clusterrolebindings: clusterrolebindings, + clusterrolebindingCache: clusterrolebindingCache, + roleCache: roleCache, + helmCharts: helmCharts, + helmReleases: helmReleases, + namespaces: namespaces, + namespaceCache: namespaceCache, + rolebindings: rolebindings, + rolebindingCache: rolebindingCache, + projectGetter: projectGetter, + } + + h.initIndexers() + + h.initResolvers(ctx) + + // Why do we need to add the managedBy string to the generatingHandlerName? + // + // By default, generating handlers use the name of the controller as the set ID for the wrangler.apply operation + // Therefore, if multiple iterations of the helm-controller are using the same set ID, they will try to overwrite each other's + // resources since each controller will detect the other's set as resources that need to be cleaned up to apply the new set + // + // To resolve this, we simply prefix the provided managedBy string to the generatingHandler controller's name only to ensure that the + // set ID specified will only target this particular controller + generatingHandlerName := fmt.Sprintf("%s-project-helm-chart-registration", opts.ControllerName) + helmprojectcontroller.RegisterProjectHelmChartGeneratingHandler(ctx, + projectHelmCharts, + apply, + "", + generatingHandlerName, + h.OnChange, + &generic.GeneratingHandlerOptions{ + AllowClusterScoped: true, + }) + + remove.RegisterScopedOnRemoveHandler(ctx, projectHelmCharts, "on-project-helm-chart-remove", + func(_ string, obj runtime.Object) (bool, error) { + if obj == nil { + return false, nil + } + projectHelmChart, ok := obj.(*v1alpha2.ProjectHelmChart) + if !ok { + return false, nil + } + return h.shouldManage(projectHelmChart), nil + }, + helmprojectcontroller.FromProjectHelmChartHandlerToHandler(h.OnRemove), + ) + + err := h.initRemoveCleanupLabels() + if err != nil { + logrus.Fatal(err) + } +} + +func (h *handler) shouldManage(projectHelmChart *v1alpha2.ProjectHelmChart) bool { + if projectHelmChart == nil { + return false + } + namespace, err := h.namespaceCache.Get(projectHelmChart.Namespace) + if err != nil { + // If the namespace that the projectHelmChart resides in does not exist, it shouldn't be managed + // + // Note: we know that this error would only happen if the namespace is not found since the only valid error returned from this + // call is errors.NewNotFound(c.resource, name) + return false + } + isProjectRegistrationNamespace := h.projectGetter.IsProjectRegistrationNamespace(namespace) + if !isProjectRegistrationNamespace { + // only watching resources in registered namespaces + return false + } + if projectHelmChart.Spec.HelmAPIVersion != h.opts.HelmAPIVersion { + // only watch resources with the HelmAPIVersion this controller was configured with + return false + } + return true +} + +func (h *handler) OnChange(projectHelmChart *v1alpha2.ProjectHelmChart, projectHelmChartStatus v1alpha2.ProjectHelmChartStatus) ([]runtime.Object, v1alpha2.ProjectHelmChartStatus, error) { + var objs []runtime.Object + + // initial checks to see if we should handle this + shouldManage := h.shouldManage(projectHelmChart) + if !shouldManage { + return nil, projectHelmChartStatus, nil + } + if projectHelmChart.DeletionTimestamp != nil { + return nil, projectHelmChartStatus, nil + } + + // handle charts with cleanup label + if common2.HasCleanupLabel(projectHelmChart) { + projectHelmChartStatus = h.getCleanupStatus(projectHelmChart, projectHelmChartStatus) + logrus.Infof("Cleaning up HelmChart and HelmRelease for ProjectHelmChart %s/%s", projectHelmChart.Namespace, projectHelmChart.Name) + return nil, projectHelmChartStatus, nil + } + + // get information about the projectHelmChart + projectID, err := h.getProjectID(projectHelmChart) + if err != nil { + return nil, projectHelmChartStatus, err + } + releaseNamespace, releaseName := h.getReleaseNamespaceAndName(projectHelmChart) + + // check if the releaseName is already tracked by another ProjectHelmChart + projectHelmCharts, err := h.projectHelmChartCache.GetByIndex(ProjectHelmChartByReleaseName, releaseName) + if err != nil { + return nil, projectHelmChartStatus, fmt.Errorf("unable to get ProjectHelmCharts to verify if release is already tracked: %s", err) + } + for _, conflictingProjectHelmChart := range projectHelmCharts { + if conflictingProjectHelmChart == nil { + continue + } + if projectHelmChart.Name == conflictingProjectHelmChart.Name && projectHelmChart.Namespace == conflictingProjectHelmChart.Namespace { + // looking at the same projectHelmChart that we have at hand + continue + } + if len(conflictingProjectHelmChart.Status.Status) == 0 { + // the other ProjectHelmChart hasn't been processed yet, so let it fail out whenever it is processed + continue + } + if conflictingProjectHelmChart.Status.Status == "UnableToCreateHelmRelease" { + // the other ProjectHelmChart is the one that will not be able to progress, so we can continue to update this one + continue + } + // we have found another ProjectHelmChart that already exists and is tracking this release with some non-conflicting status + err = fmt.Errorf( + "ProjectHelmChart %s/%s already tracks release %s/%s", + conflictingProjectHelmChart.Namespace, conflictingProjectHelmChart.Name, + releaseName, releaseNamespace, + ) + projectHelmChartStatus = h.getUnableToCreateHelmReleaseStatus(projectHelmChart, projectHelmChartStatus, err) + return nil, projectHelmChartStatus, nil + } + + // set basic statuses + projectHelmChartStatus.SystemNamespace = h.systemNamespace + projectHelmChartStatus.ReleaseNamespace = releaseNamespace + projectHelmChartStatus.ReleaseName = releaseName + + // gather target project namespaces + targetProjectNamespaces, err := h.projectGetter.GetTargetProjectNamespaces(projectHelmChart) + if err != nil { + return nil, projectHelmChartStatus, fmt.Errorf("unable to find project namespaces to deploy ProjectHelmChart: %s", err) + } + if len(targetProjectNamespaces) == 0 { + projectReleaseNamespace := h.getProjectReleaseNamespace(projectID, true, projectHelmChart) + if projectReleaseNamespace != nil { + objs = append(objs, projectReleaseNamespace) + } + projectHelmChartStatus = h.getNoTargetNamespacesStatus(projectHelmChart, projectHelmChartStatus) + return objs, projectHelmChartStatus, nil + } + + if releaseNamespace != h.systemNamespace && releaseNamespace != projectHelmChart.Namespace { + // need to add release namespace to list of objects to be created + projectReleaseNamespace := h.getProjectReleaseNamespace(projectID, false, projectHelmChart) + objs = append(objs, projectReleaseNamespace) + // need to add auto-generated release namespace to target namespaces + targetProjectNamespaces = append(targetProjectNamespaces, releaseNamespace) + } + projectHelmChartStatus.TargetNamespaces = targetProjectNamespaces + + // get values.yaml from ProjectHelmChart spec and default overrides + values := h.getValues(projectHelmChart, projectID, targetProjectNamespaces) + valuesContentBytes, err := values.ToYAML() + if err != nil { + err = fmt.Errorf("unable to marshall spec.values: %s", err) + projectHelmChartStatus = h.getValuesParseErrorStatus(projectHelmChart, projectHelmChartStatus, err) + return nil, projectHelmChartStatus, nil + } + + ns, err := h.namespaceCache.Get(releaseNamespace) + if ns == nil || apierrors.IsNotFound(err) { + // The release namespace does not exist yet, create it and leave the status as UnableToCreateHelmRelease + // + // Note: since we have a resolver that watches for the project release namespace, this handler will get re-enqueued + // + // Note: the reason why we need to do this check is to ensure that deleting a project release namespace will delete + // and recreate the HelmChart and HelmRelease resources, which will ensure that the HelmChart gets re-installed onto + // the newly created namespace. Without this, a deleted release namespace will always have ProjectHelmCharts stuck in + // WaitingForDashboardValues since the underlying helm release will never be recreated + err = fmt.Errorf("cannot find release namespace %s to deploy release", releaseNamespace) + projectHelmChartStatus = h.getUnableToCreateHelmReleaseStatus(projectHelmChart, projectHelmChartStatus, err) + return objs, projectHelmChartStatus, nil + } else if err != nil { + return nil, projectHelmChartStatus, err + } + + // get rolebindings that need to be created in release namespace + k8sRolesToRoleRefs, err := h.getSubjectRoleToRoleRefsFromRoles(projectHelmChart) + if err != nil { + return nil, projectHelmChartStatus, fmt.Errorf("unable to get release roles from project release namespace %s for %s/%s: %s", releaseNamespace, projectHelmChart.Namespace, projectHelmChart.Name, err) + } + k8sRolesToSubjects, err := h.getSubjectRoleToSubjectsFromBindings(projectHelmChart) + if err != nil { + return nil, projectHelmChartStatus, fmt.Errorf("unable to get rolebindings to default project operator roles from project registration namespace %s for %s/%s: %s", projectHelmChart.Namespace, projectHelmChart.Namespace, projectHelmChart.Name, err) + } + objs = append(objs, + h.getRoleBindings(projectID, k8sRolesToRoleRefs, k8sRolesToSubjects, projectHelmChart)..., + ) + + // append the helm chart and helm release + objs = append(objs, + h.getHelmChart(projectID, string(valuesContentBytes), projectHelmChart), + h.getHelmRelease(projectID, projectHelmChart), + ) + + // get dashboard values if available + dashboardValues, err := h.getDashboardValuesFromConfigmaps(projectHelmChart) + if err != nil { + return nil, projectHelmChartStatus, fmt.Errorf("unable to get dashboard values from status ConfigMaps: %s", err) + } + if len(dashboardValues) == 0 { + projectHelmChartStatus = h.getWaitingForDashboardValuesStatus(projectHelmChart, projectHelmChartStatus) + } else { + projectHelmChartStatus.DashboardValues = dashboardValues + projectHelmChartStatus = h.getDeployedStatus(projectHelmChart, projectHelmChartStatus) + } + return objs, projectHelmChartStatus, nil +} + +func (h *handler) OnRemove(_ string, projectHelmChart *v1alpha2.ProjectHelmChart) (*v1alpha2.ProjectHelmChart, error) { + if projectHelmChart == nil { + return nil, nil + } + + // get information about the projectHelmChart + projectID, err := h.getProjectID(projectHelmChart) + if err != nil { + return projectHelmChart, err + } + + // Get orphaned release namespace and apply it; if another ProjectHelmChart exists in this namespace, it will automatically remove + // the orphaned label on enqueuing the namespace since that will enqueue all ProjectHelmCharts associated with it + projectReleaseNamespace := h.getProjectReleaseNamespace(projectID, true, projectHelmChart) + if projectReleaseNamespace == nil { + // nothing to be done since this operator does not create project release namespaces + return projectHelmChart, nil + } + + // Why aren't we modifying the set ID or owner here? + // Since this applier runs without deleting objects whose GVKs indicate that they are namespaces, + // we don't have to worry about another controller using this same set ID (e.g. another Project Operator) + // that will delete this projectReleaseNamespace on seeing it + err = h.apply.ApplyObjects(projectReleaseNamespace) + if err != nil { + return projectHelmChart, fmt.Errorf("unable to add orphaned annotation to project release namespace %s", projectReleaseNamespace.Name) + } + return projectHelmChart, nil +} diff --git a/pkg/helm-project-operator/controllers/project/indexers.go b/pkg/helm-project-operator/controllers/project/indexers.go new file mode 100644 index 00000000..742af68c --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/indexers.go @@ -0,0 +1,141 @@ +package project + +import ( + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" +) + +// All namespaces +const ( + // ProjectHelmChartByReleaseName identifies a ProjectHelmChart by the underlying Helm release it is tied to + ProjectHelmChartByReleaseName = "helm.cattle.io/project-helm-chart-by-release-name" +) + +// Registration namespaces only +const ( + // RoleBindingInRegistrationNamespaceByRoleRef identifies the set of RoleBindings in a registration namespace + // that are tied to specific RoleRefs that need to be watched by the operator + RoleBindingInRegistrationNamespaceByRoleRef = "helm.cattle.io/role-binding-in-registration-ns-by-role-ref" + + // ClusterRoleBindingByRoleRef identifies the set of ClusterRoleBindings that are tied to RoleRefs that need + // to be watched by the operator + ClusterRoleBindingByRoleRef = "helm.cattle.io/cluster-role-binding-by-role-ref" + + // BindingReferencesDefaultOperatorRole is the value of the both of the above indices when a ClusterRoleBinding or RoleBinding + // is tied to a RoleRef that matches a default ClusterRole that is watched by the operator to create admin, edit, or view RoleBindings + // in the Project Release Namespace + BindingReferencesDefaultOperatorRole = "bound-to-default-role" +) + +// NamespacedBindingReferencesDefaultOperatorRole is the index used to mark a RoleBinding as one that targets +// one of the default operator roles (supplied in RuntimeOptions under AdminClusterRole, EditClusterRole, and ViewClusterRole) +func NamespacedBindingReferencesDefaultOperatorRole(namespace string) string { + return fmt.Sprintf("%s/%s", namespace, BindingReferencesDefaultOperatorRole) +} + +// Release namespaces only +const ( + // RoleInReleaseNamespaceByReleaseNamespaceName identifies a Role in a release namespace that needs to have RBAC synced + // on changes to RoleBindings in the Project Registration Namespace or ClusterRoleBindings. + // The value of this will be the namespace and name of the Helm release that it is for. + RoleInReleaseNamespaceByReleaseNamespaceName = "helm.cattle.io/role-in-release-ns-by-release-namespace-name" + + // ConfigMapInReleaseNamespaceByReleaseNamespaceName identifies a ConfigMap in a release namespace that is tied to the + // ProjectHelmChart's status in the release namespace. + // The value of this will be the namespace and name of the Helm release that it is for. + ConfigMapInReleaseNamespaceByReleaseNamespaceName = "helm.cattle.io/configmap-in-release-ns-by-release-namespace-name" +) + +// initIndexers initializes indexers that allow for more efficient computations on related resources without relying on additional +// calls to be made to the Kubernetes API by referencing the cache instead +func (h *handler) initIndexers() { + h.projectHelmChartCache.AddIndexer(ProjectHelmChartByReleaseName, h.projectHelmChartToReleaseName) + + h.rolebindingCache.AddIndexer(RoleBindingInRegistrationNamespaceByRoleRef, h.roleBindingInRegistrationNamespaceToRoleRef) + + h.clusterrolebindingCache.AddIndexer(ClusterRoleBindingByRoleRef, h.clusterRoleBindingToRoleRef) + + h.roleCache.AddIndexer(RoleInReleaseNamespaceByReleaseNamespaceName, h.roleInReleaseNamespaceToReleaseNamespaceName) + + h.configmapCache.AddIndexer(ConfigMapInReleaseNamespaceByReleaseNamespaceName, h.configMapInReleaseNamespaceToReleaseNamespaceName) +} + +func (h *handler) projectHelmChartToReleaseName(projectHelmChart *v1alpha1.ProjectHelmChart) ([]string, error) { + shouldManage := h.shouldManage(projectHelmChart) + if !shouldManage { + return nil, nil + } + _, releaseName := h.getReleaseNamespaceAndName(projectHelmChart) + return []string{releaseName}, nil +} + +func (h *handler) roleBindingInRegistrationNamespaceToRoleRef(rb *rbacv1.RoleBinding) ([]string, error) { + if rb == nil { + return nil, nil + } + namespace, err := h.namespaceCache.Get(rb.Namespace) + if err != nil { + // If we can't get the namespace the rolebinding resides in role binding resides in does not exist, we don't need to index + // it since it's probably gotten deleted anyways. + // + // Note: we know that this error would only happen if the namespace is not found since the only valid error returned from this + // call is errors.NewNotFound(c.resource, name) + return nil, nil + } + isProjectRegistrationNamespace := h.projectGetter.IsProjectRegistrationNamespace(namespace) + if !isProjectRegistrationNamespace { + return nil, nil + } + _, isDefaultRoleRef := common2.IsDefaultClusterRoleRef(h.opts, rb.RoleRef.Name) + if !isDefaultRoleRef { + // we only care about rolebindings in the registration namespace that are tied to the default roles + // created by this operator + return nil, nil + } + // keep track of this rolebinding in the index so we can grab it later + return []string{NamespacedBindingReferencesDefaultOperatorRole(rb.Namespace)}, nil +} + +func (h *handler) clusterRoleBindingToRoleRef(crb *rbacv1.ClusterRoleBinding) ([]string, error) { + if crb == nil { + return nil, nil + } + _, isDefaultRoleRef := common2.IsDefaultClusterRoleRef(h.opts, crb.RoleRef.Name) + if !isDefaultRoleRef { + // we only care about rolebindings in the registration namespace that are tied to the default roles + // created by this operator + return nil, nil + } + // keep track of this rolebinding in the index so we can grab it later + return []string{BindingReferencesDefaultOperatorRole}, nil +} + +func (h *handler) roleInReleaseNamespaceToReleaseNamespaceName(role *rbacv1.Role) ([]string, error) { + if role == nil { + return nil, nil + } + return h.getReleaseIndexFromNamespaceAndLabels(role.Namespace, role.Labels, common2.HelmProjectOperatorProjectHelmChartRoleLabel) +} + +func (h *handler) configMapInReleaseNamespaceToReleaseNamespaceName(configmap *corev1.ConfigMap) ([]string, error) { + if configmap == nil { + return nil, nil + } + return h.getReleaseIndexFromNamespaceAndLabels(configmap.Namespace, configmap.Labels, common2.HelmProjectOperatorDashboardValuesConfigMapLabel) +} + +func (h *handler) getReleaseIndexFromNamespaceAndLabels(namespace string, labels map[string]string, releaseLabel string) ([]string, error) { + if labels == nil { + return nil, nil + } + releaseName, ok := labels[releaseLabel] + if !ok { + return nil, nil + } + + return []string{fmt.Sprintf("%s/%s", namespace, releaseName)}, nil +} diff --git a/pkg/helm-project-operator/controllers/project/merge.go b/pkg/helm-project-operator/controllers/project/merge.go new file mode 100644 index 00000000..64d4cc83 --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/merge.go @@ -0,0 +1,65 @@ +package project + +// Copied from https://github.com/rancher/wrangler/blob/004e382969b42fb2f538ffd6699569d30e490428/pkg/data/merge.go#L3-L24 +// Why did we copy the code? The logic for checking bothMaps needs to account for more possible types than map[string]interface{}, +// namely v1alpha1.GenericMap and map[interface{}]interface{} + +func MergeMaps(base, overlay map[string]interface{}) map[string]interface{} { + result := map[string]interface{}{} + for k, v := range base { + result[k] = v + } + for k, v := range overlay { + if baseMap, overlayMap, bothMaps := bothMaps(result[k], v); bothMaps { + v = MergeMaps(baseMap, overlayMap) + } + result[k] = v + } + return result +} + +func bothMaps(left, right interface{}) (map[string]interface{}, map[string]interface{}, bool) { + leftMap, isMap := getMap(left) + if !isMap { + return nil, nil, false + } + rightMap, isMap := getMap(right) + if !isMap { + return nil, nil, false + } + return leftMap, rightMap, true +} + +func getMap(entry interface{}) (map[string]interface{}, bool) { + // check if map[string]interface{} + entryMapStringInterface, isMapStringInterface := entry.(map[string]interface{}) + if isMapStringInterface { + return entryMapStringInterface, true + } + + // check if v1alpha1.GenericMap + entryGenericMap, isGenericMap := entry.(map[string]interface{}) + if isGenericMap { + return entryGenericMap, true + } + + // check if map[interface{}]interface{} + entryMapInterfaceInterface, isMapInterfaceInterface := entry.(map[interface{}]interface{}) + if isMapInterfaceInterface { + return convertMapInterfaceInterfaceToMapStringInterface(entryMapInterfaceInterface) + } + + return nil, false +} + +func convertMapInterfaceInterfaceToMapStringInterface(entry map[interface{}]interface{}) (map[string]interface{}, bool) { + out := make(map[string]interface{}, len(entry)) + for k, v := range entry { + key, isString := k.(string) + if !isString { + return nil, false + } + out[key] = v + } + return out, true +} diff --git a/pkg/helm-project-operator/controllers/project/registrationdata.go b/pkg/helm-project-operator/controllers/project/registrationdata.go new file mode 100644 index 00000000..ba79b45e --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/registrationdata.go @@ -0,0 +1,80 @@ +package project + +import ( + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + + "github.com/sirupsen/logrus" + rbacv1 "k8s.io/api/rbac/v1" +) + +// Note: each resource created here should have a resolver set in resolvers.go + +// getSubjectRoleToSubjectsFromBindings gets all RoleBindings in the Project Registration Namespace that need to be synced to assign the corresponding +// permission in the Project Release Namespace. See pkg/controllers/project/resources.go for more information on how this is used +func (h *handler) getSubjectRoleToSubjectsFromBindings(projectHelmChart *v1alpha1.ProjectHelmChart) (map[string][]rbacv1.Subject, error) { + defaultClusterRoles := common.GetDefaultClusterRoles(h.opts) + subjectRoleToSubjects := make(map[string][]rbacv1.Subject) + subjectRoleToSubjectMap := make(map[string]map[string]rbacv1.Subject) + if len(defaultClusterRoles) == 0 { + // no roles to get get subjects for + return subjectRoleToSubjects, nil + } + for subjectRole := range defaultClusterRoles { + subjectRoleToSubjectMap[subjectRole] = make(map[string]rbacv1.Subject) + } + roleBindings, err := h.rolebindingCache.GetByIndex( + RoleBindingInRegistrationNamespaceByRoleRef, + NamespacedBindingReferencesDefaultOperatorRole(projectHelmChart.Namespace), + ) + if err != nil { + return nil, err + } + for _, rb := range roleBindings { + if rb == nil { + continue + } + subjectRole, isDefaultRoleRef := common.IsDefaultClusterRoleRef(h.opts, rb.RoleRef.Name) + if !isDefaultRoleRef { + logrus.Debugf("Role %s is not a default role for %s", subjectRole, projectHelmChart.Namespace) + continue + } + filteredSubjects := common.FilterToUsersAndGroups(rb.Subjects) + currSubjects := subjectRoleToSubjectMap[subjectRole] + for _, filteredSubject := range filteredSubjects { + // collect into a map to avoid putting duplicates of the same subject + // we use an index of kind and name since a Group can have the same name as a User, but should be considered separate + currSubjects[fmt.Sprintf("%s-%s", filteredSubject.Kind, filteredSubject.Name)] = filteredSubject + } + } + clusterRoleBindings, err := h.clusterrolebindingCache.GetByIndex(ClusterRoleBindingByRoleRef, BindingReferencesDefaultOperatorRole) + if err != nil { + return nil, err + } + for _, crb := range clusterRoleBindings { + if crb == nil { + continue + } + subjectRole, isDefaultRoleRef := common.IsDefaultClusterRoleRef(h.opts, crb.RoleRef.Name) + if !isDefaultRoleRef { + continue + } + filteredSubjects := common.FilterToUsersAndGroups(crb.Subjects) + currSubjects := subjectRoleToSubjectMap[subjectRole] + for _, filteredSubject := range filteredSubjects { + // collect into a map to avoid putting duplicates of the same subject + // we use an index of kind and name since a Group can have the same name as a User, but should be considered separate + currSubjects[fmt.Sprintf("%s-%s", filteredSubject.Kind, filteredSubject.Name)] = filteredSubject + } + } + // convert back into list so that no duplicates are created + for subjectRole := range defaultClusterRoles { + subjects := []rbacv1.Subject{} + for _, subject := range subjectRoleToSubjectMap[subjectRole] { + subjects = append(subjects, subject) + } + subjectRoleToSubjects[subjectRole] = subjects + } + return subjectRoleToSubjects, nil +} diff --git a/pkg/helm-project-operator/controllers/project/releasedata.go b/pkg/helm-project-operator/controllers/project/releasedata.go new file mode 100644 index 00000000..c80a7801 --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/releasedata.go @@ -0,0 +1,114 @@ +package project + +import ( + "encoding/json" + "fmt" + v1alpha2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + "strings" + + "github.com/rancher/wrangler/pkg/data" + "github.com/sirupsen/logrus" + rbacv1 "k8s.io/api/rbac/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" +) + +// Note: each resource created here should have a resolver set in resolvers.go + +// getDashboardValuesFromConfigMaps returns the generic map that represents a merge of all the contents of all ConfigMaps in the +// Project Release Namespace with the label helm.cattle.io/dashboard-values-configmap: {{ .Release.Name }}. +// +// Generally, these ConfigMaps should be part of the deployed Helm chart and should not have conflicts with each other +// It's also a common pattern to only have a single ConfigMap that this refers to. +func (h *handler) getDashboardValuesFromConfigmaps(projectHelmChart *v1alpha2.ProjectHelmChart) (v1alpha2.GenericMap, error) { + releaseNamespace, releaseName := h.getReleaseNamespaceAndName(projectHelmChart) + exists, err := h.verifyReleaseNamespaceExists(releaseNamespace) + if err != nil { + return nil, err + } + if !exists { + return nil, nil + } + configMaps, err := h.configmapCache.GetByIndex(ConfigMapInReleaseNamespaceByReleaseNamespaceName, fmt.Sprintf("%s/%s", releaseNamespace, releaseName)) + if err != nil { + return nil, err + } + var values v1alpha2.GenericMap + for _, configMap := range configMaps { + if configMap == nil { + continue + } + for jsonKey, jsonContent := range configMap.Data { + if !strings.HasSuffix(jsonKey, ".json") { + logrus.Errorf("dashboard values configmap %s/%s has non-JSON key %s, expected only keys ending with .json. skipping...", configMap.Namespace, configMap.Name, jsonKey) + continue + } + var jsonMap map[string]interface{} + err := json.Unmarshal([]byte(jsonContent), &jsonMap) + if err != nil { + logrus.Errorf("could not marshall content in dashboard values configmap %s/%s in key %s (err='%s'). skipping...", configMap.Namespace, configMap.Name, jsonKey, err) + continue + } + values = data.MergeMapsConcatSlice(values, jsonMap) + } + } + return values, nil +} + +// getSubjectRoleToRoleRefsFromRoles gets all Roles in the Project Release Namespace that need RoleBindings to be created automatically +// based on permissions set in the Project Registration namespace. See pkg/controllers/project/resources.go for more information on how this is used +func (h *handler) getSubjectRoleToRoleRefsFromRoles(projectHelmChart *v1alpha2.ProjectHelmChart) (map[string][]rbacv1.RoleRef, error) { + subjectRoleToRoleRefs := make(map[string][]rbacv1.RoleRef) + for subjectRole := range common2.GetDefaultClusterRoles(h.opts) { + subjectRoleToRoleRefs[subjectRole] = []rbacv1.RoleRef{} + } + if len(subjectRoleToRoleRefs) == 0 { + // no roles were defined to be auto-aggregated + return subjectRoleToRoleRefs, nil + } + releaseNamespace, releaseName := h.getReleaseNamespaceAndName(projectHelmChart) + exists, err := h.verifyReleaseNamespaceExists(releaseNamespace) + if err != nil { + return nil, err + } + if !exists { + return nil, nil + } + roles, err := h.roleCache.GetByIndex(RoleInReleaseNamespaceByReleaseNamespaceName, fmt.Sprintf("%s/%s", releaseNamespace, releaseName)) + if err != nil { + return nil, err + } + for _, role := range roles { + if role == nil { + continue + } + subjectRole, ok := role.Labels[common2.HelmProjectOperatorProjectHelmChartRoleAggregateFromLabel] + if !ok { + // cannot assign roles if this label is not provided + continue + } + roleRefs, ok := subjectRoleToRoleRefs[subjectRole] + if !ok { + // label value is invalid since it does not point to default subject role name + continue + } + subjectRoleToRoleRefs[subjectRole] = append(roleRefs, rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "Role", + Name: role.Name, + }) + } + return subjectRoleToRoleRefs, nil +} + +func (h *handler) verifyReleaseNamespaceExists(releaseNamespace string) (bool, error) { + _, err := h.namespaceCache.Get(releaseNamespace) + if err != nil { + if apierrors.IsNotFound(err) { + // release namespace has not been created yet + return false, nil + } + return false, err + } + return true, nil +} diff --git a/pkg/helm-project-operator/controllers/project/resolvers.go b/pkg/helm-project-operator/controllers/project/resolvers.go new file mode 100644 index 00000000..9e8c36bb --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/resolvers.go @@ -0,0 +1,244 @@ +package project + +import ( + "context" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + + helmcontrollerv1 "github.com/k3s-io/helm-controller/pkg/apis/helm.cattle.io/v1" + helmlockerv1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/wrangler/pkg/apply" + "github.com/rancher/wrangler/pkg/relatedresource" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" +) + +// Note: each resource created in resources.go, registrationdata.go, or releasedata.go should have a resolver handler here +// The only exception is ProjectHelmCharts since those are handled by the main generating controller + +// initResolvers initializes resolvers that need to be set to watch child resources of ProjectHelmCharts +func (h *handler) initResolvers(ctx context.Context) { + if len(h.opts.ProjectLabel) != 0 && len(h.opts.ProjectReleaseLabelValue) == 0 { + // Only trigger watching project release namespace if it is created by the operator + relatedresource.Watch( + ctx, "watch-project-release-namespace", h.resolveProjectReleaseNamespace, h.projectHelmCharts, + h.namespaces, + ) + } + + relatedresource.Watch( + ctx, "watch-system-namespace-chart-data", h.resolveSystemNamespaceData, h.projectHelmCharts, + h.helmCharts, h.helmReleases, + ) + + relatedresource.Watch( + ctx, "watch-project-registration-chart-data", h.resolveProjectRegistrationNamespaceData, h.projectHelmCharts, + h.rolebindings, h.clusterrolebindings, + ) + + relatedresource.Watch( + ctx, "watch-project-release-chart-data", h.resolveProjectReleaseNamespaceData, h.projectHelmCharts, + h.rolebindings, h.configmaps, h.roles, + ) +} + +// Project Release Namespace + +func (h *handler) resolveProjectReleaseNamespace(_, _ string, obj runtime.Object) ([]relatedresource.Key, error) { + if obj == nil { + return nil, nil + } + ns, ok := obj.(*corev1.Namespace) + if !ok { + return nil, nil + } + // since the release namespace will be created and owned by the ProjectHelmChart, + // we can simply leverage is annotations to identify what we should resolve to. + // If the release namespace is orphaned, the owner annotation should be removed automatically + return h.resolveProjectHelmChartOwned(ns.Annotations) +} + +// System Namespace Data + +func (h *handler) resolveSystemNamespaceData(namespace, _ string, obj runtime.Object) ([]relatedresource.Key, error) { + if namespace != h.systemNamespace { + return nil, nil + } + if obj == nil { + return nil, nil + } + // since the HelmChart and HelmRelease will be created and owned by the ProjectHelmChart, + // we can simply leverage is annotations to identify what we should resolve to. + if helmChart, ok := obj.(*helmcontrollerv1.HelmChart); ok { + return h.resolveProjectHelmChartOwned(helmChart.Annotations) + } + if helmRelease, ok := obj.(*helmlockerv1alpha1.HelmRelease); ok { + return h.resolveProjectHelmChartOwned(helmRelease.Annotations) + } + return nil, nil +} + +// Project Registration Namespace Data + +func (h *handler) resolveProjectRegistrationNamespaceData(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) { + //h.projectHelmCharts, h.rolebindings, h.clusterrolebindings + + if obj == nil { + return nil, nil + } + if rb, ok := obj.(*rbacv1.RoleBinding); ok { + logrus.Debugf("Resolving project registration namespace rolebindings for %s", namespace) + return h.resolveProjectRegistrationNamespaceRoleBinding(namespace, name, rb) + } + if crb, ok := obj.(*rbacv1.ClusterRoleBinding); ok { + return h.resolveClusterRoleBinding(namespace, name, crb) + } + return nil, nil +} + +func (h *handler) resolveProjectRegistrationNamespaceRoleBinding(namespace, _ string, rb *rbacv1.RoleBinding) ([]relatedresource.Key, error) { + namespaceObj, err := h.namespaceCache.Get(namespace) + if err != nil { + logrus.Debugf("Namespace not found %s: ", namespace) + return nil, err + } + isProjectRegistrationNamespace := h.projectGetter.IsProjectRegistrationNamespace(namespaceObj) + if !isProjectRegistrationNamespace { + logrus.Debugf("%s is not a project registration namespace: ", namespace) + return nil, nil + } + + // we want to re-enqueue the ProjectHelmChart if the rolebinding's ref points to one of the operator default roles + _, isDefaultRoleRef := common2.IsDefaultClusterRoleRef(h.opts, rb.RoleRef.Name) + if !isDefaultRoleRef { + return nil, nil + } + // re-enqueue all HelmCharts in this project registration namespace + projectHelmCharts, err := h.projectHelmChartCache.List(namespace, labels.Everything()) + if err != nil { + logrus.Debugf("Error in resolveProjectRegistrationNamespaceRoleBinding while re-enqueuing HelmCharts in %s", namespace) + return nil, err + } + var keys []relatedresource.Key + for _, projectHelmChart := range projectHelmCharts { + if projectHelmChart == nil { + continue + } + keys = append(keys, relatedresource.Key{ + Namespace: namespace, + Name: projectHelmChart.Name, + }) + } + return keys, nil +} + +func (h *handler) resolveClusterRoleBinding(_, _ string, crb *rbacv1.ClusterRoleBinding) ([]relatedresource.Key, error) { + // we want to re-enqueue the ProjectHelmChart if the rolebinding's ref points to one of the operator default roles + _, isDefaultRoleRef := common2.IsDefaultClusterRoleRef(h.opts, crb.RoleRef.Name) + if !isDefaultRoleRef { + return nil, nil + } + // re-enqueue all HelmCharts in all Project Registration namespaces + namespaces, err := h.namespaceCache.List(labels.Everything()) + if err != nil { + return nil, err + } + var keys []relatedresource.Key + for _, namespace := range namespaces { + if namespace == nil { + continue + } + isProjectRegistrationNamespace := h.projectGetter.IsProjectRegistrationNamespace(namespace) + if !isProjectRegistrationNamespace { + continue + } + projectHelmCharts, err := h.projectHelmChartCache.List(namespace.Name, labels.Everything()) + if err != nil { + logrus.Debugf("Error in resolveClusterRoleBinding while re-enqueuing HelmCharts in %s", namespace) + return nil, err + } + for _, projectHelmChart := range projectHelmCharts { + if projectHelmChart == nil { + continue + } + keys = append(keys, relatedresource.Key{ + Namespace: projectHelmChart.Namespace, + Name: projectHelmChart.Name, + }) + } + } + return keys, nil +} + +// Project Release Namespace Data + +func (h *handler) resolveProjectReleaseNamespaceData(_, _ string, obj runtime.Object) ([]relatedresource.Key, error) { + if obj == nil { + return nil, nil + } + if rb, ok := obj.(*rbacv1.RoleBinding); ok { + // since the rolebinding will be created and owned by the ProjectHelmChart, + // we can simply leverage is annotations to identify what we should resolve to. + return h.resolveProjectHelmChartOwned(rb.Annotations) + } + if configmap, ok := obj.(*corev1.ConfigMap); ok { + return h.resolveByProjectReleaseLabelValue(configmap.Labels, common2.HelmProjectOperatorDashboardValuesConfigMapLabel) + } + if role, ok := obj.(*rbacv1.Role); ok { + return h.resolveByProjectReleaseLabelValue(role.Labels, common2.HelmProjectOperatorProjectHelmChartRoleLabel) + } + return nil, nil +} + +// Common + +func (h *handler) resolveProjectHelmChartOwned(annotations map[string]string) ([]relatedresource.Key, error) { + // Q: Why aren't we using relatedresource.OwnerResolver? + // A: in k8s, you can't set an owner reference across namespaces, which means that when --project-label is provided + // (where the ProjectHelmChart will be outside the systemNamespace where the HelmCharts and HelmReleases are created), + // ownerReferences will not be set on the object. However, wrangler annotations will be set since those objects are + // created via a wrangler apply. Therefore, we leverage those annotations to figure out which ProjectHelmChart to enqueue + if annotations == nil { + return nil, nil + } + ownerNamespace, ok := annotations[apply.LabelNamespace] + if !ok { + return nil, nil + } + ownerName, ok := annotations[apply.LabelName] + if !ok { + return nil, nil + } + + return []relatedresource.Key{{ + Namespace: ownerNamespace, + Name: ownerName, + }}, nil +} + +func (h *handler) resolveByProjectReleaseLabelValue(labels map[string]string, projectReleaseLabel string) ([]relatedresource.Key, error) { + if labels == nil { + return nil, nil + } + releaseName, ok := labels[projectReleaseLabel] + if !ok { + return nil, nil + } + projectHelmCharts, err := h.projectHelmChartCache.GetByIndex(ProjectHelmChartByReleaseName, releaseName) + if err != nil { + return nil, err + } + var keys []relatedresource.Key + for _, projectHelmChart := range projectHelmCharts { + if projectHelmChart == nil { + continue + } + keys = append(keys, relatedresource.Key{ + Namespace: projectHelmChart.Namespace, + Name: projectHelmChart.Name, + }) + } + return keys, nil +} diff --git a/pkg/helm-project-operator/controllers/project/resources.go b/pkg/helm-project-operator/controllers/project/resources.go new file mode 100644 index 00000000..cb870814 --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/resources.go @@ -0,0 +1,111 @@ +package project + +import ( + helmcontrollerv1 "github.com/k3s-io/helm-controller/pkg/apis/helm.cattle.io/v1" + "github.com/k3s-io/helm-controller/pkg/controllers/chart" + helmlockerv1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-locker/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-locker/controllers/release" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + v1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Note: each resource created here should have a resolver set in resolvers.go +// The only exception is ProjectHelmCharts since those are handled by the main generating controller + +// getHelmChart returns the HelmChart created on behalf of this ProjectHelmChart +func (h *handler) getHelmChart(projectID string, valuesContent string, projectHelmChart *v1alpha1.ProjectHelmChart) *helmcontrollerv1.HelmChart { + // must be in system namespace since helm controllers are configured to only watch one namespace + jobImage := DefaultJobImage + if len(h.opts.HelmJobImage) > 0 { + jobImage = h.opts.HelmJobImage + } + releaseNamespace, releaseName := h.getReleaseNamespaceAndName(projectHelmChart) + helmChart := helmcontrollerv1.NewHelmChart(h.systemNamespace, releaseName, helmcontrollerv1.HelmChart{ + Spec: helmcontrollerv1.HelmChartSpec{ + TargetNamespace: releaseNamespace, + Chart: releaseName, + JobImage: jobImage, + ChartContent: h.opts.ChartContent, + ValuesContent: valuesContent, + }, + }) + helmChart.SetLabels(common2.GetHelmResourceLabels(projectID, projectHelmChart.Spec.HelmAPIVersion)) + helmChart.SetAnnotations(map[string]string{ + chart.ManagedBy: h.opts.ControllerName, + }) + return helmChart +} + +// getHelmRelease returns the HelmRelease created on behalf of this ProjectHelmChart +func (h *handler) getHelmRelease(projectID string, projectHelmChart *v1alpha1.ProjectHelmChart) *helmlockerv1alpha1.HelmRelease { + // must be in system namespace since helmlocker controllers are configured to only watch one namespace + releaseNamespace, releaseName := h.getReleaseNamespaceAndName(projectHelmChart) + helmRelease := helmlockerv1alpha1.NewHelmRelease(h.systemNamespace, releaseName, helmlockerv1alpha1.HelmRelease{ + Spec: helmlockerv1alpha1.HelmReleaseSpec{ + Release: helmlockerv1alpha1.ReleaseKey{ + Namespace: releaseNamespace, + Name: releaseName, + }, + }, + }) + helmRelease.SetLabels(common2.GetHelmResourceLabels(projectID, projectHelmChart.Spec.HelmAPIVersion)) + helmRelease.SetAnnotations(map[string]string{ + release.ManagedBy: h.opts.ControllerName, + }) + return helmRelease +} + +// getProjectReleaseNamespace returns the Project Release Namespace created on behalf of this ProjectHelmChart, if required +func (h *handler) getProjectReleaseNamespace(projectID string, isOrphaned bool, projectHelmChart *v1alpha1.ProjectHelmChart) *v1.Namespace { + releaseNamespace, _ := h.getReleaseNamespaceAndName(projectHelmChart) + if releaseNamespace == h.systemNamespace || releaseNamespace == projectHelmChart.Namespace { + return nil + } + projectReleaseNamespace := &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: releaseNamespace, + Annotations: common2.GetProjectNamespaceAnnotations(h.opts.ProjectReleaseLabelValue, h.opts.ProjectLabel, h.opts.ClusterID), + Labels: common2.GetProjectNamespaceLabels(projectID, h.opts.ProjectLabel, h.opts.ProjectReleaseLabelValue, isOrphaned), + }, + } + return projectReleaseNamespace +} + +// getRoleBindings returns the RoleBindings created on behalf of this ProjectHelmChart in the Project Release Namespace based on Roles created in the +// Project Release Namespace and RoleBindings attached to the default operator roles (configured as AdminClusterRole, EditClusterRole, and ViewClusterRole +// +// in the providedRuntimeOptions) in the Project Registration Namespace only. To update these RoleBindings in the release namespace, you will need to assign +// +// additional permissions to the default roles in the Project Registration Namespace or manually assign RoleBindings in the release namespace. +func (h *handler) getRoleBindings(projectID string, k8sRoleToRoleRefs map[string][]rbacv1.RoleRef, k8sRoleToSubjects map[string][]rbacv1.Subject, projectHelmChart *v1alpha1.ProjectHelmChart) []runtime.Object { + var objs []runtime.Object + releaseNamespace, _ := h.getReleaseNamespaceAndName(projectHelmChart) + + for subjectRole := range common2.GetDefaultClusterRoles(h.opts) { + // note: these role refs point to roles in the release namespace + roleRefs := k8sRoleToRoleRefs[subjectRole] + // note: these subjects are inferred from the rolebindings tied to the default roles in the registration namespace + subjects := k8sRoleToSubjects[subjectRole] + if len(subjects) == 0 { + // no need to create empty RoleBindings + continue + } + for _, roleRef := range roleRefs { + objs = append(objs, &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleRef.Name, + Namespace: releaseNamespace, + Labels: common2.GetCommonLabels(projectID), + }, + RoleRef: roleRef, + Subjects: subjects, + }) + } + } + + return objs +} diff --git a/pkg/helm-project-operator/controllers/project/status.go b/pkg/helm-project-operator/controllers/project/status.go new file mode 100644 index 00000000..065f48bf --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/status.go @@ -0,0 +1,68 @@ +package project + +import ( + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" +) + +// getCleanupStatus returns the status on seeing the cleanup label on a ProjectHelmChart +func (h *handler) getCleanupStatus(projectHelmChart *v1alpha1.ProjectHelmChart, _ v1alpha1.ProjectHelmChartStatus) v1alpha1.ProjectHelmChartStatus { + return v1alpha1.ProjectHelmChartStatus{ + Status: "AwaitingOperatorRedeployment", + StatusMessage: fmt.Sprintf( + "ProjectHelmChart was marked with label %s=true, which indicates that the resource should be cleaned up "+ + "until the Project Operator that responds to ProjectHelmCharts in %s with spec.helmApiVersion=%s "+ + "is redeployed onto the cluster. On redeployment, this label will automatically be removed by the operator.", + common.HelmProjectOperatedCleanupLabel, projectHelmChart.Namespace, projectHelmChart.Spec.HelmAPIVersion, + ), + } +} + +// getUnableToCreateHelmReleaseStatus returns the status on seeing a conflicting ProjectHelmChart already tracking the desired Helm release +func (h *handler) getUnableToCreateHelmReleaseStatus(projectHelmChart *v1alpha1.ProjectHelmChart, _ v1alpha1.ProjectHelmChartStatus, err error) v1alpha1.ProjectHelmChartStatus { + releaseNamespace, releaseName := h.getReleaseNamespaceAndName(projectHelmChart) + return v1alpha1.ProjectHelmChartStatus{ + Status: "UnableToCreateHelmRelease", + StatusMessage: fmt.Sprintf( + "Unable to create a release (%s/%s) for ProjectHelmChart: %s", + releaseName, releaseNamespace, err, + ), + } +} + +// getNoTargetNamespacesStatus returns the status on seeing that a ProjectHelmChart's projectNamespaceSelector (or +// the Project Registration Namespace's namespaceSelector) targets no namespaces +func (h *handler) getNoTargetNamespacesStatus(_ *v1alpha1.ProjectHelmChart, _ v1alpha1.ProjectHelmChartStatus) v1alpha1.ProjectHelmChartStatus { + return v1alpha1.ProjectHelmChartStatus{ + Status: "NoTargetProjectNamespaces", + StatusMessage: "There are no project namespaces to deploy a ProjectHelmChart.", + } +} + +// getValuesParseErrorStatus returns the status on encountering an error with parsing the provided contents of spec.values on the ProjectHelmChart +func (h *handler) getValuesParseErrorStatus(_ *v1alpha1.ProjectHelmChart, projectHelmChartStatus v1alpha1.ProjectHelmChartStatus, err error) v1alpha1.ProjectHelmChartStatus { + // retain existing status if possible + projectHelmChartStatus.Status = "UnableToParseValues" + projectHelmChartStatus.StatusMessage = fmt.Sprintf("Unable to convert provided spec.values into valid configuration of ProjectHelmChart: %s", err) + return projectHelmChartStatus +} + +// getWaitingForDashboardValuesStatus returns the transitionary status that occurs after deploying a Helm chart but before a dashboard configmap is created +// If a ProjectHelmChart is stuck in this status, it is likely either an error on the Operator for not creating this ConfigMap or there might be an issue +// with the underlying Job ran by the child HelmChart resource created on this ProjectHelmChart's behalf +func (h *handler) getWaitingForDashboardValuesStatus(_ *v1alpha1.ProjectHelmChart, projectHelmChartStatus v1alpha1.ProjectHelmChartStatus) v1alpha1.ProjectHelmChartStatus { + // retain existing status + projectHelmChartStatus.Status = "WaitingForDashboardValues" + projectHelmChartStatus.StatusMessage = "Waiting for status.dashboardValues content to be provided by the deployed Helm release, but HelmChart and HelmRelease should be deployed." + projectHelmChartStatus.DashboardValues = nil + return projectHelmChartStatus +} + +// getDeployedStatus returns the status that indicates the ProjectHelmChart is successfully deployed +func (h *handler) getDeployedStatus(_ *v1alpha1.ProjectHelmChart, projectHelmChartStatus v1alpha1.ProjectHelmChartStatus) v1alpha1.ProjectHelmChartStatus { + // retain existing status + projectHelmChartStatus.Status = "Deployed" + projectHelmChartStatus.StatusMessage = "ProjectHelmChart has been successfully deployed!" + return projectHelmChartStatus +} diff --git a/pkg/helm-project-operator/controllers/project/utils.go b/pkg/helm-project-operator/controllers/project/utils.go new file mode 100644 index 00000000..90ebf9fe --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/utils.go @@ -0,0 +1,69 @@ +package project + +import ( + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" +) + +// getProjectID returns the projectID tied to this ProjectHelmChart +func (h *handler) getProjectID(projectHelmChart *v1alpha1.ProjectHelmChart) (string, error) { + if len(h.opts.ProjectLabel) == 0 { + // use the projectHelmChart's name as the projectID + return projectHelmChart.Name, nil + } + projectRegistrationNamespace, err := h.namespaceCache.Get(projectHelmChart.Namespace) + if err != nil { + return "", fmt.Errorf("unable to parse projectID for projectHelmChart %s/%s: %s", projectHelmChart.Namespace, projectHelmChart.Name, err) + } + projectID, ok := projectRegistrationNamespace.Labels[h.opts.ProjectLabel] + if !ok { + return "", nil + } + return projectID, nil +} + +// getProjectNamespaceSelector returns the projectNamespaceSelector tied to this ProjectHelmChart +func (h *handler) getProjectNamespaceSelector(projectHelmChart *v1alpha1.ProjectHelmChart, projectID string) map[string]interface{} { + if len(h.opts.ProjectLabel) == 0 { + // Use the projectHelmChart selector as the namespaceSelector + if projectHelmChart.Spec.ProjectNamespaceSelector == nil { + return map[string]interface{}{} + } + return map[string]interface{}{ + "matchLabels": projectHelmChart.Spec.ProjectNamespaceSelector.MatchLabels, + "matchExpressions": projectHelmChart.Spec.ProjectNamespaceSelector.MatchExpressions, + } + } + if len(h.opts.ProjectReleaseLabelValue) == 0 { + // Release namespace is not created, so use namespaceSelector provided tied to projectID + return map[string]interface{}{ + "matchLabels": map[string]string{ + h.opts.ProjectLabel: projectID, + }, + } + } + // use the HelmProjectOperated label + return map[string]interface{}{ + "matchLabels": map[string]string{ + common.HelmProjectOperatorProjectLabel: projectID, + }, + } +} + +// getReleaseNamespaceAndName returns the name of the Project Release namespace and the name of the Helm Release +// that will be deployed into the Project Release namespace on behalf of the ProjectHelmChart +func (h *handler) getReleaseNamespaceAndName(projectHelmChart *v1alpha1.ProjectHelmChart) (string, string) { + projectReleaseName := fmt.Sprintf("%s-%s", projectHelmChart.Name, h.opts.ReleaseName) + if h.opts.Singleton { + // This changes the naming scheme of the deployed resources such that only one can every be created per namespace + projectReleaseName = fmt.Sprintf("%s-%s", projectHelmChart.Namespace, h.opts.ReleaseName) + } + if len(h.opts.ProjectLabel) == 0 || len(h.opts.ProjectReleaseLabelValue) == 0 { + // Underlying Helm releases will be created in the namespace where the ProjectHelmChart is registered (project registration namespace) + // The project registration namespace will either be the system namespace or auto-generated namespaces depending on the user values provided + return projectHelmChart.Namespace, projectReleaseName + } + // Underlying Helm releases will be created in dedicated project release namespaces + return projectReleaseName, projectReleaseName +} diff --git a/pkg/helm-project-operator/controllers/project/values.go b/pkg/helm-project-operator/controllers/project/values.go new file mode 100644 index 00000000..6fb97390 --- /dev/null +++ b/pkg/helm-project-operator/controllers/project/values.go @@ -0,0 +1,41 @@ +package project + +import ( + v1alpha2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" +) + +// getValues returns the values.yaml that should be applied for this ProjectHelmChart after processing default and required overrides +func (h *handler) getValues(projectHelmChart *v1alpha2.ProjectHelmChart, projectID string, targetProjectNamespaces []string) v1alpha2.GenericMap { + // default values that are set if the user does not provide them + values := map[string]interface{}{ + "global": map[string]interface{}{ + "cattle": map[string]interface{}{ + "systemDefaultRegistry": h.opts.SystemDefaultRegistry, + "url": h.opts.CattleURL, + }, + }, + } + + // overlay provided values, which will override the above values if provided + values = MergeMaps(values, projectHelmChart.Spec.Values) + + // overlay operator provided values overrides, which will override the above values even if provided + values = MergeMaps(values, h.valuesOverride) + + // required project-based values that must be set even if user tries to override them + requiredOverrides := map[string]interface{}{ + "global": map[string]interface{}{ + "cattle": map[string]interface{}{ + "clusterId": h.opts.ClusterID, + "projectNamespaces": targetProjectNamespaces, + "projectID": projectID, + "releaseProjectID": h.opts.ProjectReleaseLabelValue, + "projectNamespaceSelector": h.getProjectNamespaceSelector(projectHelmChart, projectID), + }, + }, + } + // overlay required values, which will override the above values even if provided + values = MergeMaps(values, requiredOverrides) + + return values +} diff --git a/pkg/helm-project-operator/crd/crds.go b/pkg/helm-project-operator/crd/crds.go new file mode 100644 index 00000000..8df02d5e --- /dev/null +++ b/pkg/helm-project-operator/crd/crds.go @@ -0,0 +1,180 @@ +package crd + +import ( + "context" + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "io" + "os" + "path/filepath" + "strings" + "sync" + + helmcontrollercrd "github.com/k3s-io/helm-controller/pkg/crd" + helmlockercrd "github.com/rancher/prometheus-federator/pkg/helm-locker/crd" + "github.com/rancher/wrangler/pkg/crd" + "github.com/rancher/wrangler/pkg/yaml" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/rest" +) + +// WriteFiles writes CRDs and dependent CRDs to the paths specified +// +// Note: It is recommended to write CRDs to the templates directory (or similar) and to write +// CRD dependencies to the crds/ directory since you do not want the uninstall or upgrade of the +// CRD chart to destroy existing dependent CRDs in the cluster as that could break other components +// +// i.e. if you uninstall the HelmChart CRD, it can destroy an RKE2 or K3s cluster that also uses those CRs +// to manage internal Kubernetes component state +func WriteFiles(crdDirpath, crdDepDirpath string) error { + objs, depObjs, err := Objects(false) + if err != nil { + return err + } + if err := writeFiles(crdDirpath, objs); err != nil { + return err + } + return writeFiles(crdDepDirpath, depObjs) +} + +func writeFiles(dirpath string, objs []runtime.Object) error { + if err := os.MkdirAll(dirpath, 0755); err != nil { + return err + } + + objMap := make(map[string][]byte) + + for _, o := range objs { + data, err := yaml.Export(o) + if err != nil { + return err + } + meta, err := meta.Accessor(o) + if err != nil { + return err + } + key := strings.SplitN(meta.GetName(), ".", 2)[0] + objMap[key] = data + } + + var wg sync.WaitGroup + wg.Add(len(objMap)) + for key, data := range objMap { + go func(key string, data []byte) { + defer wg.Done() + f, err := os.Create(filepath.Join(dirpath, fmt.Sprintf("%s.yaml", key))) + if err != nil { + logrus.Error(err) + } + defer f.Close() + _, err = f.Write(data) + if err != nil { + logrus.Error(err) + } + }(key, data) + } + wg.Wait() + + return nil +} + +// Print prints CRDs to out and dependent CRDs to depOut +func Print(out io.Writer, depOut io.Writer) { + objs, depObjs, err := Objects(false) + if err != nil { + logrus.Fatalf("%s", err) + } + if err := printCrd(out, objs); err != nil { + logrus.Fatalf("%s", err) + } + if err := printCrd(depOut, depObjs); err != nil { + logrus.Fatalf("%s", err) + } +} + +func printCrd(out io.Writer, objs []runtime.Object) error { + data, err := yaml.Export(objs...) + if err != nil { + return err + } + _, err = out.Write(data) + return err +} + +// Objects returns runtime.Objects for every CRD or CRD Dependency this operator relies on +func Objects(v1beta1 bool) (crds, crdDeps []runtime.Object, err error) { + crdDefs, crdDepDefs := List() + crds, err = objects(v1beta1, crdDefs) + if err != nil { + return nil, nil, err + } + crdDeps, err = objects(v1beta1, crdDepDefs) + if err != nil { + return nil, nil, err + } + return +} + +func objects(v1beta1 bool, crdDefs []crd.CRD) (crds []runtime.Object, err error) { + for _, crdDef := range crdDefs { + if v1beta1 { + crd, err := crdDef.ToCustomResourceDefinitionV1Beta1() + if err != nil { + return nil, err + } + crds = append(crds, crd) + } else { + crd, err := crdDef.ToCustomResourceDefinition() + if err != nil { + return nil, err + } + crds = append(crds, crd) + } + } + return +} + +// List returns the list of CRDs and dependent CRDs for this operator +func List() ([]crd.CRD, []crd.CRD) { + crds := []crd.CRD{ + newCRD(&v1alpha1.ProjectHelmChart{}, func(c crd.CRD) crd.CRD { + return c. + WithColumn("Status", ".status.status"). + WithColumn("System Namespace", ".status.systemNamespace"). + WithColumn("Release Namespace", ".status.releaseNamespace"). + WithColumn("Release Name", ".status.releaseName"). + WithColumn("Target Namespaces", ".status.targetNamespaces") + }), + } + crdDeps := append(helmcontrollercrd.List(), helmlockercrd.List()...) + return crds, crdDeps +} + +// Create creates all CRDs and dependent CRDs in the cluster +func Create(ctx context.Context, cfg *rest.Config) error { + factory, err := crd.NewFactoryFromClient(cfg) + if err != nil { + return err + } + + crds, crdDeps := List() + return factory.BatchCreateCRDs(ctx, append(crds, crdDeps...)...).BatchWait() +} + +func newCRD(obj interface{}, customize func(crd.CRD) crd.CRD) crd.CRD { + crd := crd.CRD{ + GVK: schema.GroupVersionKind{ + Group: "helm.cattle.io", + Version: "v1alpha1", + }, + Status: true, + SchemaObject: obj, + } + if customize != nil { + crd = customize(crd) + } + return crd +} diff --git a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/factory.go b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/factory.go new file mode 100644 index 00000000..38c58cd4 --- /dev/null +++ b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/factory.go @@ -0,0 +1,67 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package helm + +import ( + "github.com/rancher/wrangler/pkg/generic" + "k8s.io/client-go/rest" +) + +type Factory struct { + *generic.Factory +} + +func NewFactoryFromConfigOrDie(config *rest.Config) *Factory { + f, err := NewFactoryFromConfig(config) + if err != nil { + panic(err) + } + return f +} + +func NewFactoryFromConfig(config *rest.Config) (*Factory, error) { + return NewFactoryFromConfigWithOptions(config, nil) +} + +func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) { + return NewFactoryFromConfigWithOptions(config, &FactoryOptions{ + Namespace: namespace, + }) +} + +type FactoryOptions = generic.FactoryOptions + +func NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) { + f, err := generic.NewFactoryFromConfigWithOptions(config, opts) + return &Factory{ + Factory: f, + }, err +} + +func NewFactoryFromConfigWithOptionsOrDie(config *rest.Config, opts *FactoryOptions) *Factory { + f, err := NewFactoryFromConfigWithOptions(config, opts) + if err != nil { + panic(err) + } + return f +} + +func (c *Factory) Helm() Interface { + return New(c.ControllerFactory()) +} diff --git a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/interface.go b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/interface.go new file mode 100644 index 00000000..7b907e1b --- /dev/null +++ b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/interface.go @@ -0,0 +1,43 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package helm + +import ( + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1" + "github.com/rancher/lasso/pkg/controller" +) + +type Interface interface { + V1alpha1() v1alpha1.Interface +} + +type group struct { + controllerFactory controller.SharedControllerFactory +} + +// New returns a new Interface. +func New(controllerFactory controller.SharedControllerFactory) Interface { + return &group{ + controllerFactory: controllerFactory, + } +} + +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.controllerFactory) +} diff --git a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/interface.go b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/interface.go new file mode 100644 index 00000000..f3ef9bd1 --- /dev/null +++ b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/interface.go @@ -0,0 +1,48 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/schemes" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func init() { + schemes.Register(v1alpha1.AddToScheme) +} + +type Interface interface { + ProjectHelmChart() ProjectHelmChartController +} + +func New(controllerFactory controller.SharedControllerFactory) Interface { + return &version{ + controllerFactory: controllerFactory, + } +} + +type version struct { + controllerFactory controller.SharedControllerFactory +} + +func (c *version) ProjectHelmChart() ProjectHelmChartController { + return NewProjectHelmChartController(schema.GroupVersionKind{Group: "helm.cattle.io", Version: "v1alpha1", Kind: "ProjectHelmChart"}, "projecthelmcharts", true, c.controllerFactory) +} diff --git a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/projecthelmchart.go b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/projecthelmchart.go new file mode 100644 index 00000000..9648a73c --- /dev/null +++ b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/projecthelmchart.go @@ -0,0 +1,376 @@ +/* +Copyright 2024 Rancher Labs, Inc. + +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. +*/ + +// Code generated by main. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "github.com/rancher/lasso/pkg/client" + "github.com/rancher/lasso/pkg/controller" + "github.com/rancher/wrangler/pkg/apply" + "github.com/rancher/wrangler/pkg/condition" + "github.com/rancher/wrangler/pkg/generic" + "github.com/rancher/wrangler/pkg/kv" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" +) + +type ProjectHelmChartHandler func(string, *v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) + +type ProjectHelmChartController interface { + generic.ControllerMeta + ProjectHelmChartClient + + OnChange(ctx context.Context, name string, sync ProjectHelmChartHandler) + OnRemove(ctx context.Context, name string, sync ProjectHelmChartHandler) + Enqueue(namespace, name string) + EnqueueAfter(namespace, name string, duration time.Duration) + + Cache() ProjectHelmChartCache +} + +type ProjectHelmChartClient interface { + Create(*v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) + Update(*v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) + UpdateStatus(*v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) + Delete(namespace, name string, options *metav1.DeleteOptions) error + Get(namespace, name string, options metav1.GetOptions) (*v1alpha1.ProjectHelmChart, error) + List(namespace string, opts metav1.ListOptions) (*v1alpha1.ProjectHelmChartList, error) + Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error) + Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ProjectHelmChart, err error) +} + +type ProjectHelmChartCache interface { + Get(namespace, name string) (*v1alpha1.ProjectHelmChart, error) + List(namespace string, selector labels.Selector) ([]*v1alpha1.ProjectHelmChart, error) + + AddIndexer(indexName string, indexer ProjectHelmChartIndexer) + GetByIndex(indexName, key string) ([]*v1alpha1.ProjectHelmChart, error) +} + +type ProjectHelmChartIndexer func(obj *v1alpha1.ProjectHelmChart) ([]string, error) + +type projectHelmChartController struct { + controller controller.SharedController + client *client.Client + gvk schema.GroupVersionKind + groupResource schema.GroupResource +} + +func NewProjectHelmChartController(gvk schema.GroupVersionKind, resource string, namespaced bool, controller controller.SharedControllerFactory) ProjectHelmChartController { + c := controller.ForResourceKind(gvk.GroupVersion().WithResource(resource), gvk.Kind, namespaced) + return &projectHelmChartController{ + controller: c, + client: c.Client(), + gvk: gvk, + groupResource: schema.GroupResource{ + Group: gvk.Group, + Resource: resource, + }, + } +} + +func FromProjectHelmChartHandlerToHandler(sync ProjectHelmChartHandler) generic.Handler { + return func(key string, obj runtime.Object) (ret runtime.Object, err error) { + var v *v1alpha1.ProjectHelmChart + if obj == nil { + v, err = sync(key, nil) + } else { + v, err = sync(key, obj.(*v1alpha1.ProjectHelmChart)) + } + if v == nil { + return nil, err + } + return v, err + } +} + +func (c *projectHelmChartController) Updater() generic.Updater { + return func(obj runtime.Object) (runtime.Object, error) { + newObj, err := c.Update(obj.(*v1alpha1.ProjectHelmChart)) + if newObj == nil { + return nil, err + } + return newObj, err + } +} + +func UpdateProjectHelmChartDeepCopyOnChange(client ProjectHelmChartClient, obj *v1alpha1.ProjectHelmChart, handler func(obj *v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error)) (*v1alpha1.ProjectHelmChart, error) { + if obj == nil { + return obj, nil + } + + copyObj := obj.DeepCopy() + newObj, err := handler(copyObj) + if newObj != nil { + copyObj = newObj + } + if obj.ResourceVersion == copyObj.ResourceVersion && !equality.Semantic.DeepEqual(obj, copyObj) { + return client.Update(copyObj) + } + + return copyObj, err +} + +func (c *projectHelmChartController) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) { + c.controller.RegisterHandler(ctx, name, controller.SharedControllerHandlerFunc(handler)) +} + +func (c *projectHelmChartController) AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler) { + c.AddGenericHandler(ctx, name, generic.NewRemoveHandler(name, c.Updater(), handler)) +} + +func (c *projectHelmChartController) OnChange(ctx context.Context, name string, sync ProjectHelmChartHandler) { + c.AddGenericHandler(ctx, name, FromProjectHelmChartHandlerToHandler(sync)) +} + +func (c *projectHelmChartController) OnRemove(ctx context.Context, name string, sync ProjectHelmChartHandler) { + c.AddGenericHandler(ctx, name, generic.NewRemoveHandler(name, c.Updater(), FromProjectHelmChartHandlerToHandler(sync))) +} + +func (c *projectHelmChartController) Enqueue(namespace, name string) { + c.controller.Enqueue(namespace, name) +} + +func (c *projectHelmChartController) EnqueueAfter(namespace, name string, duration time.Duration) { + c.controller.EnqueueAfter(namespace, name, duration) +} + +func (c *projectHelmChartController) Informer() cache.SharedIndexInformer { + return c.controller.Informer() +} + +func (c *projectHelmChartController) GroupVersionKind() schema.GroupVersionKind { + return c.gvk +} + +func (c *projectHelmChartController) Cache() ProjectHelmChartCache { + return &projectHelmChartCache{ + indexer: c.Informer().GetIndexer(), + resource: c.groupResource, + } +} + +func (c *projectHelmChartController) Create(obj *v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) { + result := &v1alpha1.ProjectHelmChart{} + return result, c.client.Create(context.TODO(), obj.Namespace, obj, result, metav1.CreateOptions{}) +} + +func (c *projectHelmChartController) Update(obj *v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) { + result := &v1alpha1.ProjectHelmChart{} + return result, c.client.Update(context.TODO(), obj.Namespace, obj, result, metav1.UpdateOptions{}) +} + +func (c *projectHelmChartController) UpdateStatus(obj *v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) { + result := &v1alpha1.ProjectHelmChart{} + return result, c.client.UpdateStatus(context.TODO(), obj.Namespace, obj, result, metav1.UpdateOptions{}) +} + +func (c *projectHelmChartController) Delete(namespace, name string, options *metav1.DeleteOptions) error { + if options == nil { + options = &metav1.DeleteOptions{} + } + return c.client.Delete(context.TODO(), namespace, name, *options) +} + +func (c *projectHelmChartController) Get(namespace, name string, options metav1.GetOptions) (*v1alpha1.ProjectHelmChart, error) { + result := &v1alpha1.ProjectHelmChart{} + return result, c.client.Get(context.TODO(), namespace, name, result, options) +} + +func (c *projectHelmChartController) List(namespace string, opts metav1.ListOptions) (*v1alpha1.ProjectHelmChartList, error) { + result := &v1alpha1.ProjectHelmChartList{} + return result, c.client.List(context.TODO(), namespace, result, opts) +} + +func (c *projectHelmChartController) Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error) { + return c.client.Watch(context.TODO(), namespace, opts) +} + +func (c *projectHelmChartController) Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (*v1alpha1.ProjectHelmChart, error) { + result := &v1alpha1.ProjectHelmChart{} + return result, c.client.Patch(context.TODO(), namespace, name, pt, data, result, metav1.PatchOptions{}, subresources...) +} + +type projectHelmChartCache struct { + indexer cache.Indexer + resource schema.GroupResource +} + +func (c *projectHelmChartCache) Get(namespace, name string) (*v1alpha1.ProjectHelmChart, error) { + obj, exists, err := c.indexer.GetByKey(namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(c.resource, name) + } + return obj.(*v1alpha1.ProjectHelmChart), nil +} + +func (c *projectHelmChartCache) List(namespace string, selector labels.Selector) (ret []*v1alpha1.ProjectHelmChart, err error) { + + err = cache.ListAllByNamespace(c.indexer, namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ProjectHelmChart)) + }) + + return ret, err +} + +func (c *projectHelmChartCache) AddIndexer(indexName string, indexer ProjectHelmChartIndexer) { + utilruntime.Must(c.indexer.AddIndexers(map[string]cache.IndexFunc{ + indexName: func(obj interface{}) (strings []string, e error) { + return indexer(obj.(*v1alpha1.ProjectHelmChart)) + }, + })) +} + +func (c *projectHelmChartCache) GetByIndex(indexName, key string) (result []*v1alpha1.ProjectHelmChart, err error) { + objs, err := c.indexer.ByIndex(indexName, key) + if err != nil { + return nil, err + } + result = make([]*v1alpha1.ProjectHelmChart, 0, len(objs)) + for _, obj := range objs { + result = append(result, obj.(*v1alpha1.ProjectHelmChart)) + } + return result, nil +} + +type ProjectHelmChartStatusHandler func(obj *v1alpha1.ProjectHelmChart, status v1alpha1.ProjectHelmChartStatus) (v1alpha1.ProjectHelmChartStatus, error) + +type ProjectHelmChartGeneratingHandler func(obj *v1alpha1.ProjectHelmChart, status v1alpha1.ProjectHelmChartStatus) ([]runtime.Object, v1alpha1.ProjectHelmChartStatus, error) + +func RegisterProjectHelmChartStatusHandler(ctx context.Context, controller ProjectHelmChartController, condition condition.Cond, name string, handler ProjectHelmChartStatusHandler) { + statusHandler := &projectHelmChartStatusHandler{ + client: controller, + condition: condition, + handler: handler, + } + controller.AddGenericHandler(ctx, name, FromProjectHelmChartHandlerToHandler(statusHandler.sync)) +} + +func RegisterProjectHelmChartGeneratingHandler(ctx context.Context, controller ProjectHelmChartController, apply apply.Apply, + condition condition.Cond, name string, handler ProjectHelmChartGeneratingHandler, opts *generic.GeneratingHandlerOptions) { + statusHandler := &projectHelmChartGeneratingHandler{ + ProjectHelmChartGeneratingHandler: handler, + apply: apply, + name: name, + gvk: controller.GroupVersionKind(), + } + if opts != nil { + statusHandler.opts = *opts + } + controller.OnChange(ctx, name, statusHandler.Remove) + RegisterProjectHelmChartStatusHandler(ctx, controller, condition, name, statusHandler.Handle) +} + +type projectHelmChartStatusHandler struct { + client ProjectHelmChartClient + condition condition.Cond + handler ProjectHelmChartStatusHandler +} + +func (a *projectHelmChartStatusHandler) sync(key string, obj *v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) { + if obj == nil { + return obj, nil + } + + origStatus := obj.Status.DeepCopy() + obj = obj.DeepCopy() + newStatus, err := a.handler(obj, obj.Status) + if err != nil { + // Revert to old status on error + newStatus = *origStatus.DeepCopy() + } + + if a.condition != "" { + if errors.IsConflict(err) { + a.condition.SetError(&newStatus, "", nil) + } else { + a.condition.SetError(&newStatus, "", err) + } + } + if !equality.Semantic.DeepEqual(origStatus, &newStatus) { + if a.condition != "" { + // Since status has changed, update the lastUpdatedTime + a.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339)) + } + + var newErr error + obj.Status = newStatus + newObj, newErr := a.client.UpdateStatus(obj) + if err == nil { + err = newErr + } + if newErr == nil { + obj = newObj + } + } + return obj, err +} + +type projectHelmChartGeneratingHandler struct { + ProjectHelmChartGeneratingHandler + apply apply.Apply + opts generic.GeneratingHandlerOptions + gvk schema.GroupVersionKind + name string +} + +func (a *projectHelmChartGeneratingHandler) Remove(key string, obj *v1alpha1.ProjectHelmChart) (*v1alpha1.ProjectHelmChart, error) { + if obj != nil { + return obj, nil + } + + obj = &v1alpha1.ProjectHelmChart{} + obj.Namespace, obj.Name = kv.RSplit(key, "/") + obj.SetGroupVersionKind(a.gvk) + + return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts). + WithOwner(obj). + WithSetID(a.name). + ApplyObjects() +} + +func (a *projectHelmChartGeneratingHandler) Handle(obj *v1alpha1.ProjectHelmChart, status v1alpha1.ProjectHelmChartStatus) (v1alpha1.ProjectHelmChartStatus, error) { + if !obj.DeletionTimestamp.IsZero() { + return status, nil + } + + objs, newStatus, err := a.ProjectHelmChartGeneratingHandler(obj, status) + if err != nil { + return newStatus, err + } + + return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts). + WithOwner(obj). + WithSetID(a.name). + ApplyObjects(objs...) +} diff --git a/pkg/helm-project-operator/operator/init.go b/pkg/helm-project-operator/operator/init.go new file mode 100644 index 00000000..2c8d5530 --- /dev/null +++ b/pkg/helm-project-operator/operator/init.go @@ -0,0 +1,34 @@ +package operator + +import ( + "context" + "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/crd" + + "github.com/rancher/wrangler/pkg/ratelimit" + "k8s.io/client-go/tools/clientcmd" +) + +// Init sets up a new Helm Project Operator with the provided options and configuration +func Init(ctx context.Context, systemNamespace string, cfg clientcmd.ClientConfig, opts common.Options) error { + if systemNamespace == "" { + return fmt.Errorf("system namespace was not specified, unclear where to place HelmCharts or HelmReleases") + } + if err := opts.Validate(); err != nil { + return err + } + + clientConfig, err := cfg.ClientConfig() + if err != nil { + return err + } + clientConfig.RateLimiter = ratelimit.None + + if err := crd.Create(ctx, clientConfig); err != nil { + return err + } + + return controllers.Register(ctx, systemNamespace, cfg, opts) +} From 3a77b3fe9ea01d38bf010d93c7ede3f37823b99a Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 14:32:08 -0400 Subject: [PATCH 25/45] Update gomod to remove helm-project-operator imports --- go.mod | 7 +++---- go.sum | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 1b698838..7130f8f1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/rancher/prometheus-federator go 1.22.3 replace ( - github.com/rancher/helm-project-operator/pkg/helm-locker => ./pkg/helm-locker k8s.io/api => k8s.io/api v0.22.3 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.22.3 k8s.io/apimachinery => k8s.io/apimachinery v0.22.3 @@ -14,12 +13,13 @@ replace ( require ( github.com/google/uuid v1.2.0 github.com/hashicorp/go-multierror v1.1.0 - github.com/rancher/helm-project-operator v0.3.1 + github.com/k3s-io/helm-controller v0.13.1 github.com/rancher/lasso v0.0.0-20220303220127-8cf5555ec03c github.com/rancher/wrangler v0.8.11-0.20220217210408-3ecd23dfea3b github.com/rancher/wrangler-cli v0.0.0-20211112052728-f172e9bf59af github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.4.0 + gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.8.0 k8s.io/api v0.23.3 k8s.io/apimachinery v0.23.3 @@ -47,7 +47,6 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmoiron/sqlx v1.3.4 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/k3s-io/helm-controller v0.13.1 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.4 // indirect @@ -56,6 +55,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nxadm/tail v1.4.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -79,7 +79,6 @@ require ( google.golang.org/protobuf v1.27.1 // indirect gopkg.in/gorp.v1 v1.7.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/apiextensions-apiserver v0.23.1 // indirect k8s.io/code-generator v0.23.3 // indirect diff --git a/go.sum b/go.sum index 8bb21116..f1a3f30a 100644 --- a/go.sum +++ b/go.sum @@ -941,8 +941,6 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA= github.com/rancher/client-go v1.22.3-rancher.1 h1:aNVLaIY5YGah1i9wRVXLOGRbLyekohjQAKHXeQm6Cxo= github.com/rancher/client-go v1.22.3-rancher.1/go.mod h1:ElDjYf8gvZsKDYexmsmnMQ0DYO8W9RwBjfQ1PI53yow= -github.com/rancher/helm-project-operator v0.3.1 h1:yN/DZCTPpCDDfBPTjc4emXuKn4jPGVROiGU5W6Y1m60= -github.com/rancher/helm-project-operator v0.3.1/go.mod h1:HkQq2yAWVGoZ0Q6jUlNTJaI2J8mar/PF8Ur2PN9nmYY= github.com/rancher/lasso v0.0.0-20210616224652-fc3ebd901c08/go.mod h1:9qZd/S8DqWzfKtjKGgSoHqGEByYmUE3qRaBaaAHwfEM= github.com/rancher/lasso v0.0.0-20220303220127-8cf5555ec03c h1:TyDYClPPCN2rWM97gd1jkvzlEy6ByYEN9IMK6nUY3dY= github.com/rancher/lasso v0.0.0-20220303220127-8cf5555ec03c/go.mod h1:T6WoUopOHBWTGjnphruTJAgoZ+dpm6llvn6GDYaa7Kw= From f116e557f2323d89b3c662bfe345de864594a621 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 14:35:54 -0400 Subject: [PATCH 26/45] Add hpo example chart --- .../project-operator-example/0.0.0/Chart.yaml | 11 ++++ .../project-operator-example/0.0.0/README.md | 5 ++ .../0.0.0/questions.yaml | 7 +++ .../0.0.0/templates/configmaps.yaml | 45 ++++++++++++++ .../0.0.0/templates/dashboard-roles.yaml | 61 +++++++++++++++++++ .../templates/dashboard-values-configmap.yaml | 10 +++ .../0.0.0/values.yaml | 34 +++++++++++ 7 files changed, 173 insertions(+) create mode 100644 charts/project-operator-example/0.0.0/Chart.yaml create mode 100644 charts/project-operator-example/0.0.0/README.md create mode 100644 charts/project-operator-example/0.0.0/questions.yaml create mode 100644 charts/project-operator-example/0.0.0/templates/configmaps.yaml create mode 100755 charts/project-operator-example/0.0.0/templates/dashboard-roles.yaml create mode 100644 charts/project-operator-example/0.0.0/templates/dashboard-values-configmap.yaml create mode 100644 charts/project-operator-example/0.0.0/values.yaml diff --git a/charts/project-operator-example/0.0.0/Chart.yaml b/charts/project-operator-example/0.0.0/Chart.yaml new file mode 100644 index 00000000..cb2d7715 --- /dev/null +++ b/charts/project-operator-example/0.0.0/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: project-operator-example +description: Example Helm Project Operator chart +version: 0.0.0 +appVersion: 0.0.0 +annotations: + catalog.cattle.io/certified: rancher + catalog.cattle.io/hidden: "true" + catalog.cattle.io/release-name: project-operator-example + catalog.cattle.io/os: linux,windows + catalog.cattle.io/permits-os: linux,windows diff --git a/charts/project-operator-example/0.0.0/README.md b/charts/project-operator-example/0.0.0/README.md new file mode 100644 index 00000000..6f12dc05 --- /dev/null +++ b/charts/project-operator-example/0.0.0/README.md @@ -0,0 +1,5 @@ +# project-operator-example + +This chart is a dummy chart that is deployed on behalf of the default Helm Project Operator. + +This chart is primarily intended for testing purposes. diff --git a/charts/project-operator-example/0.0.0/questions.yaml b/charts/project-operator-example/0.0.0/questions.yaml new file mode 100644 index 00000000..2464e356 --- /dev/null +++ b/charts/project-operator-example/0.0.0/questions.yaml @@ -0,0 +1,7 @@ +questions: + - variable: data + label: mydata + description: My Data + type: string + required: true + group: Data diff --git a/charts/project-operator-example/0.0.0/templates/configmaps.yaml b/charts/project-operator-example/0.0.0/templates/configmaps.yaml new file mode 100644 index 00000000..55336dbc --- /dev/null +++ b/charts/project-operator-example/0.0.0/templates/configmaps.yaml @@ -0,0 +1,45 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config-map + namespace: {{ .Release.Namespace }} +data: + config: |- +{{ .Values.data | toYaml | indent 4 }} + project-namespaces: |- +{{ .Values.global.cattle.projectNamespaces | toYaml | indent 4 }} + project-id: |- +{{ .Values.global.cattle.projectID | toYaml | indent 4 }} + release-project-id: |- +{{ .Values.global.cattle.releaseProjectID | toYaml | indent 4 }} + project-namespace-selector: |- +{{ .Values.global.cattle.projectNamespaceSelector | toYaml | indent 4 }} + system-default-registry: |- +{{ .Values.global.cattle.systemDefaultRegistry | toYaml | indent 4 }} + cattle-url: |- +{{ .Values.global.cattle.url | toYaml | indent 4 }} + cluster-id: |- +{{ .Values.global.cattle.clusterId | toYaml | indent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config-map-2 + namespace: {{ .Release.Namespace }} +data: + config: |- +{{ .Values.data | toYaml | indent 4 }} + project-namespaces: |- +{{ .Values.global.cattle.projectNamespaces | toYaml | indent 4 }} + project-id: |- +{{ .Values.global.cattle.projectID | toYaml | indent 4 }} + release-project-id: |- +{{ .Values.global.cattle.releaseProjectID | toYaml | indent 4 }} + project-namespace-selector: |- +{{ .Values.global.cattle.projectNamespaceSelector | toYaml | indent 4 }} + system-default-registry: |- +{{ .Values.global.cattle.systemDefaultRegistry | toYaml | indent 4 }} + cattle-url: |- +{{ .Values.global.cattle.url | toYaml | indent 4 }} + cluster-id: |- +{{ .Values.global.cattle.clusterId | toYaml | indent 4 }} diff --git a/charts/project-operator-example/0.0.0/templates/dashboard-roles.yaml b/charts/project-operator-example/0.0.0/templates/dashboard-roles.yaml new file mode 100755 index 00000000..4fb0f004 --- /dev/null +++ b/charts/project-operator-example/0.0.0/templates/dashboard-roles.yaml @@ -0,0 +1,61 @@ +{{- if and .Values.global.rbac.create .Values.global.rbac.userRoles.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-admin + namespace: {{ .Release.Namespace }} + labels: + helm.cattle.io/project-helm-chart-role: {{ .Release.Name }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + helm.cattle.io/project-helm-chart-role-aggregate-from: admin + {{- end }} +rules: +- apiGroups: + - "test.cattle.io" + resources: + - test + resourceNames: + - test + verbs: + - 'test' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-edit + namespace: {{ .Release.Namespace }} + labels: + helm.cattle.io/project-helm-chart-role: {{ .Release.Name }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + helm.cattle.io/project-helm-chart-role-aggregate-from: edit + {{- end }} +rules: +- apiGroups: + - "test.cattle.io" + resources: + - test + resourceNames: + - test + verbs: + - 'test' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-view + namespace: {{ .Release.Namespace }} + labels: + helm.cattle.io/project-helm-chart-role: {{ .Release.Name }} + {{- if .Values.global.rbac.userRoles.aggregateToDefaultRoles }} + helm.cattle.io/project-helm-chart-role-aggregate-from: view + {{- end }} +rules: +- apiGroups: + - "test.cattle.io" + resources: + - test + resourceNames: + - test + verbs: + - 'test' +{{- end }} diff --git a/charts/project-operator-example/0.0.0/templates/dashboard-values-configmap.yaml b/charts/project-operator-example/0.0.0/templates/dashboard-values-configmap.yaml new file mode 100644 index 00000000..a117a47f --- /dev/null +++ b/charts/project-operator-example/0.0.0/templates/dashboard-values-configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-dashboard-values + namespace: {{ .Release.Namespace }} + labels: + helm.cattle.io/dashboard-values-configmap: {{ .Release.Name }} +data: + data.json: |- +{{ .Values.data | toJson | indent 4 }} diff --git a/charts/project-operator-example/0.0.0/values.yaml b/charts/project-operator-example/0.0.0/values.yaml new file mode 100644 index 00000000..c4c2d53a --- /dev/null +++ b/charts/project-operator-example/0.0.0/values.yaml @@ -0,0 +1,34 @@ +global: + cattle: + clusterID: "" + projectID: "" + projectNamespaces: [] + projectNamespaceSelector: {} + releaseProjectID: "" + systemDefaultRegistry: "" + url: "" + rbac: + ## Create RBAC resources for ServiceAccounts and users + ## + create: true + + userRoles: + ## Create default user Roles that the Helm Project Operator will automatically create RoleBindings for + ## + ## How does this work? + ## + ## The operator will watch for all subjects bound to each Kubernetes default ClusterRole in the project registration namespace + ## where the ProjectHelmChart that deployed this chart belongs to; if it observes a subject bound to a particular role in + ## the project registration namespace (e.g. edit) and if a Role exists that is deployed by this chart with the label + ## 'helm.cattle.io/project-helm-chart-role-aggregate-from': '', it will automaticaly create a RoleBinding + ## in the release namespace binding all such subjects to that Role. + ## + ## Note: while the default behavior is to use the Kubernetes default ClusterRole, the operator deployment can be configured + ## to use a different set of ClusterRoles as the source of truth for admin, edit, and view permissions. + ## + create: true + ## Add labels to Roles to have the operator pick them up + aggregateToDefaultRoles: true + +data: + hello: world From 0f0fbdc532d7d54044f4e1b406b56144ccc84d90 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 14:41:23 -0400 Subject: [PATCH 27/45] Enable hpo generator --- generate.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/generate.go b/generate.go index 12622a80..2b15f877 100644 --- a/generate.go +++ b/generate.go @@ -2,8 +2,8 @@ //go:generate go run pkg/helm-locker/codegen/main.go //go:generate go run ./pkg/helm-locker/codegen crds ./crds/helm-locker/crds.yaml -// //go:generate go run pkg/helm-project-operator/codegen/cleanup/main.go -// //go:generate go run pkg/helm-project-operator/codegen/main.go -// //go:generate go run ./pkg/helm-project-operator/codegen crds ./crds/helm-project-operator ./crds/helm-project-operator +//go:generate go run pkg/helm-project-operator/codegen/cleanup/main.go +//go:generate go run pkg/helm-project-operator/codegen/main.go +//go:generate go run ./pkg/helm-project-operator/codegen crds ./crds/helm-project-operator ./crds/helm-project-operator package main From c383c30e62017130f7c718d77e232681bf7b84e1 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 14:45:05 -0400 Subject: [PATCH 28/45] import existing crds files --- .../helmchartconfigs.yaml | 29 +++++ crds/helm-project-operator/helmcharts.yaml | 92 ++++++++++++++++ crds/helm-project-operator/helmreleases.yaml | 85 ++++++++++++++ .../projecthelmcharts.yaml | 104 ++++++++++++++++++ 4 files changed, 310 insertions(+) create mode 100644 crds/helm-project-operator/helmchartconfigs.yaml create mode 100644 crds/helm-project-operator/helmcharts.yaml create mode 100644 crds/helm-project-operator/helmreleases.yaml create mode 100644 crds/helm-project-operator/projecthelmcharts.yaml diff --git a/crds/helm-project-operator/helmchartconfigs.yaml b/crds/helm-project-operator/helmchartconfigs.yaml new file mode 100644 index 00000000..33165da7 --- /dev/null +++ b/crds/helm-project-operator/helmchartconfigs.yaml @@ -0,0 +1,29 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: helmchartconfigs.helm.cattle.io +spec: + group: helm.cattle.io + names: + kind: HelmChartConfig + plural: helmchartconfigs + singular: helmchartconfig + preserveUnknownFields: false + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + failurePolicy: + nullable: true + type: string + valuesContent: + nullable: true + type: string + type: object + type: object + served: true + storage: true diff --git a/crds/helm-project-operator/helmcharts.yaml b/crds/helm-project-operator/helmcharts.yaml new file mode 100644 index 00000000..b0c232be --- /dev/null +++ b/crds/helm-project-operator/helmcharts.yaml @@ -0,0 +1,92 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: helmcharts.helm.cattle.io +spec: + group: helm.cattle.io + names: + kind: HelmChart + plural: helmcharts + singular: helmchart + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.jobName + name: Job + type: string + - jsonPath: .spec.chart + name: Chart + type: string + - jsonPath: .spec.targetNamespace + name: TargetNamespace + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.repo + name: Repo + type: string + - jsonPath: .spec.helmVersion + name: HelmVersion + type: string + - jsonPath: .spec.bootstrap + name: Bootstrap + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + bootstrap: + type: boolean + chart: + nullable: true + type: string + chartContent: + nullable: true + type: string + failurePolicy: + nullable: true + type: string + helmVersion: + nullable: true + type: string + jobImage: + nullable: true + type: string + repo: + nullable: true + type: string + repoCA: + nullable: true + type: string + set: + additionalProperties: + nullable: true + type: string + nullable: true + type: object + targetNamespace: + nullable: true + type: string + timeout: + nullable: true + type: string + valuesContent: + nullable: true + type: string + version: + nullable: true + type: string + type: object + status: + properties: + jobName: + nullable: true + type: string + type: object + type: object + served: true + storage: true diff --git a/crds/helm-project-operator/helmreleases.yaml b/crds/helm-project-operator/helmreleases.yaml new file mode 100644 index 00000000..79588258 --- /dev/null +++ b/crds/helm-project-operator/helmreleases.yaml @@ -0,0 +1,85 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: helmreleases.helm.cattle.io +spec: + group: helm.cattle.io + names: + kind: HelmRelease + plural: helmreleases + singular: helmrelease + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.release.name + name: Release Name + type: string + - jsonPath: .spec.release.namespace + name: Release Namespace + type: string + - jsonPath: .status.version + name: Version + type: string + - jsonPath: .status.state + name: State + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + release: + properties: + name: + nullable: true + type: string + namespace: + nullable: true + type: string + type: object + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + nullable: true + type: string + lastUpdateTime: + nullable: true + type: string + message: + nullable: true + type: string + reason: + nullable: true + type: string + status: + nullable: true + type: string + type: + nullable: true + type: string + type: object + nullable: true + type: array + description: + nullable: true + type: string + notes: + nullable: true + type: string + state: + nullable: true + type: string + version: + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/crds/helm-project-operator/projecthelmcharts.yaml b/crds/helm-project-operator/projecthelmcharts.yaml new file mode 100644 index 00000000..bae4bde9 --- /dev/null +++ b/crds/helm-project-operator/projecthelmcharts.yaml @@ -0,0 +1,104 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: projecthelmcharts.helm.cattle.io +spec: + group: helm.cattle.io + names: + kind: ProjectHelmChart + plural: projecthelmcharts + singular: projecthelmchart + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .status.systemNamespace + name: System Namespace + type: string + - jsonPath: .status.releaseNamespace + name: Release Namespace + type: string + - jsonPath: .status.releaseName + name: Release Name + type: string + - jsonPath: .status.targetNamespaces + name: Target Namespaces + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + helmApiVersion: + nullable: true + type: string + projectNamespaceSelector: + nullable: true + properties: + matchExpressions: + items: + properties: + key: + nullable: true + type: string + operator: + nullable: true + type: string + values: + items: + nullable: true + type: string + nullable: true + type: array + type: object + nullable: true + type: array + matchLabels: + additionalProperties: + nullable: true + type: string + nullable: true + type: object + type: object + values: + nullable: true + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + status: + properties: + dashboardValues: + nullable: true + type: object + x-kubernetes-preserve-unknown-fields: true + releaseName: + nullable: true + type: string + releaseNamespace: + nullable: true + type: string + status: + nullable: true + type: string + statusMessage: + nullable: true + type: string + systemNamespace: + nullable: true + type: string + targetNamespaces: + items: + nullable: true + type: string + nullable: true + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} From c2b455d7549e00ead0763fdea28f1de11ef29cd1 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 14:46:24 -0400 Subject: [PATCH 29/45] run go generate --- .../generated/controllers/helm.cattle.io/interface.go | 2 +- .../generated/controllers/helm.cattle.io/v1alpha1/interface.go | 2 +- .../controllers/helm.cattle.io/v1alpha1/projecthelmchart.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/interface.go b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/interface.go index 7b907e1b..9997c99d 100644 --- a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/interface.go +++ b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/interface.go @@ -19,8 +19,8 @@ limitations under the License. package helm import ( - v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/controller" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1" ) type Interface interface { diff --git a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/interface.go b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/interface.go index f3ef9bd1..d18e61f9 100644 --- a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/interface.go +++ b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/interface.go @@ -19,8 +19,8 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/controller" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "github.com/rancher/wrangler/pkg/schemes" "k8s.io/apimachinery/pkg/runtime/schema" ) diff --git a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/projecthelmchart.go b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/projecthelmchart.go index 9648a73c..4be582b4 100644 --- a/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/projecthelmchart.go +++ b/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1/projecthelmchart.go @@ -22,9 +22,9 @@ import ( "context" "time" - v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "github.com/rancher/lasso/pkg/client" "github.com/rancher/lasso/pkg/controller" + v1alpha1 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "github.com/rancher/wrangler/pkg/apply" "github.com/rancher/wrangler/pkg/condition" "github.com/rancher/wrangler/pkg/generic" From 0a14b2c948cad95e4a1fdfcea858363eabc7d951 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 14:55:26 -0400 Subject: [PATCH 30/45] Add in HPO ci actions --- .github/workflows/hpo-ci.yaml | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/hpo-ci.yaml diff --git a/.github/workflows/hpo-ci.yaml b/.github/workflows/hpo-ci.yaml new file mode 100644 index 00000000..7d0136a6 --- /dev/null +++ b/.github/workflows/hpo-ci.yaml @@ -0,0 +1,66 @@ +name: "[helm-project-operator] ci" + +env: + CGO_ENABLED: 0 + YQ_VERSION: v4.44.3 + +on: + pull_request: + workflow_call: + inputs: + skip_build: + default: 'false' + required: false + type: string + +permissions: + contents: read + +jobs: + build: + strategy: + matrix: + arch: + - x64 + - arm64 + runs-on : runs-on,image=ubuntu22-full-${{ matrix.arch }},runner=4cpu-linux-${{ matrix.arch }},run-id=${{ github.run_id }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name : setup Go + uses : actions/setup-go@v5 + with: + go-version: 1.22 + - name: Install mikefarah/yq + run: | + sudo wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }} -O /usr/bin/yq && sudo chmod +x /usr/bin/yq; + - uses: azure/setup-helm@v3 + - name: Perform CI + run : BUILD_TARGET=helm-project-operator make ci + build-images: + if: ${{ inputs.skip_build != 'true' }} + strategy: + matrix: + arch: + - x64 + - arm64 + runs-on : runs-on,image=ubuntu22-full-${{ matrix.arch }},runner=4cpu-linux-${{ matrix.arch }},run-id=${{ github.run_id }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name : Export image version + run : | + source ./scripts/version + echo IMAGE=$IMAGE >> $GITHUB_ENV + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build Helm Project Operator image + uses: docker/build-push-action@v5 + with: + context: . + file: ./package/Dockerfile-helm-project-operator + push: false + tags: ${{ env.IMAGE }} + platforms: linux/amd64 \ No newline at end of file From 310f05c3152ed51d85938565659dd4811bfe8cb7 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 14:59:03 -0400 Subject: [PATCH 31/45] ensure Golang is setup --- .github/workflows/hpo-ci.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/hpo-ci.yaml b/.github/workflows/hpo-ci.yaml index 7d0136a6..16d13d6c 100644 --- a/.github/workflows/hpo-ci.yaml +++ b/.github/workflows/hpo-ci.yaml @@ -50,6 +50,10 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + - name : Set up Go + uses : actions/setup-go@v5 + with: + go-version: 1.22 - name : Export image version run : | source ./scripts/version From f5445966e39f2f5c16748dd2bbb8be074603f7db Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 15:10:05 -0400 Subject: [PATCH 32/45] limit validate-chart to primary prom-fed package --- scripts/validate-chart | 4 ++-- scripts/validate-packages | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/validate-chart b/scripts/validate-chart index 88ceb28b..490205ee 100755 --- a/scripts/validate-chart +++ b/scripts/validate-chart @@ -1,8 +1,8 @@ #!/usr/bin/env bash set -e -# We don't do this for helm-locker -if [[ "${BUILD_TARGET}" == "helm-locker" ]]; then +# We only do this for prometheus-federator +if [[ "${BUILD_TARGET}" != "prometheus-federator" ]]; then exit fi diff --git a/scripts/validate-packages b/scripts/validate-packages index 64644be6..3d97f1a1 100755 --- a/scripts/validate-packages +++ b/scripts/validate-packages @@ -28,3 +28,5 @@ if [[ "${monitoring_commit}" != "${grafana_commit}" ]]; then echo "ERROR: Expected .commit in ${grafana_pkg_path} (found ${grafana_commit}) to match .commit in ${monitoring_pkg_path} (found ${monitoring_commit})" exit 1 fi + +echo "Completed validating packages" \ No newline at end of file From 5f663fe891b1f374641e50b3036aef0fd0142f78 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 15:15:36 -0400 Subject: [PATCH 33/45] Move project-operator-example to examples dir --- .../project-operator-example}/Chart.yaml | 12 ++++++------ .../project-operator-example}/README.md | 0 .../project-operator-example}/questions.yaml | 0 .../templates/configmaps.yaml | 0 .../templates/dashboard-roles.yaml | 0 .../templates/dashboard-values-configmap.yaml | 0 .../project-operator-example}/values.yaml | 0 7 files changed, 6 insertions(+), 6 deletions(-) rename {charts/project-operator-example/0.0.0 => examples/helm-project-operator/project-operator-example}/Chart.yaml (100%) rename {charts/project-operator-example/0.0.0 => examples/helm-project-operator/project-operator-example}/README.md (100%) rename {charts/project-operator-example/0.0.0 => examples/helm-project-operator/project-operator-example}/questions.yaml (100%) rename {charts/project-operator-example/0.0.0 => examples/helm-project-operator/project-operator-example}/templates/configmaps.yaml (100%) rename {charts/project-operator-example/0.0.0 => examples/helm-project-operator/project-operator-example}/templates/dashboard-roles.yaml (100%) mode change 100755 => 100644 rename {charts/project-operator-example/0.0.0 => examples/helm-project-operator/project-operator-example}/templates/dashboard-values-configmap.yaml (100%) rename {charts/project-operator-example/0.0.0 => examples/helm-project-operator/project-operator-example}/values.yaml (100%) diff --git a/charts/project-operator-example/0.0.0/Chart.yaml b/examples/helm-project-operator/project-operator-example/Chart.yaml similarity index 100% rename from charts/project-operator-example/0.0.0/Chart.yaml rename to examples/helm-project-operator/project-operator-example/Chart.yaml index cb2d7715..43228d16 100644 --- a/charts/project-operator-example/0.0.0/Chart.yaml +++ b/examples/helm-project-operator/project-operator-example/Chart.yaml @@ -1,11 +1,11 @@ -apiVersion: v2 -name: project-operator-example -description: Example Helm Project Operator chart -version: 0.0.0 -appVersion: 0.0.0 annotations: catalog.cattle.io/certified: rancher catalog.cattle.io/hidden: "true" - catalog.cattle.io/release-name: project-operator-example catalog.cattle.io/os: linux,windows catalog.cattle.io/permits-os: linux,windows + catalog.cattle.io/release-name: project-operator-example +apiVersion: v2 +appVersion: 0.0.0 +description: Example Helm Project Operator chart +name: project-operator-example +version: 0.0.0 diff --git a/charts/project-operator-example/0.0.0/README.md b/examples/helm-project-operator/project-operator-example/README.md similarity index 100% rename from charts/project-operator-example/0.0.0/README.md rename to examples/helm-project-operator/project-operator-example/README.md diff --git a/charts/project-operator-example/0.0.0/questions.yaml b/examples/helm-project-operator/project-operator-example/questions.yaml similarity index 100% rename from charts/project-operator-example/0.0.0/questions.yaml rename to examples/helm-project-operator/project-operator-example/questions.yaml diff --git a/charts/project-operator-example/0.0.0/templates/configmaps.yaml b/examples/helm-project-operator/project-operator-example/templates/configmaps.yaml similarity index 100% rename from charts/project-operator-example/0.0.0/templates/configmaps.yaml rename to examples/helm-project-operator/project-operator-example/templates/configmaps.yaml diff --git a/charts/project-operator-example/0.0.0/templates/dashboard-roles.yaml b/examples/helm-project-operator/project-operator-example/templates/dashboard-roles.yaml old mode 100755 new mode 100644 similarity index 100% rename from charts/project-operator-example/0.0.0/templates/dashboard-roles.yaml rename to examples/helm-project-operator/project-operator-example/templates/dashboard-roles.yaml diff --git a/charts/project-operator-example/0.0.0/templates/dashboard-values-configmap.yaml b/examples/helm-project-operator/project-operator-example/templates/dashboard-values-configmap.yaml similarity index 100% rename from charts/project-operator-example/0.0.0/templates/dashboard-values-configmap.yaml rename to examples/helm-project-operator/project-operator-example/templates/dashboard-values-configmap.yaml diff --git a/charts/project-operator-example/0.0.0/values.yaml b/examples/helm-project-operator/project-operator-example/values.yaml similarity index 100% rename from charts/project-operator-example/0.0.0/values.yaml rename to examples/helm-project-operator/project-operator-example/values.yaml From 6c225b26d23c209e4311cf6e99ada3ec5eb06186 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 15:20:06 -0400 Subject: [PATCH 34/45] Adjust chart embedding script --- scripts/build-chart | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/scripts/build-chart b/scripts/build-chart index 71f0ec0f..0901d818 100755 --- a/scripts/build-chart +++ b/scripts/build-chart @@ -18,13 +18,23 @@ elif [ "$BUILD_TARGET" == "helm-project-operator" ]; then fi CHART=${CHART:-${DEFAULT_CHART_TARGET}} -CHART_VERSION=${EMBEDDED_CHART_VERSION:-$(find ./charts/${CHART} -maxdepth 1 -mindepth 1 -type d | tr - \~ | sort -rV | tr \~ - | head -n1 | cut -d'/' -f4)} -# Prepare and package charts -package-charts "charts/${CHART}/${CHART_VERSION}" ./build/charts +if [ "$BUILD_TARGET" == "prometheus-federator" ]; then + CHART_VERSION=${EMBEDDED_CHART_VERSION:-$(find ./charts/${CHART} -maxdepth 1 -mindepth 1 -type d | tr - \~ | sort -rV | tr \~ - | head -n1 | cut -d'/' -f4)} + + # Prepare and package charts + package-charts "charts/${CHART}/${CHART_VERSION}" ./build/charts -# Prepare chart for embedding location -base64 -i "build/charts/${CHART}-${CHART_VERSION}.tgz" > "cmd/${BUILD_TARGET}/fs/${CHART}.tgz.base64" -rm "build/charts/${CHART}-${CHART_VERSION}.tgz" + # Prepare chart for embedding location + base64 -i "build/charts/${CHART}-${CHART_VERSION}.tgz" > "cmd/${BUILD_TARGET}/fs/${CHART}.tgz.base64" + rm "build/charts/${CHART}-${CHART_VERSION}.tgz" +elif [ "$BUILD_TARGET" == "helm-project-operator" ]; then + # Update the chart placeholders + edit-charts "examples/helm-project-operator/${CHART}/Chart.yaml" "${HELM_CHART_VERSION}" "${HELM_IMAGE_TAG}" + package-charts "./examples/helm-project-operator/${CHART}" ./build/charts + + # Prepare chart for embedding location + base64 -i "./build/charts/${CHART}-${HELM_CHART_VERSION}.tgz" > "cmd/${BUILD_TARGET}/fs/${CHART}.tgz.base64" +fi echo "Completed ${CHART} (${CHART_VERSION}) build process." \ No newline at end of file From 23175b4ca51fdbc9c2aedf96292cf43fdf109b02 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 15:21:19 -0400 Subject: [PATCH 35/45] fix missing util funcs --- scripts/build-chart | 10 ++++++++-- scripts/util-chart | 18 +++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/scripts/build-chart b/scripts/build-chart index 0901d818..2824a0a9 100755 --- a/scripts/build-chart +++ b/scripts/build-chart @@ -29,9 +29,15 @@ if [ "$BUILD_TARGET" == "prometheus-federator" ]; then base64 -i "build/charts/${CHART}-${CHART_VERSION}.tgz" > "cmd/${BUILD_TARGET}/fs/${CHART}.tgz.base64" rm "build/charts/${CHART}-${CHART_VERSION}.tgz" elif [ "$BUILD_TARGET" == "helm-project-operator" ]; then + # Prepare base chart for build + rm -rf build/charts + mkdir -p build/charts + cp -rf "examples/helm-project-operator/${CHART}" build/charts/ + + # Update the chart placeholders - edit-charts "examples/helm-project-operator/${CHART}/Chart.yaml" "${HELM_CHART_VERSION}" "${HELM_IMAGE_TAG}" - package-charts "./examples/helm-project-operator/${CHART}" ./build/charts + edit-charts "build/charts/${CHART}/Chart.yaml" "${HELM_CHART_VERSION}" "${HELM_IMAGE_TAG}" + package-charts "./build/charts/${CHART}" ./build/charts # Prepare chart for embedding location base64 -i "./build/charts/${CHART}-${HELM_CHART_VERSION}.tgz" > "cmd/${BUILD_TARGET}/fs/${CHART}.tgz.base64" diff --git a/scripts/util-chart b/scripts/util-chart index 39de4bc5..1de00285 100644 --- a/scripts/util-chart +++ b/scripts/util-chart @@ -1,7 +1,23 @@ #!/usr/bin/env bash +function is_gnu_sed(){ + sed --version >/dev/null 2>&1 +} + +function sed_i_wrapper(){ + if is_gnu_sed; then + $(which sed) "$@" + else + a=() + for b in "$@"; do + [[ $b == '-i' ]] && a=("${a[@]}" "$b" "") || a=("${a[@]}" "$b") + done + $(which sed) "${a[@]}" + fi +} + function edit-charts() { - sed -i \ + sed_i_wrapper -i \ -e 's/^version:.*/version: '${2}'/' \ -e 's/^appVersion:.*/appVersion: '${3}'/' \ "${1}" From f093ab1edf3d32faeb09ca332f448655a0c9cba9 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 15:38:31 -0400 Subject: [PATCH 36/45] import the helm-locker example chart --- .../helm-locker/helm-locker-example/Chart.yaml | 17 +++++++++++++++++ .../templates/configmap.yaml | 17 +++++++++++++++++ .../helm-locker-example/templates/example.yaml | 10 ++++++++++ .../templates/validate-install-crd.yaml | 14 ++++++++++++++ .../helm-locker/helm-locker-example/values.yaml | 2 ++ 5 files changed, 60 insertions(+) create mode 100644 examples/helm-locker/helm-locker-example/Chart.yaml create mode 100644 examples/helm-locker/helm-locker-example/templates/configmap.yaml create mode 100644 examples/helm-locker/helm-locker-example/templates/example.yaml create mode 100644 examples/helm-locker/helm-locker-example/templates/validate-install-crd.yaml create mode 100644 examples/helm-locker/helm-locker-example/values.yaml diff --git a/examples/helm-locker/helm-locker-example/Chart.yaml b/examples/helm-locker/helm-locker-example/Chart.yaml new file mode 100644 index 00000000..d5760a66 --- /dev/null +++ b/examples/helm-locker/helm-locker-example/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +name: helm-locker-example +description: Helm Locker Example Chart +version: 0.0.1 +appVersion: 0.0.1 +annotations: + catalog.cattle.io/certified: rancher + catalog.cattle.io/display-name: Helm Locker Example Chart + catalog.cattle.io/kube-version: '>=1.16.0-0' + catalog.cattle.io/namespace: cattle-helm-system + catalog.cattle.io/permits-os: linux,windows + catalog.cattle.io/rancher-version: '>= 2.6.0-0' + catalog.cattle.io/release-name: helm-locker-example + catalog.cattle.io/os: linux,windows +maintainers: + - email: dan.pock@suse.com + name: Dan Pock diff --git a/examples/helm-locker/helm-locker-example/templates/configmap.yaml b/examples/helm-locker/helm-locker-example/templates/configmap.yaml new file mode 100644 index 00000000..dfb18263 --- /dev/null +++ b/examples/helm-locker/helm-locker-example/templates/configmap.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config-map + namespace: {{ .Release.Namespace }} +data: + config: |- +{{ .Values.data | toYaml | indent 4 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config-map-2 + namespace: {{ .Release.Namespace }} +data: + config: |- +{{ .Values.data | toYaml | indent 4 }} \ No newline at end of file diff --git a/examples/helm-locker/helm-locker-example/templates/example.yaml b/examples/helm-locker/helm-locker-example/templates/example.yaml new file mode 100644 index 00000000..3e7e4758 --- /dev/null +++ b/examples/helm-locker/helm-locker-example/templates/example.yaml @@ -0,0 +1,10 @@ +apiVersion: helm.cattle.io/v1alpha1 +kind: HelmRelease +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} +spec: + release: + # Chart is configured to lock itself + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} \ No newline at end of file diff --git a/examples/helm-locker/helm-locker-example/templates/validate-install-crd.yaml b/examples/helm-locker/helm-locker-example/templates/validate-install-crd.yaml new file mode 100644 index 00000000..22d86cc4 --- /dev/null +++ b/examples/helm-locker/helm-locker-example/templates/validate-install-crd.yaml @@ -0,0 +1,14 @@ +#{{- if gt (len (lookup "rbac.authorization.k8s.io/v1" "ClusterRole" "" "")) 0 -}} +# {{- $found := dict -}} +# {{- set $found "helm.cattle.io/v1alpha1/HelmRelease" false -}} +# {{- range .Capabilities.APIVersions -}} +# {{- if hasKey $found (toString .) -}} +# {{- set $found (toString .) true -}} +# {{- end -}} +# {{- end -}} +# {{- range $_, $exists := $found -}} +# {{- if (eq $exists false) -}} +# {{- required "Required CRDs are missing. Please install the corresponding CRD chart before installing this chart." "" -}} +# {{- end -}} +# {{- end -}} +#{{- end -}} \ No newline at end of file diff --git a/examples/helm-locker/helm-locker-example/values.yaml b/examples/helm-locker/helm-locker-example/values.yaml new file mode 100644 index 00000000..ee9d3ba9 --- /dev/null +++ b/examples/helm-locker/helm-locker-example/values.yaml @@ -0,0 +1,2 @@ +data: + hello: rancher \ No newline at end of file From de3a9d344bfef1aedcf7010f34851ba096bc341c Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 15:41:41 -0400 Subject: [PATCH 37/45] merge gitignore files --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 06395193..fb5eb80e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +/.dapper /.cache +/.vscode /bin /build !/cmd/prometheus-federator/fs/.gitkeep @@ -8,8 +10,10 @@ /dist *.swp .idea +/helm-project-operator /prometheus-federator /.charts-build-scripts *.DS_Store dev/ -kubeconfig.yaml \ No newline at end of file +kubeconfig.yaml +get_helm.sh \ No newline at end of file From 27af1a98d553c03b9ea3d08e58d24731f9bb6590 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 15:46:37 -0400 Subject: [PATCH 38/45] Add dapper back for local-only use --- .dockerignore | 1 + Dockerfile.dapper | 41 +++++++++++++++++++++++++++++++++++++++++ Makefile | 20 +++++++++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.dapper diff --git a/.dockerignore b/.dockerignore index bec10c10..0f2ec83b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ ./.dapper ./.cache ./dist +./build \ No newline at end of file diff --git a/Dockerfile.dapper b/Dockerfile.dapper new file mode 100644 index 00000000..bf0a36b0 --- /dev/null +++ b/Dockerfile.dapper @@ -0,0 +1,41 @@ +FROM registry.suse.com/bci/golang:1.22 AS helm +RUN zypper -n install git +RUN git -C / clone --branch release-v3.9.0 --depth=1 https://github.com/rancher/helm +RUN make -C /helm + +FROM registry.suse.com/bci/golang:1.22 +# Add buildx plugin +COPY --from=docker.io/docker/buildx-bin:0.17.1 /buildx /usr/libexec/docker/cli-plugins/docker-buildx + +ARG DAPPER_HOST_ARCH +ENV ARCH $DAPPER_HOST_ARCH + +RUN zypper -n install git docker vim less file curl wget +RUN go install golang.org/x/tools/cmd/goimports@latest +RUN if [ "${ARCH}" == "amd64" ]; then \ + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.51.2; \ + fi + +ENV YQ_VERSION=v4.30.8 +RUN wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${ARCH} -O /usr/bin/yq && chmod +x /usr/bin/yq; + +COPY --from=helm ./helm/bin/helm /usr/local/bin/ +RUN if [ "${ARCH}" != "s390x" ]; then \ + helm plugin install https://github.com/quintush/helm-unittest; \ + fi + +ENV DAPPER_ENV REPO TAG HELM_IMAGE_TAG HELM_CHART_VERSION HELM_CHART_VERSION_DEV CROSS CROSS_ARCH USE_DOCKER_BUILDX TARGET_ARCH +ENV DAPPER_SOURCE /go/src/github.com/rancher/helm-project-operator/ +ENV DAPPER_OUTPUT ./bin ./dist +ENV GOCACHE /root/.cache/go-build +ENV GOPATH /go +ENV DAPPER_DOCKER_SOCKET true +ENV DAPPER_RUN_ARGS --privileged +VOLUME /var/lib/rancher/k3s +VOLUME /var/lib/cni +VOLUME /var/log +ENV HOME ${DAPPER_SOURCE} +WORKDIR ${DAPPER_SOURCE} + +ENTRYPOINT ["./scripts/entry"] +CMD ["ci"] diff --git a/Makefile b/Makefile index 32446662..54cc0faf 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,21 @@ TARGETS := $(shell ls scripts|grep -ve "^util-\|entry\|^pull-scripts") +LOCAL_TARGETS := $(addprefix local-,$(TARGETS)) +.dapper: + @echo Downloading dapper + @curl -sL https://releases.rancher.com/dapper/latest/dapper-$$(uname -s)-$$(uname -m) > ./bin/.dapper.tmp + @@chmod +x ./bin/.dapper.tmp + @./bin/.dapper.tmp -v + @mv ./bin/.dapper.tmp ./bin/.dapper + +# Default behavior for targets without dapper $(TARGETS): ./scripts/$@ +# Behavior for targets prefixed with "local-" using dapper +$(LOCAL_TARGETS): local-%: .dapper + ./bin/.dapper $(@:local-%=%) + .DEFAULT_GOAL := default # Charts Build Scripts @@ -17,4 +30,9 @@ CHARTS_BUILD_SCRIPTS_TARGETS := prepare patch clean clean-cache charts list inde $(CHARTS_BUILD_SCRIPTS_TARGETS): pull-scripts @./bin/charts-build-scripts $@ -.PHONY: $(TARGETS) $(CHARTS_BUILD_SCRIPTS_TARGETS) \ No newline at end of file +.PHONY: $(TARGETS) $(CHARTS_BUILD_SCRIPTS_TARGETS) $(LOCAL_TARGETS) list + +list-make: + @LC_ALL=C $(MAKE) -pRrq -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$$' +# IMPORTANT: The line above must be indented by (at least one) +# *actual TAB character* - *spaces* do *not* work. From 0605b14cc5ce193b144145cef404f01a31230aee Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 15:58:35 -0400 Subject: [PATCH 39/45] Add hpo e2e workflow --- .../scripts/hpo/create-project-namespace.sh | 21 ++++ .../scripts/hpo/create-projecthelmchart.sh | 31 +++++ .../scripts/hpo/delete-projecthelmchart.sh | 17 +++ .../hpo/install-helm-project-operator.sh | 69 +++++++++++ .../hpo/uninstall-helm-project-operator.sh | 10 ++ .../hpo/validate-helm-project-operator.sh | 14 +++ .github/workflows/hpo-e2e-ci.yaml | 107 ++++++++++++++++++ 7 files changed, 269 insertions(+) create mode 100755 .github/workflows/e2e/scripts/hpo/create-project-namespace.sh create mode 100755 .github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh create mode 100755 .github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh create mode 100755 .github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh create mode 100755 .github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh create mode 100755 .github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh create mode 100644 .github/workflows/hpo-e2e-ci.yaml diff --git a/.github/workflows/e2e/scripts/hpo/create-project-namespace.sh b/.github/workflows/e2e/scripts/hpo/create-project-namespace.sh new file mode 100755 index 00000000..69084c64 --- /dev/null +++ b/.github/workflows/e2e/scripts/hpo/create-project-namespace.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +source $(dirname $0)/../entry + +cd $(dirname $0)/../../../../.. + +USE_RANCHER=${USE_RANCHER:-"false"} +if [ "$USE_RANCHER" = "true" ]; then + kubectl apply -f ./examples/helm-project-operator/ci/project.yaml +fi + +kubectl apply -f ./examples/helm-project-operator/ci/namespace.yaml + +sleep "${DEFAULT_SLEEP_TIMEOUT_SECONDS}" +if ! kubectl get namespace cattle-project-p-example; then + echo "ERROR: Expected cattle-project-p-example namespace to exist after ${DEFAULT_SLEEP_TIMEOUT_SECONDS} seconds, not found" + exit 1 +fi + +echo "PASS: Project Registration Namespace was created" diff --git a/.github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh b/.github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh new file mode 100755 index 00000000..f9dcdc10 --- /dev/null +++ b/.github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +source $(dirname $0)/../entry + +cd $(dirname $0)/../../../../.. + +kubectl apply -f ./examples/helm-project-operator/ci/project-helm-chart.yaml +sleep ${DEFAULT_SLEEP_TIMEOUT_SECONDS}; + +if ! kubectl get -n cattle-helm-system job/helm-install-project-operator-example-chart-dummy; then + echo "ERROR: Helm Install Job for Example Chart was never created after ${KUBECTL_WAIT_TIMEOUT} seconds" + echo "PROJECT HELM CHARTS:" + kubectl get projecthelmchart -n cattle-project-p-example -o yaml + echo "HELM CHARTS:" + kubectl get helmcharts -n cattle-helm-system -o yaml + echo "HELM RELEASES:" + kubectl get helmreleases -n cattle-helm-system -o yaml + echo "HELM PROJECT OPERATOR:" + kubectl logs deployment/helm-project-operator -n cattle-helm-system + exit 1 +fi + +if ! kubectl wait --for=condition=complete --timeout="${KUBECTL_WAIT_TIMEOUT}" -n cattle-helm-system job/helm-install-project-operator-example-chart-dummy; then + echo "ERROR: Helm Install Job for Example Chart never completed after ${KUBECTL_WAIT_TIMEOUT} seconds" + kubectl logs job/helm-install-project-operator-example-chart-dummy -n cattle-helm-system + exit 1 +fi +kubectl logs job/helm-install-project-operator-example-chart-dummy -n cattle-helm-system + +echo "PASS: Adding ProjectHelmChart successfully installed Example Chart" diff --git a/.github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh b/.github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh new file mode 100755 index 00000000..24cb6f14 --- /dev/null +++ b/.github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +source $(dirname $0)/../entry + +cd $(dirname $0)/../../../../.. + +kubectl delete -f ./examples/helm-project-operator/ci/project-helm-chart.yaml +if kubectl get -n cattle-helm-system job/helm-delete-project-operator-example-chart-dummy --ignore-not-found; then + if ! kubectl wait --for=condition=complete --timeout="${KUBECTL_WAIT_TIMEOUT}" -n cattle-helm-system job/helm-delete-project-operator-example-chart-dummy; then + echo "ERROR: Helm Uninstall Job for Example Chart never completed after ${KUBECTL_WAIT_TIMEOUT}" + kubectl logs job/helm-delete-project-operator-example-chart-dummy -n cattle-helm-system + exit 1 + fi +fi + +echo "PASS: Removing ProjectHelmChart successfully uninstalled Example Chart" \ No newline at end of file diff --git a/.github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh b/.github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh new file mode 100755 index 00000000..395de544 --- /dev/null +++ b/.github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh @@ -0,0 +1,69 @@ +#!/bin/bash +set -e + +source $(dirname $0)/../entry + +cd $(dirname $0)/../../../../.. + +latest_chart=./charts/helm-project-operator + +case "${KUBERNETES_DISTRIBUTION_TYPE}" in +"k3s") + cluster_args="" + kubernetes_version=$(kubectl version --short | grep "Server Version" | cut -d ' ' -f3) + case "${kubernetes_version}" in + v1.23.*) + embedded_helm_controller_fixed_version="v1.23.14" + if [[ $(echo ${kubernetes_version} ${embedded_helm_controller_fixed_version} | tr " " "\n" | sort -rV | head -n 1 ) == "${embedded_helm_controller_fixed_version}" ]]; then + cluster_args="--set helmController.enabled=false" + fi + ;; + v1.24.*) + embedded_helm_controller_fixed_version="v1.24.8" + if [[ $(echo ${kubernetes_version} ${embedded_helm_controller_fixed_version} | tr " " "\n" | sort -rV | head -n 1 ) == "${embedded_helm_controller_fixed_version}" ]]; then + cluster_args="--set helmController.enabled=false" + fi + ;; + *) + embedded_helm_controller_fixed_version="v1.25.4" + if [[ $(echo ${kubernetes_version} ${embedded_helm_controller_fixed_version} | tr " " "\n" | sort -rV | head -n 1 ) == "${embedded_helm_controller_fixed_version}" ]]; then + cluster_args="--set helmController.enabled=false" + fi + ;; + esac + ;; +"rke") + cluster_args="" + ;; +"rke2") + cluster_args="" + kubernetes_version=$(kubectl version --short | grep "Server Version" | cut -d ' ' -f3) + case "${kubernetes_version}" in + v1.23.*) + embedded_helm_controller_fixed_version="v1.23.14" + if [[ $(echo ${kubernetes_version} ${embedded_helm_controller_fixed_version} | tr " " "\n" | sort -rV | head -n 1 ) == "${embedded_helm_controller_fixed_version}" ]]; then + cluster_args="--set helmController.enabled=false" + fi + ;; + v1.24.*) + embedded_helm_controller_fixed_version="v1.24.8" + if [[ $(echo ${kubernetes_version} ${embedded_helm_controller_fixed_version} | tr " " "\n" | sort -rV | head -n 1 ) == "${embedded_helm_controller_fixed_version}" ]]; then + cluster_args="--set helmController.enabled=false" + fi + ;; + *) + embedded_helm_controller_fixed_version="v1.25.4" + if [[ $(echo ${kubernetes_version} ${embedded_helm_controller_fixed_version} | tr " " "\n" | sort -rV | head -n 1 ) == "${embedded_helm_controller_fixed_version}" ]]; then + cluster_args="--set helmController.enabled=false" + fi + ;; + esac + ;; +*) + echo "KUBERNETES_DISTRIBUTION_TYPE=${KUBERNETES_DISTRIBUTION_TYPE} is unknown" + exit 1 +esac + +helm upgrade --install --create-namespace -n cattle-helm-system helm-project-operator --set image.repository=${REPO:-rancher}/helm-project-operator --set image.tag=${TAG:-dev} ${cluster_args} ${RANCHER_HELM_ARGS} ${latest_chart} + +echo "PASS: Helm Project Operator has been installed" diff --git a/.github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh b/.github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh new file mode 100755 index 00000000..68346e91 --- /dev/null +++ b/.github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +source $(dirname $0)/../entry + +cd $(dirname $0)/../../../../.. + +helm uninstall --wait -n cattle-helm-system helm-project-operator + +echo "PASS: Helm Project Operator has been uninstalled" diff --git a/.github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh b/.github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh new file mode 100755 index 00000000..d9fc3eba --- /dev/null +++ b/.github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e + +source $(dirname $0)/../entry + +cd $(dirname $0)/../../../../.. + +if ! kubectl -n cattle-helm-system rollout status deployment helm-project-operator --timeout="${KUBECTL_WAIT_TIMEOUT}"; then + echo "ERROR: Helm Project Operator did not roll out" + kubectl get pods -n cattle-helm-system -o yaml + exit 1 +fi + +echo "PASS: Helm Project Operator is up and running" diff --git a/.github/workflows/hpo-e2e-ci.yaml b/.github/workflows/hpo-e2e-ci.yaml new file mode 100644 index 00000000..11ac63d1 --- /dev/null +++ b/.github/workflows/hpo-e2e-ci.yaml @@ -0,0 +1,107 @@ +name: "[helm-project-operator] E2E Helm Project Operator" + +on: + workflow_dispatch: + inputs: + enable_tmate: + description: 'Enable debugging via tmate' + required: false + default: "false" + debug: + description: "Enable debug logs" + required: false + default: "false" + k3s_version: + description: "Version of k3s to use for the underlying cluster, should exist in https://hub.docker.com/r/rancher/k3s/tags" + required: false + pull_request: + paths-ignore: + - 'docs/**' + - 'scripts/**' + - '*.md' + - '*.dapper' + - '.gitignore' + - 'CODEOWNERS' + - 'LICENSE' + - 'Makefile' + +env: + CLUSTER_NAME: e2e-ci-helm-project-operator + CGO_ENABLED: 0 + YQ_VERSION: v4.44.3 + E2E_CI: true + REPO: rancher + TAG: v0.0.0-dev.1 + APISERVER_PORT: 8001 + DEFAULT_SLEEP_TIMEOUT_SECONDS: 10 + KUBECTL_WAIT_TIMEOUT: 120s + DEBUG: ${{ github.event.inputs.debug || false }} + +permissions: + contents: write + +jobs: + e2e-helm-project-operator: + strategy: + matrix: + arch: + - x64 + #- arm64 + k3s_version: + # k3d version list k3s | sed 's/+/-/' | sort -h + - ${{ github.event.inputs.k3s_version || 'v1.20.15-k3s1' }} + runs-on : runs-on,image=ubuntu22-full-${{ matrix.arch }},runner=4cpu-linux-${{ matrix.arch }},run-id=${{ github.run_id }} + env: + K3S_VERSION: ${{ matrix.k3s_version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name : setup Go + uses : actions/setup-go@v5 + with: + go-version: 1.22 + - name: Install mikefarah/yq + run: | + sudo wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${{ matrix.arch == 'x64' && 'amd64' || matrix.arch }} -O /usr/bin/yq && sudo chmod +x /usr/bin/yq; + - name: Perform pre-e2e image build + run: | + BUILD_TARGET=helm-project-operator REPO=${REPO} TAG=${TAG} ./scripts/build; + BUILD_TARGET=helm-project-operator REPO=${REPO} TAG=${TAG} ./scripts/package; + - name : Install k3d + run : ./.github/workflows/e2e/scripts/install-k3d.sh + - name : Setup k3d cluster + run : K3S_VERSION=${{ env.K3S_VERSION }} ./.github/workflows/e2e/scripts/setup-cluster.sh + - name: Import Images Into k3d + run: | + k3d image import ${REPO}/helm-project-operator:${TAG} -c "$CLUSTER_NAME"; + - name: Setup kubectl context + run: | + kubectl config use-context "k3d-$CLUSTER_NAME"; + - name: Set Up Tmate Debug Session + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.enable_tmate == 'true' }} + uses: mxschmitt/action-tmate@v3 + timeout-minutes: 15 + with: + limit-access-to-actor: true + - name: Install Helm Project Operator + run: ./.github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh; + + - name: Check if Helm Project Operator is up + run: ./.github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh; + + - name: Check if Project Registration Namespace is auto-created on namespace detection + run: ./.github/workflows/e2e/scripts/hpo/create-project-namespace.sh; + + - name: Deploy Example Chart via ProjectHelmChart CR + run: ./.github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh; + + - name: Delete Example Chart + run: ./.github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh; + + - name: Uninstall Helm Project Operator + run: ./.github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh; + + - name: Delete k3d cluster + if: always() + run: k3d cluster delete "$CLUSTER_NAME"; From b740451fe00e761e6dbe9df27217ae0298551c21 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 16:04:10 -0400 Subject: [PATCH 40/45] correct HPO e2e paths --- ...-namespace.sh => hpo-create-project-namespace.sh} | 4 ++-- ...cthelmchart.sh => hpo-create-projecthelmchart.sh} | 4 ++-- ...cthelmchart.sh => hpo-delete-projecthelmchart.sh} | 4 ++-- ...rator.sh => hpo-install-helm-project-operator.sh} | 6 +++--- ...tor.sh => hpo-uninstall-helm-project-operator.sh} | 4 ++-- ...ator.sh => hpo-validate-helm-project-operator.sh} | 4 ++-- .github/workflows/hpo-e2e-ci.yaml | 12 ++++++------ .github/workflows/prom-fed-e2e-ci.yaml | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) rename .github/workflows/e2e/scripts/{hpo/create-project-namespace.sh => hpo-create-project-namespace.sh} (89%) rename .github/workflows/e2e/scripts/{hpo/create-projecthelmchart.sh => hpo-create-projecthelmchart.sh} (95%) rename .github/workflows/e2e/scripts/{hpo/delete-projecthelmchart.sh => hpo-delete-projecthelmchart.sh} (91%) rename .github/workflows/e2e/scripts/{hpo/install-helm-project-operator.sh => hpo-install-helm-project-operator.sh} (96%) rename .github/workflows/e2e/scripts/{hpo/uninstall-helm-project-operator.sh => hpo-uninstall-helm-project-operator.sh} (70%) rename .github/workflows/e2e/scripts/{hpo/validate-helm-project-operator.sh => hpo-validate-helm-project-operator.sh} (83%) diff --git a/.github/workflows/e2e/scripts/hpo/create-project-namespace.sh b/.github/workflows/e2e/scripts/hpo-create-project-namespace.sh similarity index 89% rename from .github/workflows/e2e/scripts/hpo/create-project-namespace.sh rename to .github/workflows/e2e/scripts/hpo-create-project-namespace.sh index 69084c64..3a4bef32 100755 --- a/.github/workflows/e2e/scripts/hpo/create-project-namespace.sh +++ b/.github/workflows/e2e/scripts/hpo-create-project-namespace.sh @@ -1,9 +1,9 @@ #!/bin/bash set -e -source $(dirname $0)/../entry +source $(dirname $0)/entry -cd $(dirname $0)/../../../../.. +cd $(dirname $0)/../../../.. USE_RANCHER=${USE_RANCHER:-"false"} if [ "$USE_RANCHER" = "true" ]; then diff --git a/.github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh b/.github/workflows/e2e/scripts/hpo-create-projecthelmchart.sh similarity index 95% rename from .github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh rename to .github/workflows/e2e/scripts/hpo-create-projecthelmchart.sh index f9dcdc10..af1fbbe1 100755 --- a/.github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh +++ b/.github/workflows/e2e/scripts/hpo-create-projecthelmchart.sh @@ -1,9 +1,9 @@ #!/bin/bash set -e -source $(dirname $0)/../entry +source $(dirname $0)/entry -cd $(dirname $0)/../../../../.. +cd $(dirname $0)/../../../.. kubectl apply -f ./examples/helm-project-operator/ci/project-helm-chart.yaml sleep ${DEFAULT_SLEEP_TIMEOUT_SECONDS}; diff --git a/.github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh b/.github/workflows/e2e/scripts/hpo-delete-projecthelmchart.sh similarity index 91% rename from .github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh rename to .github/workflows/e2e/scripts/hpo-delete-projecthelmchart.sh index 24cb6f14..bfbb631d 100755 --- a/.github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh +++ b/.github/workflows/e2e/scripts/hpo-delete-projecthelmchart.sh @@ -1,9 +1,9 @@ #!/bin/bash set -e -source $(dirname $0)/../entry +source $(dirname $0)/entry -cd $(dirname $0)/../../../../.. +cd $(dirname $0)/../../../.. kubectl delete -f ./examples/helm-project-operator/ci/project-helm-chart.yaml if kubectl get -n cattle-helm-system job/helm-delete-project-operator-example-chart-dummy --ignore-not-found; then diff --git a/.github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh b/.github/workflows/e2e/scripts/hpo-install-helm-project-operator.sh similarity index 96% rename from .github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh rename to .github/workflows/e2e/scripts/hpo-install-helm-project-operator.sh index 395de544..030b68b4 100755 --- a/.github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh +++ b/.github/workflows/e2e/scripts/hpo-install-helm-project-operator.sh @@ -1,11 +1,11 @@ #!/bin/bash set -e -source $(dirname $0)/../entry +source $(dirname $0)/entry -cd $(dirname $0)/../../../../.. +cd $(dirname $0)/../../../.. -latest_chart=./charts/helm-project-operator +latest_chart=./packages/helm-project-operator/charts case "${KUBERNETES_DISTRIBUTION_TYPE}" in "k3s") diff --git a/.github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh b/.github/workflows/e2e/scripts/hpo-uninstall-helm-project-operator.sh similarity index 70% rename from .github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh rename to .github/workflows/e2e/scripts/hpo-uninstall-helm-project-operator.sh index 68346e91..ce133162 100755 --- a/.github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh +++ b/.github/workflows/e2e/scripts/hpo-uninstall-helm-project-operator.sh @@ -1,9 +1,9 @@ #!/bin/bash set -e -source $(dirname $0)/../entry +source $(dirname $0)/entry -cd $(dirname $0)/../../../../.. +cd $(dirname $0)/../../../.. helm uninstall --wait -n cattle-helm-system helm-project-operator diff --git a/.github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh b/.github/workflows/e2e/scripts/hpo-validate-helm-project-operator.sh similarity index 83% rename from .github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh rename to .github/workflows/e2e/scripts/hpo-validate-helm-project-operator.sh index d9fc3eba..4baea285 100755 --- a/.github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh +++ b/.github/workflows/e2e/scripts/hpo-validate-helm-project-operator.sh @@ -1,9 +1,9 @@ #!/bin/bash set -e -source $(dirname $0)/../entry +source $(dirname $0)/entry -cd $(dirname $0)/../../../../.. +cd $(dirname $0)/../../../.. if ! kubectl -n cattle-helm-system rollout status deployment helm-project-operator --timeout="${KUBECTL_WAIT_TIMEOUT}"; then echo "ERROR: Helm Project Operator did not roll out" diff --git a/.github/workflows/hpo-e2e-ci.yaml b/.github/workflows/hpo-e2e-ci.yaml index 11ac63d1..9c57855f 100644 --- a/.github/workflows/hpo-e2e-ci.yaml +++ b/.github/workflows/hpo-e2e-ci.yaml @@ -85,22 +85,22 @@ jobs: with: limit-access-to-actor: true - name: Install Helm Project Operator - run: ./.github/workflows/e2e/scripts/hpo/install-helm-project-operator.sh; + run: ./.github/workflows/e2e/scripts/hpo-install-helm-project-operator.sh; - name: Check if Helm Project Operator is up - run: ./.github/workflows/e2e/scripts/hpo/validate-helm-project-operator.sh; + run: ./.github/workflows/e2e/scripts/hpo-validate-helm-project-operator.sh; - name: Check if Project Registration Namespace is auto-created on namespace detection - run: ./.github/workflows/e2e/scripts/hpo/create-project-namespace.sh; + run: ./.github/workflows/e2e/scripts/hpo-create-project-namespace.sh; - name: Deploy Example Chart via ProjectHelmChart CR - run: ./.github/workflows/e2e/scripts/hpo/create-projecthelmchart.sh; + run: ./.github/workflows/e2e/scripts/hpo-create-projecthelmchart.sh; - name: Delete Example Chart - run: ./.github/workflows/e2e/scripts/hpo/delete-projecthelmchart.sh; + run: ./.github/workflows/e2e/scripts/hpo-delete-projecthelmchart.sh; - name: Uninstall Helm Project Operator - run: ./.github/workflows/e2e/scripts/hpo/uninstall-helm-project-operator.sh; + run: ./.github/workflows/e2e/scripts/hpo-uninstall-helm-project-operator.sh; - name: Delete k3d cluster if: always() diff --git a/.github/workflows/prom-fed-e2e-ci.yaml b/.github/workflows/prom-fed-e2e-ci.yaml index aefcc7f4..4e531bc4 100644 --- a/.github/workflows/prom-fed-e2e-ci.yaml +++ b/.github/workflows/prom-fed-e2e-ci.yaml @@ -102,7 +102,7 @@ jobs: - name: Perform pre-e2e image build run: | - EMBEDDED_CHART_VERSION=0.3.4 REPO=${REPO} TAG=${TAG} make build; + EMBEDED_CHART_VERSION=0.3.4 REPO=${REPO} TAG=${TAG} make build; REPO=${REPO} TAG=${TAG} make package; - name : Install k3d From dff3025c4201098eef15edbce0c2343f0ae37a3b Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 16:48:42 -0400 Subject: [PATCH 41/45] Correct bci image tag --- package/Dockerfile-helm-project-operator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/Dockerfile-helm-project-operator b/package/Dockerfile-helm-project-operator index 0a11b552..9dc393a0 100644 --- a/package/Dockerfile-helm-project-operator +++ b/package/Dockerfile-helm-project-operator @@ -34,7 +34,7 @@ COPY . . RUN make build-chart RUN make build -FROM registry.suse.com/bci/bci-micro:15.6 +FROM registry.suse.com/bci/bci-micro:latest RUN echo 'helmprojectoperator:x:1000:1000::/home/helmprojectoperator:/bin/bash' >> /etc/passwd && \ echo 'helmprojectoperator:x:1000:' >> /etc/group && \ mkdir /home/helmprojectoperator && \ From 878decdc969815edcd036b81963ce4fd7d149b85 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 16:48:50 -0400 Subject: [PATCH 42/45] Fix arch issue in CI --- .github/workflows/hpo-ci.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/hpo-ci.yaml b/.github/workflows/hpo-ci.yaml index 16d13d6c..10fceca0 100644 --- a/.github/workflows/hpo-ci.yaml +++ b/.github/workflows/hpo-ci.yaml @@ -67,4 +67,3 @@ jobs: file: ./package/Dockerfile-helm-project-operator push: false tags: ${{ env.IMAGE }} - platforms: linux/amd64 \ No newline at end of file From c86f635c8841585d9ede615bd73ee07c2f539ec0 Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Fri, 11 Oct 2024 17:00:00 -0400 Subject: [PATCH 43/45] enable arm64 for hpo --- .github/workflows/hpo-e2e-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hpo-e2e-ci.yaml b/.github/workflows/hpo-e2e-ci.yaml index 9c57855f..3e017fda 100644 --- a/.github/workflows/hpo-e2e-ci.yaml +++ b/.github/workflows/hpo-e2e-ci.yaml @@ -46,7 +46,7 @@ jobs: matrix: arch: - x64 - #- arm64 + - arm64 k3s_version: # k3d version list k3s | sed 's/+/-/' | sort -h - ${{ github.event.inputs.k3s_version || 'v1.20.15-k3s1' }} From 9bd26df51c8c9cdf0d931ae60b5e8effb912e99d Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Sun, 13 Oct 2024 21:09:53 -0400 Subject: [PATCH 44/45] golinter fixes --- pkg/helm-project-operator/codegen/main.go | 3 ++- pkg/helm-project-operator/controllers/common/runtime.go | 3 ++- pkg/helm-project-operator/controllers/controllers.go | 3 ++- pkg/helm-project-operator/controllers/hardened/controller.go | 1 + pkg/helm-project-operator/controllers/hardened/resolvers.go | 1 + pkg/helm-project-operator/controllers/namespace/controller.go | 1 + pkg/helm-project-operator/controllers/namespace/getter.go | 3 ++- pkg/helm-project-operator/controllers/namespace/resources.go | 3 ++- pkg/helm-project-operator/controllers/project/cleanup.go | 1 + pkg/helm-project-operator/controllers/project/controller.go | 1 + pkg/helm-project-operator/controllers/project/indexers.go | 1 + .../controllers/project/registrationdata.go | 1 + pkg/helm-project-operator/controllers/project/releasedata.go | 3 ++- pkg/helm-project-operator/controllers/project/resolvers.go | 1 + pkg/helm-project-operator/controllers/project/status.go | 1 + pkg/helm-project-operator/controllers/project/utils.go | 1 + pkg/helm-project-operator/crd/crds.go | 3 ++- pkg/helm-project-operator/operator/init.go | 1 + 18 files changed, 25 insertions(+), 7 deletions(-) diff --git a/pkg/helm-project-operator/codegen/main.go b/pkg/helm-project-operator/codegen/main.go index f162d891..09fd83ea 100644 --- a/pkg/helm-project-operator/codegen/main.go +++ b/pkg/helm-project-operator/codegen/main.go @@ -1,9 +1,10 @@ package main import ( + "os" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/crd" - "os" "github.com/sirupsen/logrus" diff --git a/pkg/helm-project-operator/controllers/common/runtime.go b/pkg/helm-project-operator/controllers/common/runtime.go index 4598423f..0165833d 100644 --- a/pkg/helm-project-operator/controllers/common/runtime.go +++ b/pkg/helm-project-operator/controllers/common/runtime.go @@ -1,10 +1,11 @@ package common import ( - "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "os" "path/filepath" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" ) diff --git a/pkg/helm-project-operator/controllers/controllers.go b/pkg/helm-project-operator/controllers/controllers.go index c6435697..e684041f 100644 --- a/pkg/helm-project-operator/controllers/controllers.go +++ b/pkg/helm-project-operator/controllers/controllers.go @@ -4,13 +4,14 @@ import ( "context" "errors" "fmt" + "time" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/hardened" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/namespace" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/project" helmproject "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io" helmprojectcontroller "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1" - "time" "github.com/k3s-io/helm-controller/pkg/controllers/chart" k3shelm "github.com/k3s-io/helm-controller/pkg/generated/controllers/helm.cattle.io" diff --git a/pkg/helm-project-operator/controllers/hardened/controller.go b/pkg/helm-project-operator/controllers/hardened/controller.go index 2e6a6dbe..e90e59c7 100644 --- a/pkg/helm-project-operator/controllers/hardened/controller.go +++ b/pkg/helm-project-operator/controllers/hardened/controller.go @@ -2,6 +2,7 @@ package hardened import ( "context" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" "github.com/rancher/wrangler/pkg/apply" diff --git a/pkg/helm-project-operator/controllers/hardened/resolvers.go b/pkg/helm-project-operator/controllers/hardened/resolvers.go index e13366b4..39730ee2 100644 --- a/pkg/helm-project-operator/controllers/hardened/resolvers.go +++ b/pkg/helm-project-operator/controllers/hardened/resolvers.go @@ -2,6 +2,7 @@ package hardened import ( "context" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" "github.com/rancher/wrangler/pkg/relatedresource" diff --git a/pkg/helm-project-operator/controllers/namespace/controller.go b/pkg/helm-project-operator/controllers/namespace/controller.go index fd4e499a..f090a037 100644 --- a/pkg/helm-project-operator/controllers/namespace/controller.go +++ b/pkg/helm-project-operator/controllers/namespace/controller.go @@ -3,6 +3,7 @@ package namespace import ( "context" "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/applier" common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" helmprojectcontroller "github.com/rancher/prometheus-federator/pkg/helm-project-operator/generated/controllers/helm.cattle.io/v1alpha1" diff --git a/pkg/helm-project-operator/controllers/namespace/getter.go b/pkg/helm-project-operator/controllers/namespace/getter.go index 2c098c49..985f39d1 100644 --- a/pkg/helm-project-operator/controllers/namespace/getter.go +++ b/pkg/helm-project-operator/controllers/namespace/getter.go @@ -2,9 +2,10 @@ package namespace import ( "fmt" - "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "sort" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + corecontroller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" diff --git a/pkg/helm-project-operator/controllers/namespace/resources.go b/pkg/helm-project-operator/controllers/namespace/resources.go index a9bcd7be..66fe926c 100644 --- a/pkg/helm-project-operator/controllers/namespace/resources.go +++ b/pkg/helm-project-operator/controllers/namespace/resources.go @@ -2,9 +2,10 @@ package namespace import ( "fmt" - common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" "strings" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/pkg/helm-project-operator/controllers/project/cleanup.go b/pkg/helm-project-operator/controllers/project/cleanup.go index 3f12500a..24c47edf 100644 --- a/pkg/helm-project-operator/controllers/project/cleanup.go +++ b/pkg/helm-project-operator/controllers/project/cleanup.go @@ -2,6 +2,7 @@ package project import ( "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" "github.com/sirupsen/logrus" diff --git a/pkg/helm-project-operator/controllers/project/controller.go b/pkg/helm-project-operator/controllers/project/controller.go index ec93a195..f5599285 100644 --- a/pkg/helm-project-operator/controllers/project/controller.go +++ b/pkg/helm-project-operator/controllers/project/controller.go @@ -3,6 +3,7 @@ package project import ( "context" "fmt" + v1alpha2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/namespace" diff --git a/pkg/helm-project-operator/controllers/project/indexers.go b/pkg/helm-project-operator/controllers/project/indexers.go index 742af68c..aa7133be 100644 --- a/pkg/helm-project-operator/controllers/project/indexers.go +++ b/pkg/helm-project-operator/controllers/project/indexers.go @@ -2,6 +2,7 @@ package project import ( "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" diff --git a/pkg/helm-project-operator/controllers/project/registrationdata.go b/pkg/helm-project-operator/controllers/project/registrationdata.go index ba79b45e..9458ca6d 100644 --- a/pkg/helm-project-operator/controllers/project/registrationdata.go +++ b/pkg/helm-project-operator/controllers/project/registrationdata.go @@ -2,6 +2,7 @@ package project import ( "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" diff --git a/pkg/helm-project-operator/controllers/project/releasedata.go b/pkg/helm-project-operator/controllers/project/releasedata.go index c80a7801..e2a99dd7 100644 --- a/pkg/helm-project-operator/controllers/project/releasedata.go +++ b/pkg/helm-project-operator/controllers/project/releasedata.go @@ -3,9 +3,10 @@ package project import ( "encoding/json" "fmt" + "strings" + v1alpha2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" - "strings" "github.com/rancher/wrangler/pkg/data" "github.com/sirupsen/logrus" diff --git a/pkg/helm-project-operator/controllers/project/resolvers.go b/pkg/helm-project-operator/controllers/project/resolvers.go index 9e8c36bb..327821f2 100644 --- a/pkg/helm-project-operator/controllers/project/resolvers.go +++ b/pkg/helm-project-operator/controllers/project/resolvers.go @@ -2,6 +2,7 @@ package project import ( "context" + common2 "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" helmcontrollerv1 "github.com/k3s-io/helm-controller/pkg/apis/helm.cattle.io/v1" diff --git a/pkg/helm-project-operator/controllers/project/status.go b/pkg/helm-project-operator/controllers/project/status.go index 065f48bf..48411d71 100644 --- a/pkg/helm-project-operator/controllers/project/status.go +++ b/pkg/helm-project-operator/controllers/project/status.go @@ -2,6 +2,7 @@ package project import ( "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" ) diff --git a/pkg/helm-project-operator/controllers/project/utils.go b/pkg/helm-project-operator/controllers/project/utils.go index 90ebf9fe..a0fecac9 100644 --- a/pkg/helm-project-operator/controllers/project/utils.go +++ b/pkg/helm-project-operator/controllers/project/utils.go @@ -2,6 +2,7 @@ package project import ( "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" ) diff --git a/pkg/helm-project-operator/crd/crds.go b/pkg/helm-project-operator/crd/crds.go index 8df02d5e..84a9938d 100644 --- a/pkg/helm-project-operator/crd/crds.go +++ b/pkg/helm-project-operator/crd/crds.go @@ -3,13 +3,14 @@ package crd import ( "context" "fmt" - "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" "io" "os" "path/filepath" "strings" "sync" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/apis/helm.cattle.io/v1alpha1" + helmcontrollercrd "github.com/k3s-io/helm-controller/pkg/crd" helmlockercrd "github.com/rancher/prometheus-federator/pkg/helm-locker/crd" "github.com/rancher/wrangler/pkg/crd" diff --git a/pkg/helm-project-operator/operator/init.go b/pkg/helm-project-operator/operator/init.go index 2c8d5530..5278e601 100644 --- a/pkg/helm-project-operator/operator/init.go +++ b/pkg/helm-project-operator/operator/init.go @@ -3,6 +3,7 @@ package operator import ( "context" "fmt" + "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/controllers/common" "github.com/rancher/prometheus-federator/pkg/helm-project-operator/crd" From 2ffaec1b0de26a2e13ce8702d0671e646104240a Mon Sep 17 00:00:00 2001 From: Dan Pock Date: Mon, 14 Oct 2024 16:28:13 -0400 Subject: [PATCH 45/45] move helm-locker chart from packages to examples --- .../helm-locker/helm-locker-chart-example}/Chart.yaml | 0 .../helm-locker/helm-locker-chart-example}/README.md | 0 .../helm-locker/helm-locker-chart-example}/templates/NOTES.txt | 0 .../helm-locker-chart-example}/templates/_helpers.tpl | 0 .../helm-locker-chart-example}/templates/clusterrole.yaml | 0 .../helm-locker-chart-example}/templates/deployment.yaml | 0 .../helm-locker-chart-example}/templates/hardened.yaml | 0 .../helm-locker/helm-locker-chart-example}/templates/psp.yaml | 0 .../helm-locker/helm-locker-chart-example}/templates/rbac.yaml | 0 .../helm-locker/helm-locker-chart-example}/values.yaml | 0 packages/helm-locker/package.yaml | 2 -- 11 files changed, 2 deletions(-) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/Chart.yaml (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/README.md (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/templates/NOTES.txt (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/templates/_helpers.tpl (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/templates/clusterrole.yaml (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/templates/deployment.yaml (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/templates/hardened.yaml (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/templates/psp.yaml (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/templates/rbac.yaml (100%) rename {packages/helm-locker/charts => examples/helm-locker/helm-locker-chart-example}/values.yaml (100%) delete mode 100644 packages/helm-locker/package.yaml diff --git a/packages/helm-locker/charts/Chart.yaml b/examples/helm-locker/helm-locker-chart-example/Chart.yaml similarity index 100% rename from packages/helm-locker/charts/Chart.yaml rename to examples/helm-locker/helm-locker-chart-example/Chart.yaml diff --git a/packages/helm-locker/charts/README.md b/examples/helm-locker/helm-locker-chart-example/README.md similarity index 100% rename from packages/helm-locker/charts/README.md rename to examples/helm-locker/helm-locker-chart-example/README.md diff --git a/packages/helm-locker/charts/templates/NOTES.txt b/examples/helm-locker/helm-locker-chart-example/templates/NOTES.txt similarity index 100% rename from packages/helm-locker/charts/templates/NOTES.txt rename to examples/helm-locker/helm-locker-chart-example/templates/NOTES.txt diff --git a/packages/helm-locker/charts/templates/_helpers.tpl b/examples/helm-locker/helm-locker-chart-example/templates/_helpers.tpl similarity index 100% rename from packages/helm-locker/charts/templates/_helpers.tpl rename to examples/helm-locker/helm-locker-chart-example/templates/_helpers.tpl diff --git a/packages/helm-locker/charts/templates/clusterrole.yaml b/examples/helm-locker/helm-locker-chart-example/templates/clusterrole.yaml similarity index 100% rename from packages/helm-locker/charts/templates/clusterrole.yaml rename to examples/helm-locker/helm-locker-chart-example/templates/clusterrole.yaml diff --git a/packages/helm-locker/charts/templates/deployment.yaml b/examples/helm-locker/helm-locker-chart-example/templates/deployment.yaml similarity index 100% rename from packages/helm-locker/charts/templates/deployment.yaml rename to examples/helm-locker/helm-locker-chart-example/templates/deployment.yaml diff --git a/packages/helm-locker/charts/templates/hardened.yaml b/examples/helm-locker/helm-locker-chart-example/templates/hardened.yaml similarity index 100% rename from packages/helm-locker/charts/templates/hardened.yaml rename to examples/helm-locker/helm-locker-chart-example/templates/hardened.yaml diff --git a/packages/helm-locker/charts/templates/psp.yaml b/examples/helm-locker/helm-locker-chart-example/templates/psp.yaml similarity index 100% rename from packages/helm-locker/charts/templates/psp.yaml rename to examples/helm-locker/helm-locker-chart-example/templates/psp.yaml diff --git a/packages/helm-locker/charts/templates/rbac.yaml b/examples/helm-locker/helm-locker-chart-example/templates/rbac.yaml similarity index 100% rename from packages/helm-locker/charts/templates/rbac.yaml rename to examples/helm-locker/helm-locker-chart-example/templates/rbac.yaml diff --git a/packages/helm-locker/charts/values.yaml b/examples/helm-locker/helm-locker-chart-example/values.yaml similarity index 100% rename from packages/helm-locker/charts/values.yaml rename to examples/helm-locker/helm-locker-chart-example/values.yaml diff --git a/packages/helm-locker/package.yaml b/packages/helm-locker/package.yaml deleted file mode 100644 index 23845fcd..00000000 --- a/packages/helm-locker/package.yaml +++ /dev/null @@ -1,2 +0,0 @@ -url: local -doNotRelease: true