From 5b8fe2ee7dd3e86454776dc2dd8400b67e301560 Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Fri, 30 Jul 2021 11:17:22 +0200 Subject: [PATCH 01/10] Introduce more explicit Condition types This commit introduces new Condition types to the v1beta1 API, facilitating easier observation of (potentially) problematic state for end-users. - `ArtifactUnavailableCondition`: indicates there is no artifact available for the resource. This Condition should be set by the reconciler as soon as it observes the absence of an artifact for a source. - `CheckoutFailedCondition`: indicates a transient or persistent checkout failure. This Condition should be set by the reconciler as soon as it observes a Git checkout failure, including any prerequisites like the unavailability of the referenced Secret used for authentication. It should be deleted as soon as a successful checkout has been observed again. - `SourceVerifiedCondition`: indicates the integrity of the source has been verified. The Condition should be set to True or False by the reconciler based on the result of the integrity check. If there is no verification mode and/or secret configured, the Condition should be removed. - `IncludeUnavailableCondition`: indicates one of the referenced includes is not available. This Condition should for example be set by the reconciler when the include does not exist, or does not have an artifact. If the includes become available, it should be deleted. - `ArtifactOutdatedCondition`: indicates the current artifact of the source is outdated. This Condition should for example be set by the reconciler when it notices there is a newer revision for an artifact, or the previously included artifacts differ from the current available ones. The Condition should be removed after writing a new artifact to the storage. Signed-off-by: Hidde Beydals --- api/v1beta1/gitrepository_types.go | 57 ++++++++++++------- ...rce.toolkit.fluxcd.io_gitrepositories.yaml | 12 ++-- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/api/v1beta1/gitrepository_types.go b/api/v1beta1/gitrepository_types.go index c1014e6b7..9344e3763 100644 --- a/api/v1beta1/gitrepository_types.go +++ b/api/v1beta1/gitrepository_types.go @@ -34,6 +34,30 @@ const ( LibGit2Implementation = "libgit2" ) +const ( + // ArtifactUnavailableCondition indicates there is no Artifact available for the Source. + // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. + ArtifactUnavailableCondition string = "ArtifactUnavailable" + + // CheckoutFailedCondition indicates a transient or persistent checkout failure. If True, observations on the + // upstream Source revision are not possible, and the Artifact available for the Source may be outdated. + // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. + CheckoutFailedCondition string = "CheckoutFailed" + + // SourceVerifiedCondition indicates the integrity of the Source has been verified. If True, the integrity check + // succeeded. If False, it failed. The Condition is only present on the resource if the integrity has been verified. + SourceVerifiedCondition string = "SourceVerified" + + // IncludeUnavailableCondition indicates one of the includes is not available. For example, because it does not + // exist, or does not have an Artifact. + // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. + IncludeUnavailableCondition string = "IncludeUnavailable" + + // ArtifactOutdatedCondition indicates the current Artifact of the Source is outdated. + // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. + ArtifactOutdatedCondition string = "ArtifactOutdated" +) + // GitRepositorySpec defines the desired state of a Git repository. type GitRepositorySpec struct { // The repository URL, can be a HTTP/S or SSH address. @@ -42,10 +66,8 @@ type GitRepositorySpec struct { URL string `json:"url"` // The secret name containing the Git credentials. - // For HTTPS repositories the secret must contain username and password - // fields. - // For SSH repositories the secret must contain identity, identity.pub and - // known_hosts fields. + // For HTTPS repositories the secret must contain username and password fields. + // For SSH repositories the secret must contain 'identity', 'identity.pub' and 'known_hosts' fields. // +optional SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"` @@ -63,16 +85,16 @@ type GitRepositorySpec struct { // +optional Reference *GitRepositoryRef `json:"ref,omitempty"` - // Verify OpenPGP signature for the Git commit HEAD points to. + // Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to. // +optional Verification *GitRepositoryVerification `json:"verify,omitempty"` - // Ignore overrides the set of excluded patterns in the .sourceignore format - // (which is the same as .gitignore). If not provided, a default will be used, - // consult the documentation for your version to find out what those are. + // Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore). + // If not provided, a default will be used, consult the documentation for your version to find out what those are. // +optional Ignore *string `json:"ignore,omitempty"` + // Suspend tells the controller to suspend the reconciliation of this source. // This flag tells the controller to suspend the reconciliation of this source. // +optional Suspend bool `json:"suspend,omitempty"` @@ -84,13 +106,13 @@ type GitRepositorySpec struct { // +optional GitImplementation string `json:"gitImplementation,omitempty"` - // When enabled, after the clone is created, initializes all submodules within, - // using their default settings. + // When enabled, after the clone is created, initializes all submodules within, using their default settings. // This option is available only when using the 'go-git' GitImplementation. // +optional RecurseSubmodules bool `json:"recurseSubmodules,omitempty"` - // Extra git repositories to map into the repository + // Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for + // this resource. Include []GitRepositoryInclude `json:"include,omitempty"` // AccessFrom defines an Access Control List for allowing cross-namespace references to this object. @@ -144,11 +166,11 @@ type GitRepositoryRef struct { // GitRepositoryVerification defines the OpenPGP signature verification process. type GitRepositoryVerification struct { - // Mode describes what git object should be verified, currently ('head'). + // Mode describes what Git object should be verified, currently ('head'). // +kubebuilder:validation:Enum=head Mode string `json:"mode"` - // The secret name containing the public keys of all trusted Git authors. + // SecretRef containing the public keys of all trusted Git authors. SecretRef meta.LocalObjectReference `json:"secretRef,omitempty"` } @@ -162,8 +184,7 @@ type GitRepositoryStatus struct { // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` - // URL is the download link for the artifact output of the last repository - // sync. + // URL is the download link for the artifact output of the last repository sync. // +optional URL string `json:"url,omitempty"` @@ -179,12 +200,10 @@ type GitRepositoryStatus struct { } const ( - // GitOperationSucceedReason represents the fact that the git clone, pull - // and checkout operations succeeded. + // GitOperationSucceedReason represents the fact that the git clone, pull and checkout operations succeeded. GitOperationSucceedReason string = "GitOperationSucceed" - // GitOperationFailedReason represents the fact that the git clone, pull or - // checkout operations failed. + // GitOperationFailedReason represents the fact that the git clone, pull or checkout operations failed. GitOperationFailedReason string = "GitOperationFailed" ) diff --git a/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml b/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml index 4f5de9a06..d3f644be1 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml @@ -76,7 +76,7 @@ spec: description: Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore). If not provided, a default will be used, consult the documentation for your version to find out what those are. type: string include: - description: Extra git repositories to map into the repository + description: Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for this resource. items: description: GitRepositoryInclude defines a source with a from and to path. properties: @@ -122,7 +122,7 @@ spec: type: string type: object secretRef: - description: The secret name containing the Git credentials. For HTTPS repositories the secret must contain username and password fields. For SSH repositories the secret must contain identity, identity.pub and known_hosts fields. + description: The secret name containing the Git credentials. For HTTPS repositories the secret must contain username and password fields. For SSH repositories the secret must contain 'identity', 'identity.pub' and 'known_hosts' fields. properties: name: description: Name of the referent @@ -131,7 +131,7 @@ spec: - name type: object suspend: - description: This flag tells the controller to suspend the reconciliation of this source. + description: Suspend tells the controller to suspend the reconciliation of this source. This flag tells the controller to suspend the reconciliation of this source. type: boolean timeout: default: 20s @@ -142,15 +142,15 @@ spec: pattern: ^(http|https|ssh):// type: string verify: - description: Verify OpenPGP signature for the Git commit HEAD points to. + description: Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to. properties: mode: - description: Mode describes what git object should be verified, currently ('head'). + description: Mode describes what Git object should be verified, currently ('head'). enum: - head type: string secretRef: - description: The secret name containing the public keys of all trusted Git authors. + description: SecretRef containing the public keys of all trusted Git authors. properties: name: description: Name of the referent From b657796742028b663c63fceb7d6e09887603be5b Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Fri, 30 Jul 2021 12:33:18 +0200 Subject: [PATCH 02/10] Implement new runtime interfaces, prepare testenv This commit ensures all API objects implement the interfaces used by the runtime package to work with conditions, etc., and prepares the test suite to work with the `pkg/runtime/testenv` wrapper. Changes are made in a backwards compatible way (that being: the existing code can still be build and works as expected), but without proper dependency boundaries. The result of this is that the API package temporary depends on the runtime package, which is resolved when all reconcilers have been refactored and the API package does no longer contain condition modifying functions. Signed-off-by: Hidde Beydals --- api/go.mod | 23 +- api/go.sum | 34 ++- api/v1beta1/bucket_types.go | 33 ++- api/v1beta1/gitrepository_types.go | 33 ++- api/v1beta1/helmchart_types.go | 35 ++- api/v1beta1/helmrepository_types.go | 33 ++- .../source.toolkit.fluxcd.io_buckets.yaml | 4 +- ...rce.toolkit.fluxcd.io_gitrepositories.yaml | 8 +- .../source.toolkit.fluxcd.io_helmcharts.yaml | 2 +- ...ce.toolkit.fluxcd.io_helmrepositories.yaml | 4 +- controllers/bucket_controller.go | 14 +- controllers/gitrepository_controller.go | 23 +- controllers/helmchart_controller.go | 14 +- controllers/helmchart_controller_test.go | 116 ++++----- controllers/helmrepository_controller.go | 14 +- controllers/helmrepository_controller_test.go | 8 +- controllers/legacy_suite_test.go | 190 +++++++++++++++ controllers/storage.go | 8 +- controllers/suite_test.go | 223 +++++++----------- docs/api/source.md | 51 ++-- go.mod | 10 +- go.sum | 18 +- main.go | 24 +- 23 files changed, 559 insertions(+), 363 deletions(-) create mode 100644 controllers/legacy_suite_test.go diff --git a/api/go.mod b/api/go.mod index ce4aef76c..2af43091b 100644 --- a/api/go.mod +++ b/api/go.mod @@ -4,25 +4,46 @@ go 1.17 require ( github.com/fluxcd/pkg/apis/acl v0.0.3 - github.com/fluxcd/pkg/apis/meta v0.10.2 + github.com/fluxcd/pkg/apis/meta v0.11.0-rc.3 + // TODO(hidde): introduction of the runtime package is temporary, and the dependency should be removed as soon as + // all APIs have been updated to the runtime standards (more specifically; have dropped their condition modifying + // functions). + github.com/fluxcd/pkg/runtime v0.13.0-rc.6 k8s.io/apimachinery v0.23.1 sigs.k8s.io/controller-runtime v0.11.0 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/go-logr/logr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.6 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/googleapis/gnostic v0.5.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/onsi/gomega v1.17.0 // indirect + github.com/pkg/errors v0.9.1 // indirect golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9 // indirect + golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect + golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 // indirect + golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.27.1 // 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/api v0.23.0 // indirect + k8s.io/client-go v0.23.0 // 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-20211208161948-7d6a63dca704 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/api/go.sum b/api/go.sum index ffe31a8af..927fd8a67 100644 --- a/api/go.sum +++ b/api/go.sum @@ -68,6 +68,7 @@ github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiU github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -76,7 +77,9 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/census-instrumentation/opencensus-proto v0.2.1/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 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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= @@ -118,13 +121,16 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fluxcd/pkg/apis/acl v0.0.3 h1:Lw0ZHdpnO4G7Zy9KjrzwwBmDZQuy4qEjaU/RvA6k1lc= github.com/fluxcd/pkg/apis/acl v0.0.3/go.mod h1:XPts6lRJ9C9fIF9xVWofmQwftvhY25n1ps7W9xw0XLU= -github.com/fluxcd/pkg/apis/meta v0.10.2 h1:pnDBBEvfs4HaKiVAYgz+e/AQ8dLvcgmVfSeBroZ/KKI= -github.com/fluxcd/pkg/apis/meta v0.10.2/go.mod h1:KQ2er9xa6koy7uoPMZjIjNudB5p4tXs+w0GO6fRcy7I= +github.com/fluxcd/pkg/apis/meta v0.11.0-rc.3 h1:YY6RlaHIMXawgEOJhJbSrm4NpD9fJTCWFGKgtNfQ0/g= +github.com/fluxcd/pkg/apis/meta v0.11.0-rc.3/go.mod h1:ki5wJE4nuFOZt78q0RSYkrKwINgIBPynuswZhnTOSoI= +github.com/fluxcd/pkg/runtime v0.13.0-rc.6 h1:MsxiKYGsuRzEvyreQG5ocNaIZDwKhqvQ711/w4rTkCo= +github.com/fluxcd/pkg/runtime v0.13.0-rc.6/go.mod h1:4oKUO19TeudXrnCRnxCfMSS7EQTYpYlgfXwlQuDJ/Eg= 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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -148,6 +154,7 @@ github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -194,6 +201,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -236,6 +244,7 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/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/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -250,9 +259,11 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 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= @@ -270,6 +281,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO 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.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -309,6 +321,7 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -359,6 +372,7 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko 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/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -369,21 +383,25 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP 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.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 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 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 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.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 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 h1:vGVfV9KrDTvWt5boZO0I19g2E3CsWfpPPKZM9dt3mEw= github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/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/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -466,15 +484,18 @@ go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4 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.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 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/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 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= +go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -584,6 +605,7 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ 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.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/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= @@ -663,6 +685,7 @@ golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 h1:M69LAlWZCshgp0QSzyDcSsSIejIEeuaCVpmwcKwyLMk= golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= @@ -678,6 +701,7 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb 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.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= @@ -743,6 +767,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= 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= @@ -772,6 +797,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -852,6 +878,7 @@ 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= 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= @@ -895,11 +922,13 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.23.0 h1:WrL1gb73VSC8obi8cuYETJGXEoFNEh3LU0Pt+Sokgro= k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg= +k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY= k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4= k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc= k8s.io/apimachinery v0.23.1 h1:sfBjlDFwj2onG0Ijx5C+SrAoeUscPrmghm7wHP+uXlo= k8s.io/apimachinery v0.23.1/go.mod h1:SADt2Kl8/sttJ62RRsi9MIV4o8f5S3coArm0Iu3fBno= k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4= +k8s.io/client-go v0.23.0 h1:vcsOqyPq7XV3QmQRCBH/t9BICJM9Q1M18qahjv+rebY= k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA= k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE= k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI= @@ -908,6 +937,7 @@ 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.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +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/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= diff --git a/api/v1beta1/bucket_types.go b/api/v1beta1/bucket_types.go index 4df79c2e1..e4b4d6cdc 100644 --- a/api/v1beta1/bucket_types.go +++ b/api/v1beta1/bucket_types.go @@ -22,6 +22,7 @@ import ( "github.com/fluxcd/pkg/apis/acl" "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/conditions" ) const ( @@ -126,7 +127,7 @@ func BucketProgressing(bucket Bucket) Bucket { bucket.Status.ObservedGeneration = bucket.Generation bucket.Status.URL = "" bucket.Status.Conditions = []metav1.Condition{} - meta.SetResourceCondition(&bucket, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") + conditions.MarkUnknown(&bucket, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress") return bucket } @@ -136,14 +137,14 @@ func BucketProgressing(bucket Bucket) Bucket { func BucketReady(bucket Bucket, artifact Artifact, url, reason, message string) Bucket { bucket.Status.Artifact = &artifact bucket.Status.URL = url - meta.SetResourceCondition(&bucket, meta.ReadyCondition, metav1.ConditionTrue, reason, message) + conditions.MarkTrue(&bucket, meta.ReadyCondition, reason, message) return bucket } // BucketNotReady sets the meta.ReadyCondition on the Bucket to 'False', with // the given reason and message. It returns the modified Bucket. func BucketNotReady(bucket Bucket, reason, message string) Bucket { - meta.SetResourceCondition(&bucket, meta.ReadyCondition, metav1.ConditionFalse, reason, message) + conditions.MarkFalse(&bucket, meta.ReadyCondition, reason, message) return bucket } @@ -158,22 +159,32 @@ func BucketReadyMessage(bucket Bucket) string { return "" } -// GetArtifact returns the latest artifact from the source if present in the -// status sub-resource. +// GetConditions returns the status conditions of the object. +func (in Bucket) GetConditions() []metav1.Condition { + return in.Status.Conditions +} + +// SetConditions sets the status conditions on the object. +func (in *Bucket) SetConditions(conditions []metav1.Condition) { + in.Status.Conditions = conditions +} + +// GetInterval returns the interval at which the source is reconciled. +func (in Bucket) GetInterval() metav1.Duration { + return in.Spec.Interval +} + +// GetArtifact returns the latest artifact from the source if present in the status sub-resource. func (in *Bucket) GetArtifact() *Artifact { return in.Status.Artifact } -// GetStatusConditions returns a pointer to the Status.Conditions slice +// GetStatusConditions returns a pointer to the Status.Conditions slice. +// Deprecated: use GetConditions instead. func (in *Bucket) GetStatusConditions() *[]metav1.Condition { return &in.Status.Conditions } -// GetInterval returns the interval at which the source is updated. -func (in *Bucket) GetInterval() metav1.Duration { - return in.Spec.Interval -} - // +genclient // +genclient:Namespaced // +kubebuilder:object:root=true diff --git a/api/v1beta1/gitrepository_types.go b/api/v1beta1/gitrepository_types.go index 9344e3763..c2cfc8e4c 100644 --- a/api/v1beta1/gitrepository_types.go +++ b/api/v1beta1/gitrepository_types.go @@ -22,6 +22,7 @@ import ( "github.com/fluxcd/pkg/apis/acl" "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/conditions" ) const ( @@ -215,7 +216,7 @@ func GitRepositoryProgressing(repository GitRepository) GitRepository { repository.Status.ObservedGeneration = repository.Generation repository.Status.URL = "" repository.Status.Conditions = []metav1.Condition{} - meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") + conditions.MarkUnknown(&repository, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress") return repository } @@ -226,7 +227,7 @@ func GitRepositoryReady(repository GitRepository, artifact Artifact, includedArt repository.Status.Artifact = &artifact repository.Status.IncludedArtifacts = includedArtifacts repository.Status.URL = url - meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionTrue, reason, message) + conditions.MarkTrue(&repository, meta.ReadyCondition, reason, message) return repository } @@ -234,7 +235,7 @@ func GitRepositoryReady(repository GitRepository, artifact Artifact, includedArt // to 'False', with the given reason and message. It returns the modified // GitRepository. func GitRepositoryNotReady(repository GitRepository, reason, message string) GitRepository { - meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionFalse, reason, message) + conditions.MarkFalse(&repository, meta.ReadyCondition, reason, message) return repository } @@ -249,22 +250,32 @@ func GitRepositoryReadyMessage(repository GitRepository) string { return "" } -// GetArtifact returns the latest artifact from the source if present in the -// status sub-resource. +// GetConditions returns the status conditions of the object. +func (in GitRepository) GetConditions() []metav1.Condition { + return in.Status.Conditions +} + +// SetConditions sets the status conditions on the object. +func (in *GitRepository) SetConditions(conditions []metav1.Condition) { + in.Status.Conditions = conditions +} + +// GetInterval returns the interval at which the source is reconciled. +func (in GitRepository) GetInterval() metav1.Duration { + return in.Spec.Interval +} + +// GetArtifact returns the latest artifact from the source if present in the status sub-resource. func (in *GitRepository) GetArtifact() *Artifact { return in.Status.Artifact } -// GetStatusConditions returns a pointer to the Status.Conditions slice +// GetStatusConditions returns a pointer to the Status.Conditions slice. +// Deprecated: use GetConditions instead. func (in *GitRepository) GetStatusConditions() *[]metav1.Condition { return &in.Status.Conditions } -// GetInterval returns the interval at which the source is updated. -func (in *GitRepository) GetInterval() metav1.Duration { - return in.Spec.Interval -} - // +genclient // +genclient:Namespaced // +kubebuilder:object:root=true diff --git a/api/v1beta1/helmchart_types.go b/api/v1beta1/helmchart_types.go index 51c04781d..78a20852b 100644 --- a/api/v1beta1/helmchart_types.go +++ b/api/v1beta1/helmchart_types.go @@ -22,6 +22,7 @@ import ( "github.com/fluxcd/pkg/apis/acl" "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/conditions" ) // HelmChartKind is the string representation of a HelmChart. @@ -152,7 +153,7 @@ func HelmChartProgressing(chart HelmChart) HelmChart { chart.Status.ObservedGeneration = chart.Generation chart.Status.URL = "" chart.Status.Conditions = []metav1.Condition{} - meta.SetResourceCondition(&chart, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") + conditions.MarkUnknown(&chart, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress") return chart } @@ -162,7 +163,7 @@ func HelmChartProgressing(chart HelmChart) HelmChart { func HelmChartReady(chart HelmChart, artifact Artifact, url, reason, message string) HelmChart { chart.Status.Artifact = &artifact chart.Status.URL = url - meta.SetResourceCondition(&chart, meta.ReadyCondition, metav1.ConditionTrue, reason, message) + conditions.MarkTrue(&chart, meta.ReadyCondition, reason, message) return chart } @@ -170,7 +171,7 @@ func HelmChartReady(chart HelmChart, artifact Artifact, url, reason, message str // 'False', with the given reason and message. It returns the modified // HelmChart. func HelmChartNotReady(chart HelmChart, reason, message string) HelmChart { - meta.SetResourceCondition(&chart, meta.ReadyCondition, metav1.ConditionFalse, reason, message) + conditions.MarkFalse(&chart, meta.ReadyCondition, reason, message) return chart } @@ -185,22 +186,26 @@ func HelmChartReadyMessage(chart HelmChart) string { return "" } -// GetArtifact returns the latest artifact from the source if present in the -// status sub-resource. -func (in *HelmChart) GetArtifact() *Artifact { - return in.Status.Artifact +// GetConditions returns the status conditions of the object. +func (in HelmChart) GetConditions() []metav1.Condition { + return in.Status.Conditions } -// GetStatusConditions returns a pointer to the Status.Conditions slice -func (in *HelmChart) GetStatusConditions() *[]metav1.Condition { - return &in.Status.Conditions +// SetConditions sets the status conditions on the object. +func (in *HelmChart) SetConditions(conditions []metav1.Condition) { + in.Status.Conditions = conditions } -// GetInterval returns the interval at which the source is updated. -func (in *HelmChart) GetInterval() metav1.Duration { +// GetInterval returns the interval at which the source is reconciled. +func (in HelmChart) GetInterval() metav1.Duration { return in.Spec.Interval } +// GetArtifact returns the latest artifact from the source if present in the status sub-resource. +func (in *HelmChart) GetArtifact() *Artifact { + return in.Status.Artifact +} + // GetValuesFiles returns a merged list of ValuesFiles. func (in *HelmChart) GetValuesFiles() []string { valuesFiles := in.Spec.ValuesFiles @@ -212,6 +217,12 @@ func (in *HelmChart) GetValuesFiles() []string { return valuesFiles } +// GetStatusConditions returns a pointer to the Status.Conditions slice. +// Deprecated: use GetConditions instead. +func (in *HelmChart) GetStatusConditions() *[]metav1.Condition { + return &in.Status.Conditions +} + // +genclient // +genclient:Namespaced // +kubebuilder:object:root=true diff --git a/api/v1beta1/helmrepository_types.go b/api/v1beta1/helmrepository_types.go index 0af0d4cf6..65b924042 100644 --- a/api/v1beta1/helmrepository_types.go +++ b/api/v1beta1/helmrepository_types.go @@ -22,6 +22,7 @@ import ( "github.com/fluxcd/pkg/apis/acl" "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/conditions" ) const ( @@ -113,7 +114,7 @@ func HelmRepositoryProgressing(repository HelmRepository) HelmRepository { repository.Status.ObservedGeneration = repository.Generation repository.Status.URL = "" repository.Status.Conditions = []metav1.Condition{} - meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") + conditions.MarkUnknown(&repository, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress") return repository } @@ -123,7 +124,7 @@ func HelmRepositoryProgressing(repository HelmRepository) HelmRepository { func HelmRepositoryReady(repository HelmRepository, artifact Artifact, url, reason, message string) HelmRepository { repository.Status.Artifact = &artifact repository.Status.URL = url - meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionTrue, reason, message) + conditions.MarkTrue(&repository, meta.ReadyCondition, reason, message) return repository } @@ -131,7 +132,7 @@ func HelmRepositoryReady(repository HelmRepository, artifact Artifact, url, reas // HelmRepository to 'False', with the given reason and message. It returns the // modified HelmRepository. func HelmRepositoryNotReady(repository HelmRepository, reason, message string) HelmRepository { - meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionFalse, reason, message) + conditions.MarkFalse(&repository, meta.ReadyCondition, reason, message) return repository } @@ -146,22 +147,32 @@ func HelmRepositoryReadyMessage(repository HelmRepository) string { return "" } -// GetArtifact returns the latest artifact from the source if present in the -// status sub-resource. +// GetConditions returns the status conditions of the object. +func (in HelmRepository) GetConditions() []metav1.Condition { + return in.Status.Conditions +} + +// SetConditions sets the status conditions on the object. +func (in *HelmRepository) SetConditions(conditions []metav1.Condition) { + in.Status.Conditions = conditions +} + +// GetInterval returns the interval at which the source is reconciled. +func (in HelmRepository) GetInterval() metav1.Duration { + return in.Spec.Interval +} + +// GetArtifact returns the latest artifact from the source if present in the status sub-resource. func (in *HelmRepository) GetArtifact() *Artifact { return in.Status.Artifact } -// GetStatusConditions returns a pointer to the Status.Conditions slice +// GetStatusConditions returns a pointer to the Status.Conditions slice. +// Deprecated: use GetConditions instead. func (in *HelmRepository) GetStatusConditions() *[]metav1.Condition { return &in.Status.Conditions } -// GetInterval returns the interval at which the source is updated. -func (in *HelmRepository) GetInterval() metav1.Duration { - return in.Spec.Interval -} - // +genclient // +genclient:Namespaced // +kubebuilder:object:root=true diff --git a/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml b/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml index f613db849..7a26ead34 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml @@ -93,7 +93,7 @@ spec: description: The name of the secret containing authentication credentials for the Bucket. properties: name: - description: Name of the referent + description: Name of the referent. type: string required: - name @@ -183,7 +183,7 @@ spec: type: object type: array lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected. + description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can be detected. type: string observedGeneration: description: ObservedGeneration is the last observed generation. diff --git a/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml b/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml index d3f644be1..fd6c2138a 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_gitrepositories.yaml @@ -87,7 +87,7 @@ spec: description: Reference to a GitRepository to include. properties: name: - description: Name of the referent + description: Name of the referent. type: string required: - name @@ -125,7 +125,7 @@ spec: description: The secret name containing the Git credentials. For HTTPS repositories the secret must contain username and password fields. For SSH repositories the secret must contain 'identity', 'identity.pub' and 'known_hosts' fields. properties: name: - description: Name of the referent + description: Name of the referent. type: string required: - name @@ -153,7 +153,7 @@ spec: description: SecretRef containing the public keys of all trusted Git authors. properties: name: - description: Name of the referent + description: Name of the referent. type: string required: - name @@ -264,7 +264,7 @@ spec: type: object type: array lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected. + description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can be detected. type: string observedGeneration: description: ObservedGeneration is the last observed generation. diff --git a/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml b/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml index b45e88211..f0571e0b5 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_helmcharts.yaml @@ -200,7 +200,7 @@ spec: type: object type: array lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected. + description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can be detected. type: string observedGeneration: description: ObservedGeneration is the last observed generation. diff --git a/config/crd/bases/source.toolkit.fluxcd.io_helmrepositories.yaml b/config/crd/bases/source.toolkit.fluxcd.io_helmrepositories.yaml index bcce23a7f..981a1f88d 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_helmrepositories.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_helmrepositories.yaml @@ -75,7 +75,7 @@ spec: description: The name of the secret containing authentication credentials for the Helm repository. For HTTP/S basic auth the secret must contain username and password fields. For TLS the secret must contain a certFile and keyFile, and/or caCert fields. properties: name: - description: Name of the referent + description: Name of the referent. type: string required: - name @@ -167,7 +167,7 @@ spec: type: object type: array lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected. + description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can be detected. type: string observedGeneration: description: ObservedGeneration is the last observed generation. diff --git a/controllers/bucket_controller.go b/controllers/bucket_controller.go index a25587d1a..a795453e4 100644 --- a/controllers/bucket_controller.go +++ b/controllers/bucket_controller.go @@ -541,21 +541,11 @@ func (r *BucketReconciler) gc(bucket sourcev1.Bucket) error { // event emits a Kubernetes event and forwards the event to notification controller if configured func (r *BucketReconciler) event(ctx context.Context, bucket sourcev1.Bucket, severity, msg string) { - log := ctrl.LoggerFrom(ctx) if r.EventRecorder != nil { - r.EventRecorder.Eventf(&bucket, "Normal", severity, msg) + r.EventRecorder.Eventf(&bucket, corev1.EventTypeNormal, severity, msg) } if r.ExternalEventRecorder != nil { - objRef, err := reference.GetReference(r.Scheme, &bucket) - if err != nil { - log.Error(err, "unable to send event") - return - } - - if err := r.ExternalEventRecorder.Eventf(*objRef, nil, severity, severity, msg); err != nil { - log.Error(err, "unable to send event") - return - } + r.ExternalEventRecorder.Eventf(&bucket, corev1.EventTypeNormal, severity, msg) } } diff --git a/controllers/gitrepository_controller.go b/controllers/gitrepository_controller.go index 7642a1614..d2e92d5bb 100644 --- a/controllers/gitrepository_controller.go +++ b/controllers/gitrepository_controller.go @@ -122,7 +122,7 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques // check dependencies if len(repository.Spec.Include) > 0 { if err := r.checkDependencies(repository); err != nil { - repository = sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()) + repository = sourcev1.GitRepositoryNotReady(repository, "DependencyNotReady", err.Error()) if err := r.updateStatus(ctx, req, repository.Status); err != nil { log.Error(err, "unable to update status for dependency not ready") return ctrl.Result{Requeue: true}, err @@ -284,7 +284,7 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, repository sour var gr sourcev1.GitRepository err := r.Get(context.Background(), dName, &gr) if err != nil { - return sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()), err + return sourcev1.GitRepositoryNotReady(repository, "DependencyNotReady", err.Error()), err } includedArtifacts = append(includedArtifacts, gr.GetArtifact()) } @@ -329,11 +329,11 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, repository sour for i, incl := range repository.Spec.Include { toPath, err := securejoin.SecureJoin(tmpGit, incl.GetToPath()) if err != nil { - return sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()), err + return sourcev1.GitRepositoryNotReady(repository, "DependencyNotReady", err.Error()), err } err = r.Storage.CopyToPath(includedArtifacts[i], incl.GetFromPath(), toPath) if err != nil { - return sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()), err + return sourcev1.GitRepositoryNotReady(repository, "DependencyNotReady", err.Error()), err } } @@ -423,22 +423,11 @@ func (r *GitRepositoryReconciler) gc(repository sourcev1.GitRepository) error { // event emits a Kubernetes event and forwards the event to notification controller if configured func (r *GitRepositoryReconciler) event(ctx context.Context, repository sourcev1.GitRepository, severity, msg string) { - log := ctrl.LoggerFrom(ctx) - if r.EventRecorder != nil { - r.EventRecorder.Eventf(&repository, "Normal", severity, msg) + r.EventRecorder.Eventf(&repository, corev1.EventTypeNormal, severity, msg) } if r.ExternalEventRecorder != nil { - objRef, err := reference.GetReference(r.Scheme, &repository) - if err != nil { - log.Error(err, "unable to send event") - return - } - - if err := r.ExternalEventRecorder.Eventf(*objRef, nil, severity, severity, msg); err != nil { - log.Error(err, "unable to send event") - return - } + r.ExternalEventRecorder.Eventf(&repository, corev1.EventTypeNormal, severity, msg) } } diff --git a/controllers/helmchart_controller.go b/controllers/helmchart_controller.go index e63f8e458..46da4db08 100644 --- a/controllers/helmchart_controller.go +++ b/controllers/helmchart_controller.go @@ -634,21 +634,11 @@ func (r *HelmChartReconciler) gc(chart sourcev1.HelmChart) error { // event emits a Kubernetes event and forwards the event to notification // controller if configured. func (r *HelmChartReconciler) event(ctx context.Context, chart sourcev1.HelmChart, severity, msg string) { - log := ctrl.LoggerFrom(ctx) if r.EventRecorder != nil { - r.EventRecorder.Eventf(&chart, "Normal", severity, msg) + r.EventRecorder.Eventf(&chart, corev1.EventTypeNormal, severity, msg) } if r.ExternalEventRecorder != nil { - objRef, err := reference.GetReference(r.Scheme, &chart) - if err != nil { - log.Error(err, "unable to send event") - return - } - - if err := r.ExternalEventRecorder.Eventf(*objRef, nil, severity, severity, msg); err != nil { - log.Error(err, "unable to send event") - return - } + r.ExternalEventRecorder.Eventf(&chart, corev1.EventTypeNormal, severity, msg) } } diff --git a/controllers/helmchart_controller_test.go b/controllers/helmchart_controller_test.go index cb9838b15..f762fcd08 100644 --- a/controllers/helmchart_controller_test.go +++ b/controllers/helmchart_controller_test.go @@ -129,9 +129,9 @@ var _ = Describe("HelmChartReconciler", func() { got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) - return got.Status.Artifact != nil && storage.ArtifactExist(*got.Status.Artifact) + return got.Status.Artifact != nil && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) @@ -146,7 +146,7 @@ var _ = Describe("HelmChartReconciler", func() { _ = k8sClient.Get(context.Background(), key, now) // Test revision change and garbage collection return now.Status.Artifact.Revision != got.Status.Artifact.Revision && - !storage.ArtifactExist(*got.Status.Artifact) + !ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) When("Setting valid valuesFiles attribute", func() { @@ -161,12 +161,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -184,12 +184,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -207,12 +207,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -228,12 +228,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) @@ -250,12 +250,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) @@ -271,12 +271,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) @@ -682,7 +682,7 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) By("Committing a new version in the chart metadata") @@ -727,9 +727,9 @@ var _ = Describe("HelmChartReconciler", func() { _ = k8sClient.Get(context.Background(), key, now) // Test revision change and garbage collection return now.Status.Artifact.Revision != got.Status.Artifact.Revision && - !storage.ArtifactExist(*got.Status.Artifact) + !ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - helmChart, err := loader.Load(storage.LocalPath(*now.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*now.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values).ToNot(BeNil()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) @@ -744,7 +744,7 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Revision != updated.Status.Artifact.Revision && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) Expect(got.Status.Artifact.Revision).To(ContainSubstring(updated.Status.Artifact.Revision)) Expect(got.Status.Artifact.Revision).To(ContainSubstring(commit.String()[0:12])) @@ -762,12 +762,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -785,12 +785,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -808,12 +808,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -834,16 +834,16 @@ var _ = Describe("HelmChartReconciler", func() { // Use status condition to be sure. for _, condn := range got.Status.Conditions { if strings.Contains(condn.Message, "with merged values files [./testdata/charts/helmchart/override.yaml]") && - storage.ArtifactExist(*got.Status.Artifact) { + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) { return true } } return false }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) @@ -860,12 +860,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) @@ -970,7 +970,7 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) }) }) @@ -1213,9 +1213,9 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) @@ -1232,12 +1232,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -1255,12 +1255,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -1278,12 +1278,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) @@ -1299,12 +1299,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) @@ -1321,12 +1321,12 @@ var _ = Describe("HelmChartReconciler", func() { Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) - f, err := os.Stat(storage.LocalPath(*got.Status.Artifact)) + f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) - helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact)) + helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) diff --git a/controllers/helmrepository_controller.go b/controllers/helmrepository_controller.go index d82bdad69..a03f6b61b 100644 --- a/controllers/helmrepository_controller.go +++ b/controllers/helmrepository_controller.go @@ -335,21 +335,11 @@ func (r *HelmRepositoryReconciler) gc(repository sourcev1.HelmRepository) error // event emits a Kubernetes event and forwards the event to notification controller if configured func (r *HelmRepositoryReconciler) event(ctx context.Context, repository sourcev1.HelmRepository, severity, msg string) { - log := ctrl.LoggerFrom(ctx) if r.EventRecorder != nil { - r.EventRecorder.Eventf(&repository, "Normal", severity, msg) + r.EventRecorder.Eventf(&repository, corev1.EventTypeNormal, severity, msg) } if r.ExternalEventRecorder != nil { - objRef, err := reference.GetReference(r.Scheme, &repository) - if err != nil { - log.Error(err, "unable to send event") - return - } - - if err := r.ExternalEventRecorder.Eventf(*objRef, nil, severity, severity, msg); err != nil { - log.Error(err, "unable to send event") - return - } + r.ExternalEventRecorder.Eventf(&repository, corev1.EventTypeNormal, severity, msg) } } diff --git a/controllers/helmrepository_controller_test.go b/controllers/helmrepository_controller_test.go index e7d945a60..69a3656e9 100644 --- a/controllers/helmrepository_controller_test.go +++ b/controllers/helmrepository_controller_test.go @@ -99,7 +99,7 @@ var _ = Describe("HelmRepositoryReconciler", func() { got := &sourcev1.HelmRepository{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) - return got.Status.Artifact != nil && storage.ArtifactExist(*got.Status.Artifact) + return got.Status.Artifact != nil && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) By("Updating the chart index") @@ -112,7 +112,7 @@ var _ = Describe("HelmRepositoryReconciler", func() { _ = k8sClient.Get(context.Background(), key, now) // Test revision change and garbage collection return now.Status.Artifact.Revision != got.Status.Artifact.Revision && - !storage.ArtifactExist(*got.Status.Artifact) + !ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) updated := &sourcev1.HelmRepository{} @@ -291,7 +291,7 @@ var _ = Describe("HelmRepositoryReconciler", func() { got := &sourcev1.HelmRepository{} _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) By("Expecting missing secret error") @@ -385,7 +385,7 @@ var _ = Describe("HelmRepositoryReconciler", func() { got := &sourcev1.HelmRepository{} _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && - storage.ArtifactExist(*got.Status.Artifact) + ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) By("Expecting missing secret error") diff --git a/controllers/legacy_suite_test.go b/controllers/legacy_suite_test.go new file mode 100644 index 000000000..4b4d1f274 --- /dev/null +++ b/controllers/legacy_suite_test.go @@ -0,0 +1,190 @@ +/* +Copyright 2020 The Flux authors + +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. +*/ + +package controllers + +import ( + "context" + "math/rand" + "net/http" + "os" + "path/filepath" + "testing" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "helm.sh/helm/v3/pkg/getter" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var k8sManager ctrl.Manager +var ginkgoTestEnv *envtest.Environment +var ginkgoTestStorage *Storage + +var examplePublicKey []byte +var examplePrivateKey []byte +var exampleCA []byte +var lctx context.Context +var cancel context.CancelFunc + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecsWithDefaultAndCustomReporters(t, + "Controller Suite", + []Reporter{printer.NewlineReporter{}}) +} + +var _ = BeforeSuite(func(done Done) { + logf.SetLogger( + zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)), + ) + lctx, cancel = context.WithCancel(ctx) + + By("bootstrapping test environment") + t := true + if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" { + ginkgoTestEnv = &envtest.Environment{ + UseExistingCluster: &t, + } + } else { + ginkgoTestEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + } + } + + var err error + cfg, err = ginkgoTestEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = sourcev1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + Expect(loadExampleKeys()).To(Succeed()) + + tmpStoragePath, err := os.MkdirTemp("", "source-controller-storage-") + Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage dir") + + ginkgoTestStorage, err = NewStorage(tmpStoragePath, "localhost:5050", time.Second*30) + Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage") + // serve artifacts from the filesystem, as done in main.go + fs := http.FileServer(http.Dir(tmpStoragePath)) + http.Handle("/", fs) + go http.ListenAndServe(":5050", nil) + + k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{ + MetricsBindAddress: "0", + Scheme: scheme.Scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + err = (&GitRepositoryReconciler{ + Client: k8sManager.GetClient(), + Scheme: scheme.Scheme, + Storage: ginkgoTestStorage, + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred(), "failed to setup GtRepositoryReconciler") + + err = (&HelmRepositoryReconciler{ + Client: k8sManager.GetClient(), + Scheme: scheme.Scheme, + Storage: ginkgoTestStorage, + Getters: getter.Providers{getter.Provider{ + Schemes: []string{"http", "https"}, + New: getter.NewHTTPGetter, + }}, + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred(), "failed to setup HelmRepositoryReconciler") + + err = (&HelmChartReconciler{ + Client: k8sManager.GetClient(), + Scheme: scheme.Scheme, + Storage: ginkgoTestStorage, + Getters: getter.Providers{getter.Provider{ + Schemes: []string{"http", "https"}, + New: getter.NewHTTPGetter, + }}, + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred(), "failed to setup HelmChartReconciler") + + go func() { + err = k8sManager.Start(lctx) + Expect(err).ToNot(HaveOccurred()) + }() + + k8sClient = k8sManager.GetClient() + Expect(k8sClient).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + defer GinkgoRecover() + cancel() + if ginkgoTestStorage != nil { + err := os.RemoveAll(ginkgoTestStorage.BasePath) + Expect(err).NotTo(HaveOccurred()) + } + err := ginkgoTestEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func loadExampleKeys() (err error) { + examplePublicKey, err = os.ReadFile("testdata/certs/server.pem") + if err != nil { + return err + } + examplePrivateKey, err = os.ReadFile("testdata/certs/server-key.pem") + if err != nil { + return err + } + exampleCA, err = os.ReadFile("testdata/certs/ca.pem") + return err +} + +var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") + +func randStringRunes(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} diff --git a/controllers/storage.go b/controllers/storage.go index a70150513..d66dd6734 100644 --- a/controllers/storage.go +++ b/controllers/storage.go @@ -53,7 +53,7 @@ type Storage struct { Timeout time.Duration `json:"timeout"` } -// NewStorage creates the storage helper for a given path and hostname +// NewStorage creates the storage helper for a given path and hostname. func NewStorage(basePath string, hostname string, timeout time.Duration) (*Storage, error) { if f, err := os.Stat(basePath); os.IsNotExist(err) || !f.IsDir() { return nil, fmt.Errorf("invalid dir path: %s", basePath) @@ -81,7 +81,11 @@ func (s Storage) SetArtifactURL(artifact *sourcev1.Artifact) { if artifact.Path == "" { return } - artifact.URL = fmt.Sprintf("http://%s/%s", s.Hostname, artifact.Path) + format := "http://%s/%s" + if strings.HasPrefix(s.Hostname, "http://") || strings.HasPrefix(s.Hostname, "https://") { + format = "%s/%s" + } + artifact.URL = fmt.Sprintf(format, s.Hostname, strings.TrimLeft(artifact.Path, "/")) } // SetHostname sets the hostname of the given URL string to the current Storage.Hostname and returns the result. diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 9520bcbb6..a1e09b69b 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -17,179 +17,128 @@ limitations under the License. package controllers import ( - "context" + "fmt" "math/rand" - "net/http" "os" "path/filepath" "testing" "time" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "helm.sh/helm/v3/pkg/getter" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - "sigs.k8s.io/controller-runtime/pkg/envtest/printer" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/fluxcd/pkg/runtime/controller" + "github.com/fluxcd/pkg/runtime/testenv" + "github.com/fluxcd/pkg/testserver" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" // +kubebuilder:scaffold:imports ) -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. +// These tests make use of plain Go using Gomega for assertions. +// At the beginning of every (sub)test Gomega can be initialized +// using gomega.NewWithT. +// Refer to http://onsi.github.io/gomega/ to learn more about +// Gomega. -var cfg *rest.Config -var k8sClient client.Client -var k8sManager ctrl.Manager -var testEnv *envtest.Environment -var storage *Storage +const ( + timeout = 10 * time.Second + interval = 1 * time.Second +) -var examplePublicKey []byte -var examplePrivateKey []byte -var exampleCA []byte -var ctx context.Context -var cancel context.CancelFunc +var ( + testEnv *testenv.Environment + testStorage *Storage + testServer *testserver.ArtifactServer + testMetricsH controller.Metrics + ctx = ctrl.SetupSignalHandler() +) -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) +var ( + tlsPublicKey []byte + tlsPrivateKey []byte + tlsCA []byte +) - RunSpecsWithDefaultAndCustomReporters(t, - "Controller Suite", - []Reporter{printer.NewlineReporter{}}) +func init() { + rand.Seed(time.Now().UnixNano()) } -var _ = BeforeSuite(func(done Done) { - logf.SetLogger( - zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)), - ) - ctx, cancel = context.WithCancel(context.TODO()) - - By("bootstrapping test environment") - t := true - if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" { - testEnv = &envtest.Environment{ - UseExistingCluster: &t, - } - } else { - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, - } - } +func TestMain(m *testing.M) { + initTestTLS() + + utilruntime.Must(sourcev1.AddToScheme(scheme.Scheme)) + + testEnv = testenv.New(testenv.WithCRDPath(filepath.Join("..", "config", "crd", "bases"))) var err error - cfg, err = testEnv.Start() - Expect(err).ToNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - - err = sourcev1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - err = sourcev1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - err = sourcev1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - - Expect(loadExampleKeys()).To(Succeed()) - - tmpStoragePath, err := os.MkdirTemp("", "source-controller-storage-") - Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage dir") - - storage, err = NewStorage(tmpStoragePath, "localhost:5050", time.Second*30) - Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage") - // serve artifacts from the filesystem, as done in main.go - fs := http.FileServer(http.Dir(tmpStoragePath)) - http.Handle("/", fs) - go http.ListenAndServe(":5050", nil) - - k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme.Scheme, - }) - Expect(err).ToNot(HaveOccurred()) - - err = (&GitRepositoryReconciler{ - Client: k8sManager.GetClient(), - Scheme: scheme.Scheme, - Storage: storage, - }).SetupWithManager(k8sManager) - Expect(err).ToNot(HaveOccurred(), "failed to setup GtRepositoryReconciler") - - err = (&HelmRepositoryReconciler{ - Client: k8sManager.GetClient(), - Scheme: scheme.Scheme, - Storage: storage, - Getters: getter.Providers{getter.Provider{ - Schemes: []string{"http", "https"}, - New: getter.NewHTTPGetter, - }}, - }).SetupWithManager(k8sManager) - Expect(err).ToNot(HaveOccurred(), "failed to setup HelmRepositoryReconciler") - - err = (&HelmChartReconciler{ - Client: k8sManager.GetClient(), - Scheme: scheme.Scheme, - Storage: storage, - Getters: getter.Providers{getter.Provider{ - Schemes: []string{"http", "https"}, - New: getter.NewHTTPGetter, - }}, - }).SetupWithManager(k8sManager) - Expect(err).ToNot(HaveOccurred(), "failed to setup HelmChartReconciler") + testServer, err = testserver.NewTempArtifactServer() + if err != nil { + panic(fmt.Sprintf("Failed to create a temporary storage server: %v", err)) + } + fmt.Println("Starting the test storage server") + testServer.Start() + + testStorage, err = newTestStorage(testServer.HTTPServer) + if err != nil { + panic(fmt.Sprintf("Failed to create a test storage: %v", err)) + } + + testMetricsH = controller.MustMakeMetrics(testEnv) + + //if err := (&GitRepositoryReconciler{ + // Client: testEnv, + // Metrics: testMetricsH, + // Storage: testStorage, + //}).SetupWithManager(testEnv); err != nil { + // panic(fmt.Sprintf("Failed to start GitRepositoryReconciler: %v", err)) + //} go func() { - defer GinkgoRecover() - err = k8sManager.Start(ctx) - Expect(err).ToNot(HaveOccurred()) + fmt.Println("Starting the test environment") + if err := testEnv.Start(ctx); err != nil { + panic(fmt.Sprintf("Failed to start the test environment manager: %v", err)) + } }() + <-testEnv.Manager.Elected() - k8sClient = k8sManager.GetClient() - Expect(k8sClient).ToNot(BeNil()) + code := m.Run() - close(done) -}, 60) + fmt.Println("Stopping the test environment") + if err := testEnv.Stop(); err != nil { + panic(fmt.Sprintf("Failed to stop the test environment: %v", err)) + } -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - if storage != nil { - err := os.RemoveAll(storage.BasePath) - Expect(err).NotTo(HaveOccurred()) + fmt.Println("Stopping the storage server") + testServer.Stop() + if err := os.RemoveAll(testServer.Root()); err != nil { + panic(fmt.Sprintf("Failed to remove storage server dir: %v", err)) } - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) -func init() { - rand.Seed(time.Now().UnixNano()) + os.Exit(code) } -func loadExampleKeys() (err error) { - examplePublicKey, err = os.ReadFile("testdata/certs/server.pem") +func initTestTLS() { + var err error + tlsPublicKey, err = os.ReadFile("testdata/certs/server.pem") + if err != nil { + panic(err) + } + tlsPrivateKey, err = os.ReadFile("testdata/certs/server-key.pem") if err != nil { - return err + panic(err) } - examplePrivateKey, err = os.ReadFile("testdata/certs/server-key.pem") + tlsCA, err = os.ReadFile("testdata/certs/ca.pem") if err != nil { - return err + panic(err) } - exampleCA, err = os.ReadFile("testdata/certs/ca.pem") - return err } -var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") - -func randStringRunes(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letterRunes[rand.Intn(len(letterRunes))] +func newTestStorage(s *testserver.HTTPServer) (*Storage, error) { + storage, err := NewStorage(s.Root(), s.URL(), timeout) + if err != nil { + return nil, err } - return string(b) + return storage, nil } diff --git a/docs/api/source.md b/docs/api/source.md index 8cc4f8ddf..4a506c472 100644 --- a/docs/api/source.md +++ b/docs/api/source.md @@ -313,10 +313,8 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference (Optional)

The secret name containing the Git credentials. -For HTTPS repositories the secret must contain username and password -fields. -For SSH repositories the secret must contain identity, identity.pub and -known_hosts fields.

+For HTTPS repositories the secret must contain username and password fields. +For SSH repositories the secret must contain ‘identity’, ‘identity.pub’ and ‘known_hosts’ fields.

@@ -372,7 +370,7 @@ GitRepositoryVerification (Optional) -

Verify OpenPGP signature for the Git commit HEAD points to.

+

Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to.

@@ -384,9 +382,8 @@ string (Optional) -

Ignore overrides the set of excluded patterns in the .sourceignore format -(which is the same as .gitignore). If not provided, a default will be used, -consult the documentation for your version to find out what those are.

+

Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore). +If not provided, a default will be used, consult the documentation for your version to find out what those are.

@@ -398,7 +395,8 @@ bool (Optional) -

This flag tells the controller to suspend the reconciliation of this source.

+

Suspend tells the controller to suspend the reconciliation of this source. +This flag tells the controller to suspend the reconciliation of this source.

@@ -423,8 +421,7 @@ bool (Optional) -

When enabled, after the clone is created, initializes all submodules within, -using their default settings. +

When enabled, after the clone is created, initializes all submodules within, using their default settings. This option is available only when using the ‘go-git’ GitImplementation.

@@ -438,7 +435,8 @@ This option is available only when using the ‘go-git’ GitImplementat -

Extra git repositories to map into the repository

+

Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for +this resource.

@@ -1349,10 +1347,8 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference (Optional)

The secret name containing the Git credentials. -For HTTPS repositories the secret must contain username and password -fields. -For SSH repositories the secret must contain identity, identity.pub and -known_hosts fields.

+For HTTPS repositories the secret must contain username and password fields. +For SSH repositories the secret must contain ‘identity’, ‘identity.pub’ and ‘known_hosts’ fields.

@@ -1408,7 +1404,7 @@ GitRepositoryVerification (Optional) -

Verify OpenPGP signature for the Git commit HEAD points to.

+

Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to.

@@ -1420,9 +1416,8 @@ string (Optional) -

Ignore overrides the set of excluded patterns in the .sourceignore format -(which is the same as .gitignore). If not provided, a default will be used, -consult the documentation for your version to find out what those are.

+

Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore). +If not provided, a default will be used, consult the documentation for your version to find out what those are.

@@ -1434,7 +1429,8 @@ bool (Optional) -

This flag tells the controller to suspend the reconciliation of this source.

+

Suspend tells the controller to suspend the reconciliation of this source. +This flag tells the controller to suspend the reconciliation of this source.

@@ -1459,8 +1455,7 @@ bool (Optional) -

When enabled, after the clone is created, initializes all submodules within, -using their default settings. +

When enabled, after the clone is created, initializes all submodules within, using their default settings. This option is available only when using the ‘go-git’ GitImplementation.

@@ -1474,7 +1469,8 @@ This option is available only when using the ‘go-git’ GitImplementat -

Extra git repositories to map into the repository

+

Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for +this resource.

@@ -1547,8 +1543,7 @@ string (Optional) -

URL is the download link for the artifact output of the last repository -sync.

+

URL is the download link for the artifact output of the last repository sync.

@@ -1623,7 +1618,7 @@ string -

Mode describes what git object should be verified, currently (‘head’).

+

Mode describes what Git object should be verified, currently (‘head’).

@@ -1636,7 +1631,7 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference -

The secret name containing the public keys of all trusted Git authors.

+

SecretRef containing the public keys of all trusted Git authors.

diff --git a/go.mod b/go.mod index 160c278ae..bf5ac6f8e 100644 --- a/go.mod +++ b/go.mod @@ -10,13 +10,14 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 github.com/cyphar/filepath-securejoin v0.2.2 github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b - github.com/fluxcd/pkg/apis/meta v0.10.2 + github.com/fluxcd/pkg/apis/meta v0.11.0-rc.3 github.com/fluxcd/pkg/gittestserver v0.5.0 github.com/fluxcd/pkg/gitutil v0.1.0 github.com/fluxcd/pkg/helmtestserver v0.4.0 github.com/fluxcd/pkg/lockedfile v0.1.0 - github.com/fluxcd/pkg/runtime v0.12.3 + github.com/fluxcd/pkg/runtime v0.13.0-rc.7 github.com/fluxcd/pkg/ssh v0.2.0 + github.com/fluxcd/pkg/testserver v0.2.0 github.com/fluxcd/pkg/untar v0.1.0 github.com/fluxcd/pkg/version v0.1.0 github.com/fluxcd/source-controller/api v0.20.1 @@ -77,7 +78,6 @@ require ( github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/color v1.7.0 // indirect github.com/fluxcd/pkg/apis/acl v0.0.3 // indirect - github.com/fluxcd/pkg/testserver v0.1.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-git/gcfg v1.5.0 // indirect @@ -174,8 +174,8 @@ require ( golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9 // indirect - golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect - golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 // indirect golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect diff --git a/go.sum b/go.sum index 5581610f4..69a72d4dd 100644 --- a/go.sum +++ b/go.sum @@ -298,8 +298,8 @@ github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8S github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fluxcd/pkg/apis/acl v0.0.3 h1:Lw0ZHdpnO4G7Zy9KjrzwwBmDZQuy4qEjaU/RvA6k1lc= github.com/fluxcd/pkg/apis/acl v0.0.3/go.mod h1:XPts6lRJ9C9fIF9xVWofmQwftvhY25n1ps7W9xw0XLU= -github.com/fluxcd/pkg/apis/meta v0.10.2 h1:pnDBBEvfs4HaKiVAYgz+e/AQ8dLvcgmVfSeBroZ/KKI= -github.com/fluxcd/pkg/apis/meta v0.10.2/go.mod h1:KQ2er9xa6koy7uoPMZjIjNudB5p4tXs+w0GO6fRcy7I= +github.com/fluxcd/pkg/apis/meta v0.11.0-rc.3 h1:YY6RlaHIMXawgEOJhJbSrm4NpD9fJTCWFGKgtNfQ0/g= +github.com/fluxcd/pkg/apis/meta v0.11.0-rc.3/go.mod h1:ki5wJE4nuFOZt78q0RSYkrKwINgIBPynuswZhnTOSoI= github.com/fluxcd/pkg/gittestserver v0.5.0 h1:pPdaz7pUsukt4eQ+xQeNwoypOXGGOHFHnPjIHQAv0tE= github.com/fluxcd/pkg/gittestserver v0.5.0/go.mod h1:mFEF/Xrg+CjQH4VFCRCou2qZmhWKo7EYcjr7MIoX6+s= github.com/fluxcd/pkg/gitutil v0.1.0 h1:VO3kJY/CKOCO4ysDNqfdpTg04icAKBOSb3lbR5uE/IE= @@ -308,12 +308,14 @@ github.com/fluxcd/pkg/helmtestserver v0.4.0 h1:RT0G5buw5qrzEfIIH0fklppIvPAaQF//p github.com/fluxcd/pkg/helmtestserver v0.4.0/go.mod h1:JOI9f3oXUFIWmMKWMBan7FjglAU+fRTO/sPPV/Kj3gQ= github.com/fluxcd/pkg/lockedfile v0.1.0 h1:YsYFAkd6wawMCcD74ikadAKXA4s2sukdxrn7w8RB5eo= github.com/fluxcd/pkg/lockedfile v0.1.0/go.mod h1:EJLan8t9MiOcgTs8+puDjbE6I/KAfHbdvIy9VUgIjm8= -github.com/fluxcd/pkg/runtime v0.12.3 h1:h21AZ3YG5MAP7DxFF9hfKrP+vFzys2L7CkUbPFjbP/0= -github.com/fluxcd/pkg/runtime v0.12.3/go.mod h1:imJ2xYy/d4PbSinX2IefmZk+iS2c1P5fY0js8mCE4SM= +github.com/fluxcd/pkg/runtime v0.13.0-rc.6/go.mod h1:4oKUO19TeudXrnCRnxCfMSS7EQTYpYlgfXwlQuDJ/Eg= +github.com/fluxcd/pkg/runtime v0.13.0-rc.7 h1:snESiRwjrmNchIBautlxnXn8HzmeDEnS3PsMbP2fyeg= +github.com/fluxcd/pkg/runtime v0.13.0-rc.7/go.mod h1:uGPudgMUNC3wu7Zoh6AgJM8WSH3VpmnzjrwkVb86d3Y= github.com/fluxcd/pkg/ssh v0.2.0 h1:e9V+HReOL7czm7edVzYS1e+CnFKz1/kHiUNfLRpBdH8= github.com/fluxcd/pkg/ssh v0.2.0/go.mod h1:EpQC7Ztdlbi8S/dlYXqVDZtHtLpN3FNl3N6zWujVzbA= -github.com/fluxcd/pkg/testserver v0.1.0 h1:nOYgM1HYFZNNSUFykuWDmrsxj4jQxUCvmLHWOQeqmyA= github.com/fluxcd/pkg/testserver v0.1.0/go.mod h1:fvt8BHhXw6c1+CLw1QFZxcQprlcXzsrL4rzXaiGM+Iw= +github.com/fluxcd/pkg/testserver v0.2.0 h1:Mj0TapmKaywI6Fi5wvt1LAZpakUHmtzWQpJNKQ0Krt4= +github.com/fluxcd/pkg/testserver v0.2.0/go.mod h1:bgjjydkXsZTeFzjz9Cr4heGANr41uTB1Aj1Q5qzuYVk= github.com/fluxcd/pkg/untar v0.1.0 h1:k97V/xV5hFrAkIkVPuv5AVhyxh1ZzzAKba/lbDfGo6o= github.com/fluxcd/pkg/untar v0.1.0/go.mod h1:aGswNyzB1mlz/T/kpOS58mITBMxMKc9tlJBH037A2HY= github.com/fluxcd/pkg/version v0.1.0 h1:v+SmCanmCB5Tj2Cx9TXlj+kNRfPGbAvirkeqsp7ZEAQ= @@ -1112,8 +1114,9 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/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 h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/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= @@ -1213,8 +1216,9 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc 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-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 h1:M69LAlWZCshgp0QSzyDcSsSIejIEeuaCVpmwcKwyLMk= golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 h1:WecRHqgE09JBkh/584XIE6PMz5KKE/vER4izNUi30AQ= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/main.go b/main.go index 67f00a920..18409ce85 100644 --- a/main.go +++ b/main.go @@ -123,18 +123,6 @@ func main() { helm.MaxChartSize = helmChartLimit helm.MaxChartFileSize = helmChartFileLimit - var eventRecorder *events.Recorder - if eventsAddr != "" { - var err error - if eventRecorder, err = events.NewRecorder(eventsAddr, controllerName); err != nil { - setupLog.Error(err, "unable to create event recorder") - os.Exit(1) - } - } - - metricsRecorder := metrics.NewRecorder() - crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) - watchNamespace := "" if !watchAllNamespaces { watchNamespace = os.Getenv("RUNTIME_NAMESPACE") @@ -163,6 +151,18 @@ func main() { probes.SetupChecks(mgr, setupLog) pprof.SetupHandlers(mgr, setupLog) + var eventRecorder *events.Recorder + if eventsAddr != "" { + var err error + if eventRecorder, err = events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName); err != nil { + setupLog.Error(err, "unable to create event recorder") + os.Exit(1) + } + } + + metricsRecorder := metrics.NewRecorder() + crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) + if storageAdvAddr == "" { storageAdvAddr = determineAdvStorageAddr(storageAddr, setupLog) } From 91233778ecc03b537263e51b762ec4cf67e629f9 Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Fri, 30 Jul 2021 12:54:45 +0200 Subject: [PATCH 03/10] Introduce `artifactSet` to replace `hasArtifactUpdated` NOTE: Remove `hasArtifactUpdated` in the future once it's no longer used. Signed-off-by: Hidde Beydals --- controllers/artifact.go | 38 ++++++++++++++++++++++++ controllers/artifact_test.go | 56 ++++++++++++++++++++++-------------- 2 files changed, 73 insertions(+), 21 deletions(-) diff --git a/controllers/artifact.go b/controllers/artifact.go index 0e16fd03c..fa21bd0a4 100644 --- a/controllers/artifact.go +++ b/controllers/artifact.go @@ -1,9 +1,47 @@ +/* +Copyright 2021 The Flux authors + +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. +*/ + package controllers import sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" +type artifactSet []*sourcev1.Artifact + +// Diff returns true if any of the revisions in the artifactSet does not match any of the given artifacts. +func (s artifactSet) Diff(set artifactSet) bool { + if len(s) != len(set) { + return true + } + +outer: + for _, j := range s { + for _, k := range set { + if k.HasRevision(j.Revision) { + continue outer + } + } + return true + } + return false +} + // hasArtifactUpdated returns true if any of the revisions in the current artifacts // does not match any of the artifacts in the updated artifacts +// NOTE: artifactSet is a replacement for this. Remove this once it's not used +// anywhere. func hasArtifactUpdated(current []*sourcev1.Artifact, updated []*sourcev1.Artifact) bool { if len(current) != len(updated) { return true diff --git a/controllers/artifact_test.go b/controllers/artifact_test.go index 959661615..935c93bf7 100644 --- a/controllers/artifact_test.go +++ b/controllers/artifact_test.go @@ -1,26 +1,40 @@ +/* +Copyright 2021 The Flux authors + +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. +*/ + package controllers import ( "testing" - - sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" ) -func TestHasUpdated(t *testing.T) { +func Test_artifactSet_Diff(t *testing.T) { tests := []struct { name string - current []*sourcev1.Artifact - updated []*sourcev1.Artifact + current artifactSet + updated artifactSet expected bool }{ { - name: "not updated single", - current: []*sourcev1.Artifact{ + name: "one artifact, no diff", + current: artifactSet{ { Revision: "foo", }, }, - updated: []*sourcev1.Artifact{ + updated: artifactSet{ { Revision: "foo", }, @@ -28,13 +42,13 @@ func TestHasUpdated(t *testing.T) { expected: false, }, { - name: "updated single", - current: []*sourcev1.Artifact{ + name: "one artifact, diff", + current: artifactSet{ { Revision: "foo", }, }, - updated: []*sourcev1.Artifact{ + updated: artifactSet{ { Revision: "bar", }, @@ -42,8 +56,8 @@ func TestHasUpdated(t *testing.T) { expected: true, }, { - name: "not updated multiple", - current: []*sourcev1.Artifact{ + name: "multiple artifacts, no diff", + current: artifactSet{ { Revision: "foo", }, @@ -51,7 +65,7 @@ func TestHasUpdated(t *testing.T) { Revision: "bar", }, }, - updated: []*sourcev1.Artifact{ + updated: artifactSet{ { Revision: "foo", }, @@ -62,8 +76,8 @@ func TestHasUpdated(t *testing.T) { expected: false, }, { - name: "updated multiple", - current: []*sourcev1.Artifact{ + name: "multiple artifacts, diff", + current: artifactSet{ { Revision: "foo", }, @@ -71,7 +85,7 @@ func TestHasUpdated(t *testing.T) { Revision: "bar", }, }, - updated: []*sourcev1.Artifact{ + updated: artifactSet{ { Revision: "foo", }, @@ -82,8 +96,8 @@ func TestHasUpdated(t *testing.T) { expected: true, }, { - name: "updated different artifact count", - current: []*sourcev1.Artifact{ + name: "different artifact count", + current: artifactSet{ { Revision: "foo", }, @@ -91,7 +105,7 @@ func TestHasUpdated(t *testing.T) { Revision: "bar", }, }, - updated: []*sourcev1.Artifact{ + updated: artifactSet{ { Revision: "foo", }, @@ -101,7 +115,7 @@ func TestHasUpdated(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := hasArtifactUpdated(tt.current, tt.updated) + result := tt.current.Diff(tt.updated) if result != tt.expected { t.Errorf("Archive() result = %v, wantResult %v", result, tt.expected) } From 346f72f505ea99bb9b4d7eaf1f00a9ddb3ad284e Mon Sep 17 00:00:00 2001 From: Sunny Date: Wed, 24 Nov 2021 21:49:03 +0530 Subject: [PATCH 04/10] source: Add `GetRequeueAfter` The problem with `GetInterval()` was that the returned type was of `metav1.Duration`, while almost anywhere it was used, a type of `time.Duration` was requested. The result of this was that we had to call `GetInterval().Duration` all the time, which would become a bit cumbersome after awhile. To prevent this, we introduce a new `GetRequeueAfter() time.Duration` method, which both results the right type, and bears a name that is easier to remember where the value is used most; while setting the `Result.RequeueAfter` during reconcile operations. The introduction of this method deprecates `GetInterval()`, which should be removed in a future MINOR release. Signed-off-by: Sunny Co-authored-by: Hidde Beydals --- api/v1beta1/bucket_types.go | 8 ++++++++ api/v1beta1/gitrepository_types.go | 8 ++++++++ api/v1beta1/helmchart_types.go | 8 ++++++++ api/v1beta1/helmrepository_types.go | 8 ++++++++ api/v1beta1/source.go | 5 +++++ 5 files changed, 37 insertions(+) diff --git a/api/v1beta1/bucket_types.go b/api/v1beta1/bucket_types.go index e4b4d6cdc..b03c03a10 100644 --- a/api/v1beta1/bucket_types.go +++ b/api/v1beta1/bucket_types.go @@ -17,6 +17,8 @@ limitations under the License. package v1beta1 import ( + "time" + apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -169,7 +171,13 @@ func (in *Bucket) SetConditions(conditions []metav1.Condition) { in.Status.Conditions = conditions } +// GetRequeueAfter returns the duration after which the source must be reconciled again. +func (in Bucket) GetRequeueAfter() time.Duration { + return in.Spec.Interval.Duration +} + // GetInterval returns the interval at which the source is reconciled. +// Deprecated: use GetRequeueAfter instead. func (in Bucket) GetInterval() metav1.Duration { return in.Spec.Interval } diff --git a/api/v1beta1/gitrepository_types.go b/api/v1beta1/gitrepository_types.go index c2cfc8e4c..d4dcbbe85 100644 --- a/api/v1beta1/gitrepository_types.go +++ b/api/v1beta1/gitrepository_types.go @@ -17,6 +17,8 @@ limitations under the License. package v1beta1 import ( + "time" + apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -260,7 +262,13 @@ func (in *GitRepository) SetConditions(conditions []metav1.Condition) { in.Status.Conditions = conditions } +// GetRequeueAfter returns the duration after which the source must be reconciled again. +func (in GitRepository) GetRequeueAfter() time.Duration { + return in.Spec.Interval.Duration +} + // GetInterval returns the interval at which the source is reconciled. +// Deprecated: use GetRequeueAfter instead. func (in GitRepository) GetInterval() metav1.Duration { return in.Spec.Interval } diff --git a/api/v1beta1/helmchart_types.go b/api/v1beta1/helmchart_types.go index 78a20852b..76ed4914b 100644 --- a/api/v1beta1/helmchart_types.go +++ b/api/v1beta1/helmchart_types.go @@ -17,6 +17,8 @@ limitations under the License. package v1beta1 import ( + "time" + apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -196,7 +198,13 @@ func (in *HelmChart) SetConditions(conditions []metav1.Condition) { in.Status.Conditions = conditions } +// GetRequeueAfter returns the duration after which the source must be reconciled again. +func (in HelmChart) GetRequeueAfter() time.Duration { + return in.Spec.Interval.Duration +} + // GetInterval returns the interval at which the source is reconciled. +// Deprecated: use GetRequeueAfter instead. func (in HelmChart) GetInterval() metav1.Duration { return in.Spec.Interval } diff --git a/api/v1beta1/helmrepository_types.go b/api/v1beta1/helmrepository_types.go index 65b924042..cd585f2d0 100644 --- a/api/v1beta1/helmrepository_types.go +++ b/api/v1beta1/helmrepository_types.go @@ -17,6 +17,8 @@ limitations under the License. package v1beta1 import ( + "time" + apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -157,7 +159,13 @@ func (in *HelmRepository) SetConditions(conditions []metav1.Condition) { in.Status.Conditions = conditions } +// GetRequeueAfter returns the duration after which the source must be reconciled again. +func (in HelmRepository) GetRequeueAfter() time.Duration { + return in.Spec.Interval.Duration +} + // GetInterval returns the interval at which the source is reconciled. +// Deprecated: use GetRequeueAfter instead. func (in HelmRepository) GetInterval() metav1.Duration { return in.Spec.Interval } diff --git a/api/v1beta1/source.go b/api/v1beta1/source.go index 5c10d00fc..f22780dd1 100644 --- a/api/v1beta1/source.go +++ b/api/v1beta1/source.go @@ -17,6 +17,8 @@ limitations under the License. package v1beta1 import ( + "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -29,9 +31,12 @@ const ( // Source interface must be supported by all API types. // +k8s:deepcopy-gen=false type Source interface { + // GetRequeueAfter returns the duration after which the source must be reconciled again. + GetRequeueAfter() time.Duration // GetArtifact returns the latest artifact from the source if present in the // status sub-resource. GetArtifact() *Artifact // GetInterval returns the interval at which the source is updated. + // Deprecated: use GetRequeueAfter instead. GetInterval() metav1.Duration } From 2fabc98ad92cbb0931dfda365044d13be1404ec5 Mon Sep 17 00:00:00 2001 From: Sunny Date: Wed, 24 Nov 2021 22:30:20 +0530 Subject: [PATCH 05/10] Use new events and metrics helpers in main.go Signed-off-by: Sunny --- main.go | 64 +++++++++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/main.go b/main.go index 18409ce85..514cc34c4 100644 --- a/main.go +++ b/main.go @@ -33,13 +33,12 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" - crtlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" "github.com/fluxcd/pkg/runtime/client" + helper "github.com/fluxcd/pkg/runtime/controller" "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/runtime/leaderelection" "github.com/fluxcd/pkg/runtime/logger" - "github.com/fluxcd/pkg/runtime/metrics" "github.com/fluxcd/pkg/runtime/pprof" "github.com/fluxcd/pkg/runtime/probes" @@ -114,6 +113,7 @@ func main() { clientOptions.BindFlags(flag.CommandLine) logOptions.BindFlags(flag.CommandLine) leaderElectionOptions.BindFlags(flag.CommandLine) + flag.Parse() ctrl.SetLogger(logger.NewLogger(logOptions)) @@ -152,16 +152,12 @@ func main() { pprof.SetupHandlers(mgr, setupLog) var eventRecorder *events.Recorder - if eventsAddr != "" { - var err error - if eventRecorder, err = events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName); err != nil { - setupLog.Error(err, "unable to create event recorder") - os.Exit(1) - } + if eventRecorder, err = events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName); err != nil { + setupLog.Error(err, "unable to create event recorder") + os.Exit(1) } - metricsRecorder := metrics.NewRecorder() - crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) + metricsH := helper.MustMakeMetrics(mgr) if storageAdvAddr == "" { storageAdvAddr = determineAdvStorageAddr(storageAddr, setupLog) @@ -169,12 +165,11 @@ func main() { storage := mustInitStorage(storagePath, storageAdvAddr, setupLog) if err = (&controllers.GitRepositoryReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Storage: storage, - EventRecorder: mgr.GetEventRecorderFor(controllerName), - ExternalEventRecorder: eventRecorder, - MetricsRecorder: metricsRecorder, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Storage: storage, + EventRecorder: eventRecorder, + MetricsRecorder: metricsH.MetricsRecorder, }).SetupWithManagerAndOptions(mgr, controllers.GitRepositoryReconcilerOptions{ MaxConcurrentReconciles: concurrent, DependencyRequeueInterval: requeueDependency, @@ -183,13 +178,12 @@ func main() { os.Exit(1) } if err = (&controllers.HelmRepositoryReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Storage: storage, - Getters: getters, - EventRecorder: mgr.GetEventRecorderFor(controllerName), - ExternalEventRecorder: eventRecorder, - MetricsRecorder: metricsRecorder, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Storage: storage, + Getters: getters, + EventRecorder: eventRecorder, + MetricsRecorder: metricsH.MetricsRecorder, }).SetupWithManagerAndOptions(mgr, controllers.HelmRepositoryReconcilerOptions{ MaxConcurrentReconciles: concurrent, }); err != nil { @@ -197,13 +191,12 @@ func main() { os.Exit(1) } if err = (&controllers.HelmChartReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Storage: storage, - Getters: getters, - EventRecorder: mgr.GetEventRecorderFor(controllerName), - ExternalEventRecorder: eventRecorder, - MetricsRecorder: metricsRecorder, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Storage: storage, + Getters: getters, + EventRecorder: eventRecorder, + MetricsRecorder: metricsH.MetricsRecorder, }).SetupWithManagerAndOptions(mgr, controllers.HelmChartReconcilerOptions{ MaxConcurrentReconciles: concurrent, }); err != nil { @@ -211,12 +204,11 @@ func main() { os.Exit(1) } if err = (&controllers.BucketReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Storage: storage, - EventRecorder: mgr.GetEventRecorderFor(controllerName), - ExternalEventRecorder: eventRecorder, - MetricsRecorder: metricsRecorder, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Storage: storage, + EventRecorder: eventRecorder, + MetricsRecorder: metricsH.MetricsRecorder, }).SetupWithManagerAndOptions(mgr, controllers.BucketReconcilerOptions{ MaxConcurrentReconciles: concurrent, }); err != nil { From 7853338f4cde26e27c6a7aa4b59ed2a635a3d7a1 Mon Sep 17 00:00:00 2001 From: Sunny Date: Thu, 25 Nov 2021 00:03:11 +0530 Subject: [PATCH 06/10] Move Artifact conditions to conditions Also, introduce FetchFailedCondition for generic fetch failures. Signed-off-by: Sunny Co-authored-by: Hidde Beydals --- api/v1beta1/condition_types.go | 16 ++++++++++++++++ api/v1beta1/gitrepository_types.go | 8 -------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/api/v1beta1/condition_types.go b/api/v1beta1/condition_types.go index 4077a2ab6..1a9db62ad 100644 --- a/api/v1beta1/condition_types.go +++ b/api/v1beta1/condition_types.go @@ -18,6 +18,22 @@ package v1beta1 const SourceFinalizer = "finalizers.fluxcd.io" +const ( + // ArtifactUnavailableCondition indicates there is no Artifact available for the Source. + // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. + ArtifactUnavailableCondition string = "ArtifactUnavailable" + + // ArtifactOutdatedCondition indicates the current Artifact of the Source is outdated. + // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. + ArtifactOutdatedCondition string = "ArtifactOutdated" + + // FetchFailedCondition indicates a transient or persistent fetch failure of an upstream Source. + // If True, observations on the upstream Source revision may be impossible, and the Artifact available for the + // Source may be outdated. + // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. + FetchFailedCondition string = "FetchFailed" +) + const ( // URLInvalidReason represents the fact that a given source has an invalid URL. URLInvalidReason string = "URLInvalid" diff --git a/api/v1beta1/gitrepository_types.go b/api/v1beta1/gitrepository_types.go index d4dcbbe85..145ac6bb0 100644 --- a/api/v1beta1/gitrepository_types.go +++ b/api/v1beta1/gitrepository_types.go @@ -38,10 +38,6 @@ const ( ) const ( - // ArtifactUnavailableCondition indicates there is no Artifact available for the Source. - // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. - ArtifactUnavailableCondition string = "ArtifactUnavailable" - // CheckoutFailedCondition indicates a transient or persistent checkout failure. If True, observations on the // upstream Source revision are not possible, and the Artifact available for the Source may be outdated. // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. @@ -55,10 +51,6 @@ const ( // exist, or does not have an Artifact. // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. IncludeUnavailableCondition string = "IncludeUnavailable" - - // ArtifactOutdatedCondition indicates the current Artifact of the Source is outdated. - // This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True. - ArtifactOutdatedCondition string = "ArtifactOutdated" ) // GitRepositorySpec defines the desired state of a Git repository. From 2dbb6bc85405428b8c47453aa31801175c0177f4 Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 26 Nov 2021 00:18:08 +0530 Subject: [PATCH 07/10] Add gomega matcher for artifact Signed-off-by: Sunny Co-authored-by: Hidde Beydals --- controllers/artifact_matchers_test.go | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 controllers/artifact_matchers_test.go diff --git a/controllers/artifact_matchers_test.go b/controllers/artifact_matchers_test.go new file mode 100644 index 000000000..63ff81ced --- /dev/null +++ b/controllers/artifact_matchers_test.go @@ -0,0 +1,67 @@ +/* +Copyright 2021 The Flux authors + +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. +*/ + +package controllers + +import ( + "fmt" + + sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/types" +) + +// MatchArtifact returns a custom matcher to check equality of a v1beta1.Artifact, the timestamp and URL are ignored. +func MatchArtifact(expected *sourcev1.Artifact) types.GomegaMatcher { + return &matchArtifact{ + expected: expected, + } +} + +type matchArtifact struct { + expected *sourcev1.Artifact +} + +func (m matchArtifact) Match(actual interface{}) (success bool, err error) { + actualArtifact, ok := actual.(*sourcev1.Artifact) + if !ok { + return false, fmt.Errorf("actual should be a pointer to an Artifact") + } + + if ok, _ := BeNil().Match(m.expected); ok { + return BeNil().Match(actual) + } + + if ok, err = Equal(m.expected.Path).Match(actualArtifact.Path); !ok { + return ok, err + } + if ok, err = Equal(m.expected.Revision).Match(actualArtifact.Revision); !ok { + return ok, err + } + if ok, err = Equal(m.expected.Checksum).Match(actualArtifact.Checksum); !ok { + return ok, err + } + + return ok, err +} + +func (m matchArtifact) FailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto match\n\t%#v\n", actual, m.expected) +} + +func (m matchArtifact) NegatedFailureMessage(actual interface{}) (message string) { + return fmt.Sprintf("expected\n\t%#v\nto not match\n\t%#v\n", actual, m.expected) +} From 46360a5dceb7345d6f9f3d22ddcd862ce3f54f27 Mon Sep 17 00:00:00 2001 From: Sunny Date: Thu, 20 Jan 2022 01:09:39 +0530 Subject: [PATCH 08/10] api: Embed runtime.Object in Source interface Embedding runtime.Object in Source interface makes the Source type more useful to interact with k8s API machinery. Signed-off-by: Sunny --- api/v1beta1/source.go | 2 ++ api/v1beta1/zz_generated.deepcopy.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/api/v1beta1/source.go b/api/v1beta1/source.go index f22780dd1..1bf8bd975 100644 --- a/api/v1beta1/source.go +++ b/api/v1beta1/source.go @@ -20,6 +20,7 @@ import ( "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ) const ( @@ -31,6 +32,7 @@ const ( // Source interface must be supported by all API types. // +k8s:deepcopy-gen=false type Source interface { + runtime.Object // GetRequeueAfter returns the duration after which the source must be reconciled again. GetRequeueAfter() time.Duration // GetArtifact returns the latest artifact from the source if present in the diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index d5e4f4892..79c2bdbb7 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -25,7 +25,7 @@ import ( "github.com/fluxcd/pkg/apis/acl" "github.com/fluxcd/pkg/apis/meta" "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. From affc27ee4539495a8e73fddcee2a637f08a99b37 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 19 Dec 2021 19:41:32 +0530 Subject: [PATCH 09/10] Add internal packages error and reconcile - internal/error - Contains internal error type used across the source-controller reconcilers. - internal/reconcile - Contains helper abstractions for the controller-runtime reconcile Result type and functions to interact with the abstractions. Signed-off-by: Sunny --- internal/error/error.go | 56 ++++++++++ internal/reconcile/reconcile.go | 148 +++++++++++++++++++++++++++ internal/reconcile/reconcile_test.go | 47 +++++++++ 3 files changed, 251 insertions(+) create mode 100644 internal/error/error.go create mode 100644 internal/reconcile/reconcile.go create mode 100644 internal/reconcile/reconcile_test.go diff --git a/internal/error/error.go b/internal/error/error.go new file mode 100644 index 000000000..df20ccc49 --- /dev/null +++ b/internal/error/error.go @@ -0,0 +1,56 @@ +/* +Copyright 2021 The Flux authors + +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. +*/ + +package error + +// Stalling is the reconciliation stalled state error. It contains an error +// and a reason for the stalled condition. +type Stalling struct { + // Reason is the stalled condition reason string. + Reason string + // Err is the error that caused stalling. This can be used as the message in + // stalled condition. + Err error +} + +// Error implements error interface. +func (se *Stalling) Error() string { + return se.Err.Error() +} + +// Unwrap returns the underlying error. +func (se *Stalling) Unwrap() error { + return se.Err +} + +// Event is an error event. It can be used to construct an event to be +// recorded. +type Event struct { + // Reason is the reason for the event error. + Reason string + // Error is the actual error for the event. + Err error +} + +// Error implements error interface. +func (ee *Event) Error() string { + return ee.Err.Error() +} + +// Unwrap returns the underlying error. +func (ee *Event) Unwrap() error { + return ee.Err +} diff --git a/internal/reconcile/reconcile.go b/internal/reconcile/reconcile.go new file mode 100644 index 000000000..f7593fcee --- /dev/null +++ b/internal/reconcile/reconcile.go @@ -0,0 +1,148 @@ +/* +Copyright 2021 The Flux authors + +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. +*/ + +package reconcile + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + kuberecorder "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/patch" + + sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" + serror "github.com/fluxcd/source-controller/internal/error" +) + +// Result is a type for creating an abstraction for the controller-runtime +// reconcile Result to simplify the Result values. +type Result int + +const ( + // ResultEmpty indicates a reconcile result which does not requeue. + ResultEmpty Result = iota + // ResultRequeue indicates a reconcile result which should immediately + // requeue. + ResultRequeue + // ResultSuccess indicates a reconcile result which should be + // requeued on the interval as defined on the reconciled object. + ResultSuccess +) + +// BuildRuntimeResult converts a given Result and error into the +// return values of a controller's Reconcile function. +func BuildRuntimeResult(ctx context.Context, recorder kuberecorder.EventRecorder, obj sourcev1.Source, rr Result, err error) (ctrl.Result, error) { + // NOTE: The return values can be modified based on the error type. + // For example, if an error signifies a short requeue period that's + // not equal to the requeue period of the object, the error can be checked + // and an appropriate result with the period can be returned. + // + // Example: + // if e, ok := err.(*waitError); ok { + // return ctrl.Result{RequeueAfter: e.RequeueAfter}, err + // } + + // Log and record event based on the error. + switch e := err.(type) { + case *serror.Event: + recorder.Eventf(obj, corev1.EventTypeWarning, e.Reason, e.Error()) + case *serror.Stalling: + // Stalling errors are not returned to the runtime. Log it explicitly. + ctrl.LoggerFrom(ctx).Error(e, "reconciliation stalled") + recorder.Eventf(obj, corev1.EventTypeWarning, e.Reason, e.Error()) + } + + switch rr { + case ResultRequeue: + return ctrl.Result{Requeue: true}, err + case ResultSuccess: + return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, err + default: + return ctrl.Result{}, err + } +} + +// ComputeReconcileResult analyzes the reconcile results (result + error), +// updates the status conditions of the object with any corrections and returns +// result patch configuration and any error to the caller. The caller is +// responsible for using the patch option to patch the object in the API server. +func ComputeReconcileResult(obj conditions.Setter, res Result, recErr error, ownedConditions []string) ([]patch.Option, error) { + // Remove reconciling condition on successful reconciliation. + if recErr == nil && res == ResultSuccess { + conditions.Delete(obj, meta.ReconcilingCondition) + } + + // Patch the object, ignoring conflicts on the conditions owned by this controller. + pOpts := []patch.Option{ + patch.WithOwnedConditions{ + Conditions: ownedConditions, + }, + } + + // Analyze the reconcile error. + switch t := recErr.(type) { + case *serror.Stalling: + if res == ResultEmpty { + // The current generation has been reconciled successfully and it + // has resulted in a stalled state. Return no error to stop further + // requeuing. + pOpts = append(pOpts, patch.WithStatusObservedGeneration{}) + conditions.MarkStalled(obj, t.Reason, t.Error()) + return pOpts, nil + } + // NOTE: Non-empty result with stalling error indicates that the + // returned result is incorrect. + case nil: + // The reconcile didn't result in any error, we are not in stalled + // state. If a requeue is requested, the current generation has not been + // reconciled successfully. + if res != ResultRequeue { + pOpts = append(pOpts, patch.WithStatusObservedGeneration{}) + } + conditions.Delete(obj, meta.StalledCondition) + default: + // The reconcile resulted in some error, but we are not in stalled + // state. + conditions.Delete(obj, meta.StalledCondition) + } + + return pOpts, recErr +} + +// LowestRequeuingResult returns the ReconcileResult with the lowest requeue +// period. +// Weightage: +// ResultRequeue - immediate requeue (lowest) +// ResultSuccess - requeue at an interval +// ResultEmpty - no requeue +func LowestRequeuingResult(i, j Result) Result { + switch { + case i == ResultEmpty: + return j + case j == ResultEmpty: + return i + case i == ResultRequeue: + return i + case j == ResultRequeue: + return j + default: + return j + } +} diff --git a/internal/reconcile/reconcile_test.go b/internal/reconcile/reconcile_test.go new file mode 100644 index 000000000..bb0cf4c44 --- /dev/null +++ b/internal/reconcile/reconcile_test.go @@ -0,0 +1,47 @@ +/* +Copyright 2021 The Flux authors + +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. +*/ + +package reconcile + +import ( + "testing" + + . "github.com/onsi/gomega" +) + +func TestLowestRequeuingResult(t *testing.T) { + tests := []struct { + name string + i Result + j Result + wantResult Result + }{ + {"bail,requeue", ResultEmpty, ResultRequeue, ResultRequeue}, + {"bail,requeueInterval", ResultEmpty, ResultSuccess, ResultSuccess}, + {"requeue,bail", ResultRequeue, ResultEmpty, ResultRequeue}, + {"requeue,requeueInterval", ResultRequeue, ResultSuccess, ResultRequeue}, + {"requeueInterval,requeue", ResultSuccess, ResultRequeue, ResultRequeue}, + {"requeueInterval,requeueInterval", ResultSuccess, ResultSuccess, ResultSuccess}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + g.Expect(LowestRequeuingResult(tt.i, tt.j)).To(Equal(tt.wantResult)) + }) + } +} From d3965926a304a1e88b09cb38e8f432ca2ef45dd0 Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Fri, 21 Jan 2022 23:37:55 +0100 Subject: [PATCH 10/10] internal/util: introduce temp dir/path helpers In most of the reconcilers we have a repetative pattern of using part of the object metadata to construct a temporary file path. This commit introduces helpers as an abstraction, for both the creation of a temporary directory based on `client.Object` type and object metadata, and the generation of an arbitrary random temporary path string. Signed-off-by: Hidde Beydals --- internal/util/temp.go | 52 +++++++++++++++++++++++ internal/util/temp_test.go | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 internal/util/temp.go create mode 100644 internal/util/temp_test.go diff --git a/internal/util/temp.go b/internal/util/temp.go new file mode 100644 index 000000000..054b12801 --- /dev/null +++ b/internal/util/temp.go @@ -0,0 +1,52 @@ +/* +Copyright 2021 The Flux authors + +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. +*/ + +package util + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "os" + "path/filepath" + "strings" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// TempDirForObj creates a new temporary directory in the directory dir +// in the format of 'Kind-Namespace-Name-*', and returns the +// pathname of the new directory. +func TempDirForObj(dir string, obj client.Object) (string, error) { + return os.MkdirTemp(dir, pattern(obj)) +} + +// TempPathForObj creates a temporary file path in the format of +// '/Kind-Namespace-Name-'. +// If the given dir is empty, os.TempDir is used as a default. +func TempPathForObj(dir, suffix string, obj client.Object) string { + if dir == "" { + dir = os.TempDir() + } + randBytes := make([]byte, 16) + rand.Read(randBytes) + return filepath.Join(dir, pattern(obj)+hex.EncodeToString(randBytes)+suffix) +} + +func pattern(obj client.Object) (p string) { + kind := strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind) + return fmt.Sprintf("%s-%s-%s-", kind, obj.GetNamespace(), obj.GetName()) +} diff --git a/internal/util/temp_test.go b/internal/util/temp_test.go new file mode 100644 index 000000000..7db873e2d --- /dev/null +++ b/internal/util/temp_test.go @@ -0,0 +1,85 @@ +/* +Copyright 2021 The Flux authors + +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. +*/ + +package util + +import ( + "os" + "testing" + + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func TestTempDirForObj(t *testing.T) { + g := NewWithT(t) + + got, err := TempDirForObj("", mockObj()) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).To(BeADirectory()) + defer os.RemoveAll(got) + + got2, err := TempDirForObj(got, mockObj()) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got2).To(BeADirectory()) + defer os.RemoveAll(got2) + g.Expect(got2).To(ContainSubstring(got)) +} + +func TestTempPathForObj(t *testing.T) { + tests := []struct { + name string + dir string + suffix string + want string + }{ + { + name: "default", + want: os.TempDir() + "/secret-default-foo-", + }, + { + name: "with directory", + dir: "/foo", + want: "/foo/secret-default-foo-", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + got := TempPathForObj(tt.dir, tt.suffix, mockObj()) + g.Expect(got[:len(got)-32]).To(Equal(tt.want)) + }) + } +} + +func Test_pattern(t *testing.T) { + g := NewWithT(t) + g.Expect(pattern(mockObj())).To(Equal("secret-default-foo-")) +} + +func mockObj() client.Object { + return &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + } +}