Merge pull request #586 from fluxcd/rewrite-dev
This commit is contained in:
commit
ff94d41f05
2
Makefile
2
Makefile
|
@ -127,7 +127,7 @@ manifests: controller-gen ## Generate manifests, e.g. CRD, RBAC, etc.
|
|||
cd api; $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role paths="./..." output:crd:artifacts:config="../config/crd/bases"
|
||||
|
||||
api-docs: gen-crd-api-reference-docs ## Generate API reference documentation
|
||||
$(GEN_CRD_API_REFERENCE_DOCS) -api-dir=./api/v1beta1 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/api/source.md
|
||||
$(GEN_CRD_API_REFERENCE_DOCS) -api-dir=./api/v1beta2 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/api/source.md
|
||||
|
||||
tidy: ## Run go mod tidy
|
||||
go mod tidy
|
||||
|
|
12
PROJECT
12
PROJECT
|
@ -1,6 +1,18 @@
|
|||
domain: toolkit.fluxcd.io
|
||||
repo: github.com/fluxcd/source-controller
|
||||
resources:
|
||||
- group: source
|
||||
kind: GitRepository
|
||||
version: v1beta2
|
||||
- group: source
|
||||
kind: HelmRepository
|
||||
version: v1beta2
|
||||
- group: source
|
||||
kind: HelmChart
|
||||
version: v1beta2
|
||||
- group: source
|
||||
kind: Bucket
|
||||
version: v1beta2
|
||||
- group: source
|
||||
kind: GitRepository
|
||||
version: v1beta1
|
||||
|
|
|
@ -4,8 +4,8 @@ go 1.17
|
|||
|
||||
require (
|
||||
github.com/fluxcd/pkg/apis/acl v0.0.3
|
||||
github.com/fluxcd/pkg/apis/meta v0.10.2
|
||||
k8s.io/apimachinery v0.23.1
|
||||
github.com/fluxcd/pkg/apis/meta v0.12.0
|
||||
k8s.io/apimachinery v0.23.2
|
||||
sigs.k8s.io/controller-runtime v0.11.0
|
||||
)
|
||||
|
||||
|
@ -24,5 +24,5 @@ require (
|
|||
k8s.io/klog/v2 v2.30.0 // 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/structured-merge-diff/v4 v4.2.1 // indirect
|
||||
)
|
||||
|
|
11
api/go.sum
11
api/go.sum
|
@ -123,8 +123,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
|||
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.12.0 h1:Ssyltj6E9A7y32sZrzjog0m+bIsFM/3lHHfmpxesUAU=
|
||||
github.com/fluxcd/pkg/apis/meta v0.12.0/go.mod h1:SPrSWMwDK7Ls2/4GadzhjDjPFbKrzzgzuZ0oDO3jzso=
|
||||
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=
|
||||
|
@ -897,8 +897,8 @@ 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/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/apimachinery v0.23.2 h1:dBmjCOeYBdg2ibcQxMuUq+OopZ9fjfLIR5taP/XKeTs=
|
||||
k8s.io/apimachinery v0.23.2/go.mod h1:zDqeV0AK62LbCI0CI7KbWCAYdLg+E+8UXJ0rIz5gmS8=
|
||||
k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4=
|
||||
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=
|
||||
|
@ -924,8 +924,9 @@ sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87J
|
|||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
|
|
@ -126,7 +126,13 @@ 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")
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionUnknown,
|
||||
Reason: meta.ProgressingReason,
|
||||
Message: "reconciliation in progress",
|
||||
}
|
||||
apimeta.SetStatusCondition(bucket.GetStatusConditions(), newCondition)
|
||||
return bucket
|
||||
}
|
||||
|
||||
|
@ -136,14 +142,26 @@ 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)
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
apimeta.SetStatusCondition(bucket.GetStatusConditions(), newCondition)
|
||||
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)
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
apimeta.SetStatusCondition(bucket.GetStatusConditions(), newCondition)
|
||||
return bucket
|
||||
}
|
||||
|
||||
|
|
|
@ -196,7 +196,13 @@ 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")
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionUnknown,
|
||||
Reason: meta.ProgressingReason,
|
||||
Message: "reconciliation in progress",
|
||||
}
|
||||
apimeta.SetStatusCondition(repository.GetStatusConditions(), newCondition)
|
||||
return repository
|
||||
}
|
||||
|
||||
|
@ -207,7 +213,13 @@ 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)
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
apimeta.SetStatusCondition(repository.GetStatusConditions(), newCondition)
|
||||
return repository
|
||||
}
|
||||
|
||||
|
@ -215,7 +227,13 @@ 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)
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
apimeta.SetStatusCondition(repository.GetStatusConditions(), newCondition)
|
||||
return repository
|
||||
}
|
||||
|
||||
|
|
|
@ -152,7 +152,13 @@ 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")
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionUnknown,
|
||||
Reason: meta.ProgressingReason,
|
||||
Message: "reconciliation in progress",
|
||||
}
|
||||
apimeta.SetStatusCondition(chart.GetStatusConditions(), newCondition)
|
||||
return chart
|
||||
}
|
||||
|
||||
|
@ -162,7 +168,13 @@ 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)
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
apimeta.SetStatusCondition(chart.GetStatusConditions(), newCondition)
|
||||
return chart
|
||||
}
|
||||
|
||||
|
@ -170,7 +182,13 @@ 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)
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
apimeta.SetStatusCondition(chart.GetStatusConditions(), newCondition)
|
||||
return chart
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,13 @@ 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")
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionUnknown,
|
||||
Reason: meta.ProgressingReason,
|
||||
Message: "reconciliation in progress",
|
||||
}
|
||||
apimeta.SetStatusCondition(repository.GetStatusConditions(), newCondition)
|
||||
return repository
|
||||
}
|
||||
|
||||
|
@ -123,7 +129,13 @@ 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)
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
apimeta.SetStatusCondition(repository.GetStatusConditions(), newCondition)
|
||||
return repository
|
||||
}
|
||||
|
||||
|
@ -131,7 +143,13 @@ 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)
|
||||
newCondition := metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
apimeta.SetStatusCondition(repository.GetStatusConditions(), newCondition)
|
||||
return repository
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
Copyright 2022 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.
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Artifact represents the output of a Source synchronisation.
|
||||
type Artifact struct {
|
||||
// Path is the relative file path of this Artifact.
|
||||
// It can be used to locate the Artifact file in the root of the Artifact
|
||||
// storage on the local file system of the controller managing the Source.
|
||||
// +required
|
||||
Path string `json:"path"`
|
||||
|
||||
// URL is the HTTP address of this artifact.
|
||||
// It is used by the consumers of the artifacts to fetch and use the
|
||||
// artifacts. It is expected to be resolvable from within the cluster.
|
||||
// +required
|
||||
URL string `json:"url"`
|
||||
|
||||
// Revision is a human readable identifier traceable in the origin source
|
||||
// system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm
|
||||
// chart version, etc.
|
||||
// +optional
|
||||
Revision string `json:"revision"`
|
||||
|
||||
// Checksum is the SHA256 checksum of the artifact.
|
||||
// +optional
|
||||
Checksum string `json:"checksum"`
|
||||
|
||||
// LastUpdateTime is the timestamp corresponding to the last update of this
|
||||
// artifact.
|
||||
// +required
|
||||
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
|
||||
}
|
||||
|
||||
// HasRevision returns true if the given revision matches the current Revision
|
||||
// of the Artifact.
|
||||
func (in *Artifact) HasRevision(revision string) bool {
|
||||
if in == nil {
|
||||
return false
|
||||
}
|
||||
return in.Revision == revision
|
||||
}
|
||||
|
||||
// ArtifactDir returns the artifact dir path in the form of
|
||||
// <source-kind>/<source-namespace>/<source-name>.
|
||||
func ArtifactDir(kind, namespace, name string) string {
|
||||
kind = strings.ToLower(kind)
|
||||
return path.Join(kind, namespace, name)
|
||||
}
|
||||
|
||||
// ArtifactPath returns the artifact path in the form of
|
||||
// <source-kind>/<source-namespace>/<source-name>/<artifact-filename>.
|
||||
func ArtifactPath(kind, namespace, name, filename string) string {
|
||||
return path.Join(ArtifactDir(kind, namespace, name), filename)
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/acl"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
)
|
||||
|
||||
const (
|
||||
// BucketKind is the string representation of a Bucket.
|
||||
BucketKind = "Bucket"
|
||||
)
|
||||
|
||||
const (
|
||||
GenericBucketProvider string = "generic"
|
||||
AmazonBucketProvider string = "aws"
|
||||
GoogleBucketProvider string = "gcp"
|
||||
)
|
||||
|
||||
// BucketSpec defines the desired state of an S3 compatible bucket
|
||||
type BucketSpec struct {
|
||||
// The S3 compatible storage provider name, default ('generic').
|
||||
// +kubebuilder:validation:Enum=generic;aws;gcp
|
||||
// +kubebuilder:default:=generic
|
||||
// +optional
|
||||
Provider string `json:"provider,omitempty"`
|
||||
|
||||
// The bucket name.
|
||||
// +required
|
||||
BucketName string `json:"bucketName"`
|
||||
|
||||
// The bucket endpoint address.
|
||||
// +required
|
||||
Endpoint string `json:"endpoint"`
|
||||
|
||||
// Insecure allows connecting to a non-TLS S3 HTTP endpoint.
|
||||
// +optional
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
|
||||
// The bucket region.
|
||||
// +optional
|
||||
Region string `json:"region,omitempty"`
|
||||
|
||||
// The name of the secret containing authentication credentials
|
||||
// for the Bucket.
|
||||
// +optional
|
||||
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
|
||||
// The interval at which to check for bucket updates.
|
||||
// +required
|
||||
Interval metav1.Duration `json:"interval"`
|
||||
|
||||
// The timeout for fetch operations, defaults to 60s.
|
||||
// +kubebuilder:default="60s"
|
||||
// +optional
|
||||
Timeout *metav1.Duration `json:"timeout,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.
|
||||
// +optional
|
||||
Ignore *string `json:"ignore,omitempty"`
|
||||
|
||||
// This flag tells the controller to suspend the reconciliation of this source.
|
||||
// +optional
|
||||
Suspend bool `json:"suspend,omitempty"`
|
||||
|
||||
// AccessFrom defines an Access Control List for allowing cross-namespace references to this object.
|
||||
// +optional
|
||||
AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"`
|
||||
}
|
||||
|
||||
// BucketStatus defines the observed state of a bucket
|
||||
type BucketStatus struct {
|
||||
// ObservedGeneration is the last observed generation.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// Conditions holds the conditions for the Bucket.
|
||||
// +optional
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
|
||||
// URL is the fetch link for the artifact output of the last Bucket sync.
|
||||
// +optional
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// Artifact represents the output of the last successful Bucket sync.
|
||||
// +optional
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
|
||||
meta.ReconcileRequestStatus `json:",inline"`
|
||||
}
|
||||
|
||||
const (
|
||||
// BucketOperationSucceededReason represents the fact that the bucket listing and
|
||||
// fetch operations succeeded.
|
||||
BucketOperationSucceededReason string = "BucketOperationSucceeded"
|
||||
|
||||
// BucketOperationFailedReason represents the fact that the bucket listing or
|
||||
// fetch operations failed.
|
||||
BucketOperationFailedReason string = "BucketOperationFailed"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Deprecated: use GetConditions instead.
|
||||
func (in *Bucket) GetStatusConditions() *[]metav1.Condition {
|
||||
return &in.Status.Conditions
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +genclient:Namespaced
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// Bucket is the Schema for the buckets API
|
||||
type Bucket struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec BucketSpec `json:"spec,omitempty"`
|
||||
// +kubebuilder:default={"observedGeneration":-1}
|
||||
Status BucketStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// BucketList contains a list of Bucket
|
||||
type BucketList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Bucket `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Bucket{}, &BucketList{})
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2
|
||||
|
||||
const SourceFinalizer = "finalizers.fluxcd.io"
|
||||
|
||||
const (
|
||||
// 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"
|
||||
|
||||
// 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"
|
||||
|
||||
// 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"
|
||||
|
||||
// BuildFailedCondition indicates a transient or persistent build failure of a Source's Artifact.
|
||||
// If True, the Source can be in an ArtifactOutdatedCondition
|
||||
BuildFailedCondition string = "BuildFailed"
|
||||
)
|
||||
|
||||
const (
|
||||
// URLInvalidReason represents the fact that a given source has an invalid URL.
|
||||
URLInvalidReason string = "URLInvalid"
|
||||
|
||||
// StorageOperationFailedReason signals a failure caused by a storage operation.
|
||||
StorageOperationFailedReason string = "StorageOperationFailed"
|
||||
|
||||
// AuthenticationFailedReason represents the fact that a given secret does not
|
||||
// have the required fields or the provided credentials do not match.
|
||||
AuthenticationFailedReason string = "AuthenticationFailed"
|
||||
)
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2 contains API Schema definitions for the source v1beta2 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=source.toolkit.fluxcd.io
|
||||
package v1beta2
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/acl"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
)
|
||||
|
||||
const (
|
||||
// GitRepositoryKind is the string representation of a GitRepository.
|
||||
GitRepositoryKind = "GitRepository"
|
||||
|
||||
// GoGitImplementation represents the go-git Git implementation kind.
|
||||
GoGitImplementation = "go-git"
|
||||
// LibGit2Implementation represents the git2go Git implementation kind.
|
||||
LibGit2Implementation = "libgit2"
|
||||
)
|
||||
|
||||
const (
|
||||
// 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"
|
||||
)
|
||||
|
||||
// GitRepositorySpec defines the desired state of a Git repository.
|
||||
type GitRepositorySpec struct {
|
||||
// The repository URL, can be a HTTP/S or SSH address.
|
||||
// +kubebuilder:validation:Pattern="^(http|https|ssh)://"
|
||||
// +required
|
||||
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.
|
||||
// +optional
|
||||
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
|
||||
// The interval at which to check for repository updates.
|
||||
// +required
|
||||
Interval metav1.Duration `json:"interval"`
|
||||
|
||||
// The timeout for remote Git operations like cloning, defaults to 60s.
|
||||
// +kubebuilder:default="60s"
|
||||
// +optional
|
||||
Timeout *metav1.Duration `json:"timeout,omitempty"`
|
||||
|
||||
// The Git reference to checkout and monitor for changes, defaults to
|
||||
// master branch.
|
||||
// +optional
|
||||
Reference *GitRepositoryRef `json:"ref,omitempty"`
|
||||
|
||||
// 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.
|
||||
// +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"`
|
||||
|
||||
// Determines which git client library to use.
|
||||
// Defaults to go-git, valid values are ('go-git', 'libgit2').
|
||||
// +kubebuilder:validation:Enum=go-git;libgit2
|
||||
// +kubebuilder:default:=go-git
|
||||
// +optional
|
||||
GitImplementation string `json:"gitImplementation,omitempty"`
|
||||
|
||||
// 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"`
|
||||
|
||||
// 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.
|
||||
// +optional
|
||||
AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"`
|
||||
}
|
||||
|
||||
func (in *GitRepositoryInclude) GetFromPath() string {
|
||||
return in.FromPath
|
||||
}
|
||||
|
||||
func (in *GitRepositoryInclude) GetToPath() string {
|
||||
if in.ToPath == "" {
|
||||
return in.GitRepositoryRef.Name
|
||||
}
|
||||
return in.ToPath
|
||||
}
|
||||
|
||||
// GitRepositoryInclude defines a source with a from and to path.
|
||||
type GitRepositoryInclude struct {
|
||||
// Reference to a GitRepository to include.
|
||||
GitRepositoryRef meta.LocalObjectReference `json:"repository"`
|
||||
|
||||
// The path to copy contents from, defaults to the root directory.
|
||||
// +optional
|
||||
FromPath string `json:"fromPath"`
|
||||
|
||||
// The path to copy contents to, defaults to the name of the source ref.
|
||||
// +optional
|
||||
ToPath string `json:"toPath"`
|
||||
}
|
||||
|
||||
// GitRepositoryRef defines the Git ref used for pull and checkout operations.
|
||||
type GitRepositoryRef struct {
|
||||
// The Git branch to checkout, defaults to master.
|
||||
// +optional
|
||||
Branch string `json:"branch,omitempty"`
|
||||
|
||||
// The Git tag to checkout, takes precedence over Branch.
|
||||
// +optional
|
||||
Tag string `json:"tag,omitempty"`
|
||||
|
||||
// The Git tag semver expression, takes precedence over Tag.
|
||||
// +optional
|
||||
SemVer string `json:"semver,omitempty"`
|
||||
|
||||
// The Git commit SHA to checkout, if specified Tag filters will be ignored.
|
||||
// +optional
|
||||
Commit string `json:"commit,omitempty"`
|
||||
}
|
||||
|
||||
// GitRepositoryVerification defines the OpenPGP signature verification process.
|
||||
type GitRepositoryVerification struct {
|
||||
// Mode describes what Git object should be verified, currently ('head').
|
||||
// +kubebuilder:validation:Enum=head
|
||||
Mode string `json:"mode"`
|
||||
|
||||
// SecretRef containing the public keys of all trusted Git authors.
|
||||
SecretRef meta.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
}
|
||||
|
||||
// GitRepositoryStatus defines the observed state of a Git repository.
|
||||
type GitRepositoryStatus struct {
|
||||
// ObservedGeneration is the last observed generation.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// Conditions holds the conditions for the GitRepository.
|
||||
// +optional
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
|
||||
// URL is the fetch link for the artifact output of the last repository sync.
|
||||
// +optional
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// Artifact represents the output of the last successful repository sync.
|
||||
// +optional
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
|
||||
// IncludedArtifacts represents the included artifacts from the last successful repository sync.
|
||||
// +optional
|
||||
IncludedArtifacts []*Artifact `json:"includedArtifacts,omitempty"`
|
||||
|
||||
meta.ReconcileRequestStatus `json:",inline"`
|
||||
}
|
||||
|
||||
const (
|
||||
// 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 string = "GitOperationFailed"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Deprecated: use GetConditions instead.
|
||||
func (in *GitRepository) GetStatusConditions() *[]metav1.Condition {
|
||||
return &in.Status.Conditions
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +genclient:Namespaced
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:shortName=gitrepo
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// GitRepository is the Schema for the gitrepositories API
|
||||
type GitRepository struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec GitRepositorySpec `json:"spec,omitempty"`
|
||||
// +kubebuilder:default={"observedGeneration":-1}
|
||||
Status GitRepositoryStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GitRepositoryList contains a list of GitRepository
|
||||
// +kubebuilder:object:root=true
|
||||
type GitRepositoryList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []GitRepository `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&GitRepository{}, &GitRepositoryList{})
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is group version used to register these objects.
|
||||
GroupVersion = schema.GroupVersion{Group: "source.toolkit.fluxcd.io", Version: "v1beta2"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/acl"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
)
|
||||
|
||||
// HelmChartKind is the string representation of a HelmChart.
|
||||
const HelmChartKind = "HelmChart"
|
||||
|
||||
// HelmChartSpec defines the desired state of a Helm chart.
|
||||
type HelmChartSpec struct {
|
||||
// The name or path the Helm chart is available at in the SourceRef.
|
||||
// +required
|
||||
Chart string `json:"chart"`
|
||||
|
||||
// The chart version semver expression, ignored for charts from GitRepository
|
||||
// and Bucket sources. Defaults to latest when omitted.
|
||||
// +kubebuilder:default:=*
|
||||
// +optional
|
||||
Version string `json:"version,omitempty"`
|
||||
|
||||
// The reference to the Source the chart is available at.
|
||||
// +required
|
||||
SourceRef LocalHelmChartSourceReference `json:"sourceRef"`
|
||||
|
||||
// The interval at which to check the Source for updates.
|
||||
// +required
|
||||
Interval metav1.Duration `json:"interval"`
|
||||
|
||||
// Determines what enables the creation of a new artifact. Valid values are
|
||||
// ('ChartVersion', 'Revision').
|
||||
// See the documentation of the values for an explanation on their behavior.
|
||||
// Defaults to ChartVersion when omitted.
|
||||
// +kubebuilder:validation:Enum=ChartVersion;Revision
|
||||
// +kubebuilder:default:=ChartVersion
|
||||
// +optional
|
||||
ReconcileStrategy string `json:"reconcileStrategy,omitempty"`
|
||||
|
||||
// Alternative list of values files to use as the chart values (values.yaml
|
||||
// is not included by default), expected to be a relative path in the SourceRef.
|
||||
// Values files are merged in the order of this list with the last file overriding
|
||||
// the first. Ignored when omitted.
|
||||
// +optional
|
||||
ValuesFiles []string `json:"valuesFiles,omitempty"`
|
||||
|
||||
// Alternative values file to use as the default chart values, expected to
|
||||
// be a relative path in the SourceRef. Deprecated in favor of ValuesFiles,
|
||||
// for backwards compatibility the file defined here is merged before the
|
||||
// ValuesFiles items. Ignored when omitted.
|
||||
// +optional
|
||||
// +deprecated
|
||||
ValuesFile string `json:"valuesFile,omitempty"`
|
||||
|
||||
// This flag tells the controller to suspend the reconciliation of this source.
|
||||
// +optional
|
||||
Suspend bool `json:"suspend,omitempty"`
|
||||
|
||||
// AccessFrom defines an Access Control List for allowing cross-namespace references to this object.
|
||||
// +optional
|
||||
AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
// ReconcileStrategyChartVersion reconciles when the version of the Helm chart is different.
|
||||
ReconcileStrategyChartVersion string = "ChartVersion"
|
||||
|
||||
// ReconcileStrategyRevision reconciles when the Revision of the source is different.
|
||||
ReconcileStrategyRevision string = "Revision"
|
||||
)
|
||||
|
||||
// LocalHelmChartSourceReference contains enough information to let you locate
|
||||
// the typed referenced object at namespace level.
|
||||
type LocalHelmChartSourceReference struct {
|
||||
// APIVersion of the referent.
|
||||
// +optional
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
|
||||
// Kind of the referent, valid values are ('HelmRepository', 'GitRepository',
|
||||
// 'Bucket').
|
||||
// +kubebuilder:validation:Enum=HelmRepository;GitRepository;Bucket
|
||||
// +required
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// Name of the referent.
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// HelmChartStatus defines the observed state of the HelmChart.
|
||||
type HelmChartStatus struct {
|
||||
// ObservedGeneration is the last observed generation.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// ObservedSourceArtifactRevision is the last observed Artifact.Revision
|
||||
// of the Source reference.
|
||||
// +optional
|
||||
ObservedSourceArtifactRevision string `json:"observedSourceArtifactRevision,omitempty"`
|
||||
|
||||
// ObservedChartName is the last observed chart name as defined by the
|
||||
// resolved chart reference.
|
||||
// +optional
|
||||
ObservedChartName string `json:"observedChartName,omitempty"`
|
||||
|
||||
// Conditions holds the conditions for the HelmChart.
|
||||
// +optional
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
|
||||
// URL is the fetch link for the last chart pulled.
|
||||
// +optional
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// Artifact represents the output of the last successful chart sync.
|
||||
// +optional
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
|
||||
meta.ReconcileRequestStatus `json:",inline"`
|
||||
}
|
||||
|
||||
const (
|
||||
// ChartPullFailedReason represents the fact that the pull of the Helm chart
|
||||
// failed.
|
||||
ChartPullFailedReason string = "ChartPullFailed"
|
||||
|
||||
// ChartPullSucceededReason represents the fact that the pull of the Helm chart
|
||||
// succeeded.
|
||||
ChartPullSucceededReason string = "ChartPullSucceeded"
|
||||
|
||||
// ChartPackageFailedReason represent the fact that the package of the Helm
|
||||
// chart failed.
|
||||
ChartPackageFailedReason string = "ChartPackageFailed"
|
||||
|
||||
// ChartPackageSucceededReason represents the fact that the package of the Helm
|
||||
// chart succeeded.
|
||||
ChartPackageSucceededReason string = "ChartPackageSucceeded"
|
||||
)
|
||||
|
||||
// GetConditions returns the status conditions of the object.
|
||||
func (in HelmChart) GetConditions() []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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Prepend the deprecated ValuesFile to the list
|
||||
if in.Spec.ValuesFile != "" {
|
||||
valuesFiles = append([]string{in.Spec.ValuesFile}, valuesFiles...)
|
||||
}
|
||||
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:storageversion
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:shortName=hc
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Chart",type=string,JSONPath=`.spec.chart`
|
||||
// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version`
|
||||
// +kubebuilder:printcolumn:name="Source Kind",type=string,JSONPath=`.spec.sourceRef.kind`
|
||||
// +kubebuilder:printcolumn:name="Source Name",type=string,JSONPath=`.spec.sourceRef.name`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// HelmChart is the Schema for the helmcharts API
|
||||
type HelmChart struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec HelmChartSpec `json:"spec,omitempty"`
|
||||
// +kubebuilder:default={"observedGeneration":-1}
|
||||
Status HelmChartStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// HelmChartList contains a list of HelmChart
|
||||
type HelmChartList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []HelmChart `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&HelmChart{}, &HelmChartList{})
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/acl"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
)
|
||||
|
||||
const (
|
||||
// HelmRepositoryKind is the string representation of a HelmRepository.
|
||||
HelmRepositoryKind = "HelmRepository"
|
||||
// HelmRepositoryURLIndexKey is the key to use for indexing HelmRepository
|
||||
// resources by their HelmRepositorySpec.URL.
|
||||
HelmRepositoryURLIndexKey = ".metadata.helmRepositoryURL"
|
||||
)
|
||||
|
||||
// HelmRepositorySpec defines the reference to a Helm repository.
|
||||
type HelmRepositorySpec struct {
|
||||
// The Helm repository URL, a valid URL contains at least a protocol and host.
|
||||
// +required
|
||||
URL string `json:"url"`
|
||||
|
||||
// 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.
|
||||
// +optional
|
||||
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
|
||||
// PassCredentials allows the credentials from the SecretRef to be passed on to
|
||||
// a host that does not match the host as defined in URL.
|
||||
// This may be required if the host of the advertised chart URLs in the index
|
||||
// differ from the defined URL.
|
||||
// Enabling this should be done with caution, as it can potentially result in
|
||||
// credentials getting stolen in a MITM-attack.
|
||||
// +optional
|
||||
PassCredentials bool `json:"passCredentials,omitempty"`
|
||||
|
||||
// The interval at which to check the upstream for updates.
|
||||
// +required
|
||||
Interval metav1.Duration `json:"interval"`
|
||||
|
||||
// The timeout of index fetching, defaults to 60s.
|
||||
// +kubebuilder:default:="60s"
|
||||
// +optional
|
||||
Timeout *metav1.Duration `json:"timeout,omitempty"`
|
||||
|
||||
// This flag tells the controller to suspend the reconciliation of this source.
|
||||
// +optional
|
||||
Suspend bool `json:"suspend,omitempty"`
|
||||
|
||||
// AccessFrom defines an Access Control List for allowing cross-namespace references to this object.
|
||||
// +optional
|
||||
AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"`
|
||||
}
|
||||
|
||||
// HelmRepositoryStatus defines the observed state of the HelmRepository.
|
||||
type HelmRepositoryStatus struct {
|
||||
// ObservedGeneration is the last observed generation.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// Conditions holds the conditions for the HelmRepository.
|
||||
// +optional
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
|
||||
// URL is the fetch link for the last index fetched.
|
||||
// +optional
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// Artifact represents the output of the last successful repository sync.
|
||||
// +optional
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
|
||||
meta.ReconcileRequestStatus `json:",inline"`
|
||||
}
|
||||
|
||||
const (
|
||||
// IndexationFailedReason represents the fact that the indexation of the given
|
||||
// Helm repository failed.
|
||||
IndexationFailedReason string = "IndexationFailed"
|
||||
|
||||
// IndexationSucceededReason represents the fact that the indexation of the
|
||||
// given Helm repository succeeded.
|
||||
IndexationSucceededReason string = "IndexationSucceed"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Deprecated: use GetConditions instead.
|
||||
func (in *HelmRepository) GetStatusConditions() *[]metav1.Condition {
|
||||
return &in.Status.Conditions
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +genclient:Namespaced
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:shortName=helmrepo
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// HelmRepository is the Schema for the helmrepositories API
|
||||
type HelmRepository struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec HelmRepositorySpec `json:"spec,omitempty"`
|
||||
// +kubebuilder:default={"observedGeneration":-1}
|
||||
Status HelmRepositoryStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// HelmRepositoryList contains a list of HelmRepository
|
||||
// +kubebuilder:object:root=true
|
||||
type HelmRepositoryList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []HelmRepository `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&HelmRepository{}, &HelmRepositoryList{})
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright 2022 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 v1beta2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
// SourceIndexKey is the key used for indexing resources
|
||||
// resources based on their Source.
|
||||
SourceIndexKey string = ".metadata.source"
|
||||
)
|
||||
|
||||
// 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
|
||||
// status sub-resource.
|
||||
GetArtifact() *Artifact
|
||||
// GetInterval returns the interval at which the source is updated.
|
||||
// Deprecated: use GetRequeueAfter instead.
|
||||
GetInterval() metav1.Duration
|
||||
}
|
|
@ -0,0 +1,611 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2022 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.
|
||||
*/
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"github.com/fluxcd/pkg/apis/acl"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Artifact) DeepCopyInto(out *Artifact) {
|
||||
*out = *in
|
||||
in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Artifact.
|
||||
func (in *Artifact) DeepCopy() *Artifact {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Artifact)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Bucket) DeepCopyInto(out *Bucket) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bucket.
|
||||
func (in *Bucket) DeepCopy() *Bucket {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Bucket)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Bucket) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *BucketList) DeepCopyInto(out *BucketList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Bucket, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketList.
|
||||
func (in *BucketList) DeepCopy() *BucketList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(BucketList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *BucketList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *BucketSpec) DeepCopyInto(out *BucketSpec) {
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(meta.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
out.Interval = in.Interval
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Ignore != nil {
|
||||
in, out := &in.Ignore, &out.Ignore
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.AccessFrom != nil {
|
||||
in, out := &in.AccessFrom, &out.AccessFrom
|
||||
*out = new(acl.AccessFrom)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketSpec.
|
||||
func (in *BucketSpec) DeepCopy() *BucketSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(BucketSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *BucketStatus) DeepCopyInto(out *BucketStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Artifact != nil {
|
||||
in, out := &in.Artifact, &out.Artifact
|
||||
*out = new(Artifact)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
out.ReconcileRequestStatus = in.ReconcileRequestStatus
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BucketStatus.
|
||||
func (in *BucketStatus) DeepCopy() *BucketStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(BucketStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GitRepository) DeepCopyInto(out *GitRepository) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRepository.
|
||||
func (in *GitRepository) DeepCopy() *GitRepository {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GitRepository)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *GitRepository) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GitRepositoryInclude) DeepCopyInto(out *GitRepositoryInclude) {
|
||||
*out = *in
|
||||
out.GitRepositoryRef = in.GitRepositoryRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRepositoryInclude.
|
||||
func (in *GitRepositoryInclude) DeepCopy() *GitRepositoryInclude {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GitRepositoryInclude)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GitRepositoryList) DeepCopyInto(out *GitRepositoryList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]GitRepository, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRepositoryList.
|
||||
func (in *GitRepositoryList) DeepCopy() *GitRepositoryList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GitRepositoryList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *GitRepositoryList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GitRepositoryRef) DeepCopyInto(out *GitRepositoryRef) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRepositoryRef.
|
||||
func (in *GitRepositoryRef) DeepCopy() *GitRepositoryRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GitRepositoryRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GitRepositorySpec) DeepCopyInto(out *GitRepositorySpec) {
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(meta.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
out.Interval = in.Interval
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Reference != nil {
|
||||
in, out := &in.Reference, &out.Reference
|
||||
*out = new(GitRepositoryRef)
|
||||
**out = **in
|
||||
}
|
||||
if in.Verification != nil {
|
||||
in, out := &in.Verification, &out.Verification
|
||||
*out = new(GitRepositoryVerification)
|
||||
**out = **in
|
||||
}
|
||||
if in.Ignore != nil {
|
||||
in, out := &in.Ignore, &out.Ignore
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.Include != nil {
|
||||
in, out := &in.Include, &out.Include
|
||||
*out = make([]GitRepositoryInclude, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AccessFrom != nil {
|
||||
in, out := &in.AccessFrom, &out.AccessFrom
|
||||
*out = new(acl.AccessFrom)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRepositorySpec.
|
||||
func (in *GitRepositorySpec) DeepCopy() *GitRepositorySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GitRepositorySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GitRepositoryStatus) DeepCopyInto(out *GitRepositoryStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Artifact != nil {
|
||||
in, out := &in.Artifact, &out.Artifact
|
||||
*out = new(Artifact)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.IncludedArtifacts != nil {
|
||||
in, out := &in.IncludedArtifacts, &out.IncludedArtifacts
|
||||
*out = make([]*Artifact, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Artifact)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
out.ReconcileRequestStatus = in.ReconcileRequestStatus
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRepositoryStatus.
|
||||
func (in *GitRepositoryStatus) DeepCopy() *GitRepositoryStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GitRepositoryStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GitRepositoryVerification) DeepCopyInto(out *GitRepositoryVerification) {
|
||||
*out = *in
|
||||
out.SecretRef = in.SecretRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRepositoryVerification.
|
||||
func (in *GitRepositoryVerification) DeepCopy() *GitRepositoryVerification {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GitRepositoryVerification)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChart) DeepCopyInto(out *HelmChart) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChart.
|
||||
func (in *HelmChart) DeepCopy() *HelmChart {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChart)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmChart) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartList) DeepCopyInto(out *HelmChartList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]HelmChart, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartList.
|
||||
func (in *HelmChartList) DeepCopy() *HelmChartList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmChartList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartSpec) DeepCopyInto(out *HelmChartSpec) {
|
||||
*out = *in
|
||||
out.SourceRef = in.SourceRef
|
||||
out.Interval = in.Interval
|
||||
if in.ValuesFiles != nil {
|
||||
in, out := &in.ValuesFiles, &out.ValuesFiles
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AccessFrom != nil {
|
||||
in, out := &in.AccessFrom, &out.AccessFrom
|
||||
*out = new(acl.AccessFrom)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartSpec.
|
||||
func (in *HelmChartSpec) DeepCopy() *HelmChartSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartStatus) DeepCopyInto(out *HelmChartStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Artifact != nil {
|
||||
in, out := &in.Artifact, &out.Artifact
|
||||
*out = new(Artifact)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
out.ReconcileRequestStatus = in.ReconcileRequestStatus
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartStatus.
|
||||
func (in *HelmChartStatus) DeepCopy() *HelmChartStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmRepository) DeepCopyInto(out *HelmRepository) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmRepository.
|
||||
func (in *HelmRepository) DeepCopy() *HelmRepository {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmRepository)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmRepository) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmRepositoryList) DeepCopyInto(out *HelmRepositoryList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]HelmRepository, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmRepositoryList.
|
||||
func (in *HelmRepositoryList) DeepCopy() *HelmRepositoryList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmRepositoryList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmRepositoryList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmRepositorySpec) DeepCopyInto(out *HelmRepositorySpec) {
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(meta.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
out.Interval = in.Interval
|
||||
if in.Timeout != nil {
|
||||
in, out := &in.Timeout, &out.Timeout
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.AccessFrom != nil {
|
||||
in, out := &in.AccessFrom, &out.AccessFrom
|
||||
*out = new(acl.AccessFrom)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmRepositorySpec.
|
||||
func (in *HelmRepositorySpec) DeepCopy() *HelmRepositorySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmRepositorySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmRepositoryStatus) DeepCopyInto(out *HelmRepositoryStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Artifact != nil {
|
||||
in, out := &in.Artifact, &out.Artifact
|
||||
*out = new(Artifact)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
out.ReconcileRequestStatus = in.ReconcileRequestStatus
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmRepositoryStatus.
|
||||
func (in *HelmRepositoryStatus) DeepCopy() *HelmRepositoryStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmRepositoryStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LocalHelmChartSourceReference) DeepCopyInto(out *LocalHelmChartSourceReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalHelmChartSourceReference.
|
||||
func (in *LocalHelmChartSourceReference) DeepCopy() *LocalHelmChartSourceReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LocalHelmChartSourceReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
|
@ -111,7 +111,7 @@ spec:
|
|||
for the Bucket.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -233,7 +233,8 @@ spec:
|
|||
type: array
|
||||
lastHandledReconcileAt:
|
||||
description: LastHandledReconcileAt holds the value of the most recent
|
||||
reconcile request value, so a change can be detected.
|
||||
reconcile request value, so a change of the annotation value can
|
||||
be detected.
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: ObservedGeneration is the last observed generation.
|
||||
|
@ -246,6 +247,245 @@ spec:
|
|||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
subresources:
|
||||
status: {}
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.endpoint
|
||||
name: Endpoint
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1beta2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Bucket is the Schema for the buckets API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: BucketSpec defines the desired state of an S3 compatible
|
||||
bucket
|
||||
properties:
|
||||
accessFrom:
|
||||
description: AccessFrom defines an Access Control List for allowing
|
||||
cross-namespace references to this object.
|
||||
properties:
|
||||
namespaceSelectors:
|
||||
description: NamespaceSelectors is the list of namespace selectors
|
||||
to which this ACL applies. Items in this list are evaluated
|
||||
using a logical OR operation.
|
||||
items:
|
||||
description: NamespaceSelector selects the namespaces to which
|
||||
this ACL applies. An empty map of MatchLabels matches all
|
||||
namespaces in a cluster.
|
||||
properties:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: MatchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field is
|
||||
"key", the operator is "In", and the values array contains
|
||||
only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- namespaceSelectors
|
||||
type: object
|
||||
bucketName:
|
||||
description: The bucket name.
|
||||
type: string
|
||||
endpoint:
|
||||
description: The bucket endpoint address.
|
||||
type: string
|
||||
ignore:
|
||||
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
|
||||
insecure:
|
||||
description: Insecure allows connecting to a non-TLS S3 HTTP endpoint.
|
||||
type: boolean
|
||||
interval:
|
||||
description: The interval at which to check for bucket updates.
|
||||
type: string
|
||||
provider:
|
||||
default: generic
|
||||
description: The S3 compatible storage provider name, default ('generic').
|
||||
enum:
|
||||
- generic
|
||||
- aws
|
||||
- gcp
|
||||
type: string
|
||||
region:
|
||||
description: The bucket region.
|
||||
type: string
|
||||
secretRef:
|
||||
description: The name of the secret containing authentication credentials
|
||||
for the Bucket.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
suspend:
|
||||
description: This flag tells the controller to suspend the reconciliation
|
||||
of this source.
|
||||
type: boolean
|
||||
timeout:
|
||||
default: 60s
|
||||
description: The timeout for fetch operations, defaults to 60s.
|
||||
type: string
|
||||
required:
|
||||
- bucketName
|
||||
- endpoint
|
||||
- interval
|
||||
type: object
|
||||
status:
|
||||
default:
|
||||
observedGeneration: -1
|
||||
description: BucketStatus defines the observed state of a bucket
|
||||
properties:
|
||||
artifact:
|
||||
description: Artifact represents the output of the last successful
|
||||
Bucket sync.
|
||||
properties:
|
||||
checksum:
|
||||
description: Checksum is the SHA256 checksum of the artifact.
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
description: LastUpdateTime is the timestamp corresponding to
|
||||
the last update of this artifact.
|
||||
format: date-time
|
||||
type: string
|
||||
path:
|
||||
description: Path is the relative file path of this Artifact.
|
||||
It can be used to locate the Artifact file in the root of the
|
||||
Artifact storage on the local file system of the controller
|
||||
managing the Source.
|
||||
type: string
|
||||
revision:
|
||||
description: Revision is a human readable identifier traceable
|
||||
in the origin source system. It can be a Git commit SHA, Git
|
||||
tag, a Helm index timestamp, a Helm chart version, etc.
|
||||
type: string
|
||||
url:
|
||||
description: URL is the HTTP address of this artifact. It is used
|
||||
by the consumers of the artifacts to fetch and use the artifacts.
|
||||
It is expected to be resolvable from within the cluster.
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
- url
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions holds the conditions for the Bucket.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource. --- This struct is intended for direct
|
||||
use as an array at the field path .status.conditions. For example,
|
||||
type FooStatus struct{ // Represents the observations of a
|
||||
foo's current state. // Known .status.conditions.type are:
|
||||
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
|
||||
\ // +patchStrategy=merge // +listType=map // +listMapKey=type
|
||||
\ Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
|
||||
\n // other fields }"
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: lastTransitionTime is the last time the condition
|
||||
transitioned from one status to another. This should be when
|
||||
the underlying condition changed. If that is not known, then
|
||||
using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: message is a human readable message indicating
|
||||
details about the transition. This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration represents the .metadata.generation
|
||||
that the condition was set based upon. For instance, if .metadata.generation
|
||||
is currently 12, but the .status.conditions[x].observedGeneration
|
||||
is 9, the condition is out of date with respect to the current
|
||||
state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: reason contains a programmatic identifier indicating
|
||||
the reason for the condition's last transition. Producers
|
||||
of specific condition types may define expected values and
|
||||
meanings for this field, and whether the values are considered
|
||||
a guaranteed API. The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
--- Many .condition.type values are consistent across resources
|
||||
like Available, but because arbitrary conditions can be useful
|
||||
(see .node.status.conditions), the ability to deconflict is
|
||||
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
lastHandledReconcileAt:
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
description: URL is the fetch link for the artifact output of the
|
||||
last Bucket sync.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
|
@ -106,7 +106,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
|
||||
|
@ -153,7 +153,7 @@ spec:
|
|||
and known_hosts fields.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -186,7 +186,7 @@ spec:
|
|||
trusted Git authors.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -332,7 +332,8 @@ spec:
|
|||
type: array
|
||||
lastHandledReconcileAt:
|
||||
description: LastHandledReconcileAt holds the value of the most recent
|
||||
reconcile request value, so a change can be detected.
|
||||
reconcile request value, so a change of the annotation value can
|
||||
be detected.
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: ObservedGeneration is the last observed generation.
|
||||
|
@ -345,6 +346,350 @@ spec:
|
|||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
subresources:
|
||||
status: {}
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.url
|
||||
name: URL
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1beta2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: GitRepository is the Schema for the gitrepositories API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: GitRepositorySpec defines the desired state of a Git repository.
|
||||
properties:
|
||||
accessFrom:
|
||||
description: AccessFrom defines an Access Control List for allowing
|
||||
cross-namespace references to this object.
|
||||
properties:
|
||||
namespaceSelectors:
|
||||
description: NamespaceSelectors is the list of namespace selectors
|
||||
to which this ACL applies. Items in this list are evaluated
|
||||
using a logical OR operation.
|
||||
items:
|
||||
description: NamespaceSelector selects the namespaces to which
|
||||
this ACL applies. An empty map of MatchLabels matches all
|
||||
namespaces in a cluster.
|
||||
properties:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: MatchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field is
|
||||
"key", the operator is "In", and the values array contains
|
||||
only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- namespaceSelectors
|
||||
type: object
|
||||
gitImplementation:
|
||||
default: go-git
|
||||
description: Determines which git client library to use. Defaults
|
||||
to go-git, valid values are ('go-git', 'libgit2').
|
||||
enum:
|
||||
- go-git
|
||||
- libgit2
|
||||
type: string
|
||||
ignore:
|
||||
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: 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:
|
||||
fromPath:
|
||||
description: The path to copy contents from, defaults to the
|
||||
root directory.
|
||||
type: string
|
||||
repository:
|
||||
description: Reference to a GitRepository to include.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
toPath:
|
||||
description: The path to copy contents to, defaults to the name
|
||||
of the source ref.
|
||||
type: string
|
||||
required:
|
||||
- repository
|
||||
type: object
|
||||
type: array
|
||||
interval:
|
||||
description: The interval at which to check for repository updates.
|
||||
type: string
|
||||
recurseSubmodules:
|
||||
description: 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.
|
||||
type: boolean
|
||||
ref:
|
||||
description: The Git reference to checkout and monitor for changes,
|
||||
defaults to master branch.
|
||||
properties:
|
||||
branch:
|
||||
description: The Git branch to checkout, defaults to master.
|
||||
type: string
|
||||
commit:
|
||||
description: The Git commit SHA to checkout, if specified Tag
|
||||
filters will be ignored.
|
||||
type: string
|
||||
semver:
|
||||
description: The Git tag semver expression, takes precedence over
|
||||
Tag.
|
||||
type: string
|
||||
tag:
|
||||
description: The Git tag to checkout, takes precedence over Branch.
|
||||
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.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
suspend:
|
||||
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: 60s
|
||||
description: The timeout for remote Git operations like cloning, defaults
|
||||
to 60s.
|
||||
type: string
|
||||
url:
|
||||
description: The repository URL, can be a HTTP/S or SSH address.
|
||||
pattern: ^(http|https|ssh)://
|
||||
type: string
|
||||
verify:
|
||||
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').
|
||||
enum:
|
||||
- head
|
||||
type: string
|
||||
secretRef:
|
||||
description: SecretRef containing the public keys of all trusted
|
||||
Git authors.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
required:
|
||||
- mode
|
||||
type: object
|
||||
required:
|
||||
- interval
|
||||
- url
|
||||
type: object
|
||||
status:
|
||||
default:
|
||||
observedGeneration: -1
|
||||
description: GitRepositoryStatus defines the observed state of a Git repository.
|
||||
properties:
|
||||
artifact:
|
||||
description: Artifact represents the output of the last successful
|
||||
repository sync.
|
||||
properties:
|
||||
checksum:
|
||||
description: Checksum is the SHA256 checksum of the artifact.
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
description: LastUpdateTime is the timestamp corresponding to
|
||||
the last update of this artifact.
|
||||
format: date-time
|
||||
type: string
|
||||
path:
|
||||
description: Path is the relative file path of this Artifact.
|
||||
It can be used to locate the Artifact file in the root of the
|
||||
Artifact storage on the local file system of the controller
|
||||
managing the Source.
|
||||
type: string
|
||||
revision:
|
||||
description: Revision is a human readable identifier traceable
|
||||
in the origin source system. It can be a Git commit SHA, Git
|
||||
tag, a Helm index timestamp, a Helm chart version, etc.
|
||||
type: string
|
||||
url:
|
||||
description: URL is the HTTP address of this artifact. It is used
|
||||
by the consumers of the artifacts to fetch and use the artifacts.
|
||||
It is expected to be resolvable from within the cluster.
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
- url
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions holds the conditions for the GitRepository.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource. --- This struct is intended for direct
|
||||
use as an array at the field path .status.conditions. For example,
|
||||
type FooStatus struct{ // Represents the observations of a
|
||||
foo's current state. // Known .status.conditions.type are:
|
||||
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
|
||||
\ // +patchStrategy=merge // +listType=map // +listMapKey=type
|
||||
\ Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
|
||||
\n // other fields }"
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: lastTransitionTime is the last time the condition
|
||||
transitioned from one status to another. This should be when
|
||||
the underlying condition changed. If that is not known, then
|
||||
using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: message is a human readable message indicating
|
||||
details about the transition. This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration represents the .metadata.generation
|
||||
that the condition was set based upon. For instance, if .metadata.generation
|
||||
is currently 12, but the .status.conditions[x].observedGeneration
|
||||
is 9, the condition is out of date with respect to the current
|
||||
state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: reason contains a programmatic identifier indicating
|
||||
the reason for the condition's last transition. Producers
|
||||
of specific condition types may define expected values and
|
||||
meanings for this field, and whether the values are considered
|
||||
a guaranteed API. The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
--- Many .condition.type values are consistent across resources
|
||||
like Available, but because arbitrary conditions can be useful
|
||||
(see .node.status.conditions), the ability to deconflict is
|
||||
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
includedArtifacts:
|
||||
description: IncludedArtifacts represents the included artifacts from
|
||||
the last successful repository sync.
|
||||
items:
|
||||
description: Artifact represents the output of a Source synchronisation.
|
||||
properties:
|
||||
checksum:
|
||||
description: Checksum is the SHA256 checksum of the artifact.
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
description: LastUpdateTime is the timestamp corresponding to
|
||||
the last update of this artifact.
|
||||
format: date-time
|
||||
type: string
|
||||
path:
|
||||
description: Path is the relative file path of this Artifact.
|
||||
It can be used to locate the Artifact file in the root of
|
||||
the Artifact storage on the local file system of the controller
|
||||
managing the Source.
|
||||
type: string
|
||||
revision:
|
||||
description: Revision is a human readable identifier traceable
|
||||
in the origin source system. It can be a Git commit SHA, Git
|
||||
tag, a Helm index timestamp, a Helm chart version, etc.
|
||||
type: string
|
||||
url:
|
||||
description: URL is the HTTP address of this artifact. It is
|
||||
used by the consumers of the artifacts to fetch and use the
|
||||
artifacts. It is expected to be resolvable from within the
|
||||
cluster.
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
- url
|
||||
type: object
|
||||
type: array
|
||||
lastHandledReconcileAt:
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
description: URL is the fetch link for the artifact output of the
|
||||
last repository sync.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
|
@ -258,7 +258,8 @@ spec:
|
|||
type: array
|
||||
lastHandledReconcileAt:
|
||||
description: LastHandledReconcileAt holds the value of the most recent
|
||||
reconcile request value, so a change can be detected.
|
||||
reconcile request value, so a change of the annotation value can
|
||||
be detected.
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: ObservedGeneration is the last observed generation.
|
||||
|
@ -270,6 +271,275 @@ spec:
|
|||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
subresources:
|
||||
status: {}
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.chart
|
||||
name: Chart
|
||||
type: string
|
||||
- jsonPath: .spec.version
|
||||
name: Version
|
||||
type: string
|
||||
- jsonPath: .spec.sourceRef.kind
|
||||
name: Source Kind
|
||||
type: string
|
||||
- jsonPath: .spec.sourceRef.name
|
||||
name: Source Name
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1beta2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: HelmChart is the Schema for the helmcharts API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: HelmChartSpec defines the desired state of a Helm chart.
|
||||
properties:
|
||||
accessFrom:
|
||||
description: AccessFrom defines an Access Control List for allowing
|
||||
cross-namespace references to this object.
|
||||
properties:
|
||||
namespaceSelectors:
|
||||
description: NamespaceSelectors is the list of namespace selectors
|
||||
to which this ACL applies. Items in this list are evaluated
|
||||
using a logical OR operation.
|
||||
items:
|
||||
description: NamespaceSelector selects the namespaces to which
|
||||
this ACL applies. An empty map of MatchLabels matches all
|
||||
namespaces in a cluster.
|
||||
properties:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: MatchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field is
|
||||
"key", the operator is "In", and the values array contains
|
||||
only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- namespaceSelectors
|
||||
type: object
|
||||
chart:
|
||||
description: The name or path the Helm chart is available at in the
|
||||
SourceRef.
|
||||
type: string
|
||||
interval:
|
||||
description: The interval at which to check the Source for updates.
|
||||
type: string
|
||||
reconcileStrategy:
|
||||
default: ChartVersion
|
||||
description: Determines what enables the creation of a new artifact.
|
||||
Valid values are ('ChartVersion', 'Revision'). See the documentation
|
||||
of the values for an explanation on their behavior. Defaults to
|
||||
ChartVersion when omitted.
|
||||
enum:
|
||||
- ChartVersion
|
||||
- Revision
|
||||
type: string
|
||||
sourceRef:
|
||||
description: The reference to the Source the chart is available at.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: APIVersion of the referent.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referent, valid values are ('HelmRepository',
|
||||
'GitRepository', 'Bucket').
|
||||
enum:
|
||||
- HelmRepository
|
||||
- GitRepository
|
||||
- Bucket
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
suspend:
|
||||
description: This flag tells the controller to suspend the reconciliation
|
||||
of this source.
|
||||
type: boolean
|
||||
valuesFile:
|
||||
description: Alternative values file to use as the default chart values,
|
||||
expected to be a relative path in the SourceRef. Deprecated in favor
|
||||
of ValuesFiles, for backwards compatibility the file defined here
|
||||
is merged before the ValuesFiles items. Ignored when omitted.
|
||||
type: string
|
||||
valuesFiles:
|
||||
description: Alternative list of values files to use as the chart
|
||||
values (values.yaml is not included by default), expected to be
|
||||
a relative path in the SourceRef. Values files are merged in the
|
||||
order of this list with the last file overriding the first. Ignored
|
||||
when omitted.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
version:
|
||||
default: '*'
|
||||
description: The chart version semver expression, ignored for charts
|
||||
from GitRepository and Bucket sources. Defaults to latest when omitted.
|
||||
type: string
|
||||
required:
|
||||
- chart
|
||||
- interval
|
||||
- sourceRef
|
||||
type: object
|
||||
status:
|
||||
default:
|
||||
observedGeneration: -1
|
||||
description: HelmChartStatus defines the observed state of the HelmChart.
|
||||
properties:
|
||||
artifact:
|
||||
description: Artifact represents the output of the last successful
|
||||
chart sync.
|
||||
properties:
|
||||
checksum:
|
||||
description: Checksum is the SHA256 checksum of the artifact.
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
description: LastUpdateTime is the timestamp corresponding to
|
||||
the last update of this artifact.
|
||||
format: date-time
|
||||
type: string
|
||||
path:
|
||||
description: Path is the relative file path of this Artifact.
|
||||
It can be used to locate the Artifact file in the root of the
|
||||
Artifact storage on the local file system of the controller
|
||||
managing the Source.
|
||||
type: string
|
||||
revision:
|
||||
description: Revision is a human readable identifier traceable
|
||||
in the origin source system. It can be a Git commit SHA, Git
|
||||
tag, a Helm index timestamp, a Helm chart version, etc.
|
||||
type: string
|
||||
url:
|
||||
description: URL is the HTTP address of this artifact. It is used
|
||||
by the consumers of the artifacts to fetch and use the artifacts.
|
||||
It is expected to be resolvable from within the cluster.
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
- url
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions holds the conditions for the HelmChart.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource. --- This struct is intended for direct
|
||||
use as an array at the field path .status.conditions. For example,
|
||||
type FooStatus struct{ // Represents the observations of a
|
||||
foo's current state. // Known .status.conditions.type are:
|
||||
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
|
||||
\ // +patchStrategy=merge // +listType=map // +listMapKey=type
|
||||
\ Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
|
||||
\n // other fields }"
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: lastTransitionTime is the last time the condition
|
||||
transitioned from one status to another. This should be when
|
||||
the underlying condition changed. If that is not known, then
|
||||
using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: message is a human readable message indicating
|
||||
details about the transition. This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration represents the .metadata.generation
|
||||
that the condition was set based upon. For instance, if .metadata.generation
|
||||
is currently 12, but the .status.conditions[x].observedGeneration
|
||||
is 9, the condition is out of date with respect to the current
|
||||
state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: reason contains a programmatic identifier indicating
|
||||
the reason for the condition's last transition. Producers
|
||||
of specific condition types may define expected values and
|
||||
meanings for this field, and whether the values are considered
|
||||
a guaranteed API. The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
--- Many .condition.type values are consistent across resources
|
||||
like Available, but because arbitrary conditions can be useful
|
||||
(see .node.status.conditions), the ability to deconflict is
|
||||
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
lastHandledReconcileAt:
|
||||
description: LastHandledReconcileAt holds the value of the most recent
|
||||
reconcile request value, so a change of the annotation value can
|
||||
be detected.
|
||||
type: string
|
||||
observedChartName:
|
||||
description: ObservedChartName is the last observed chart name as
|
||||
defined by the resolved chart reference.
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: ObservedGeneration is the last observed generation.
|
||||
format: int64
|
||||
type: integer
|
||||
observedSourceArtifactRevision:
|
||||
description: ObservedSourceArtifactRevision is the last observed Artifact.Revision
|
||||
of the Source reference.
|
||||
type: string
|
||||
url:
|
||||
description: URL is the fetch link for the last chart pulled.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
|
@ -96,7 +96,7 @@ spec:
|
|||
certFile and keyFile, and/or caCert fields.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
@ -221,7 +221,8 @@ spec:
|
|||
type: array
|
||||
lastHandledReconcileAt:
|
||||
description: LastHandledReconcileAt holds the value of the most recent
|
||||
reconcile request value, so a change can be detected.
|
||||
reconcile request value, so a change of the annotation value can
|
||||
be detected.
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: ObservedGeneration is the last observed generation.
|
||||
|
@ -233,6 +234,230 @@ spec:
|
|||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
subresources:
|
||||
status: {}
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.url
|
||||
name: URL
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1beta2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: HelmRepository is the Schema for the helmrepositories API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: HelmRepositorySpec defines the reference to a Helm repository.
|
||||
properties:
|
||||
accessFrom:
|
||||
description: AccessFrom defines an Access Control List for allowing
|
||||
cross-namespace references to this object.
|
||||
properties:
|
||||
namespaceSelectors:
|
||||
description: NamespaceSelectors is the list of namespace selectors
|
||||
to which this ACL applies. Items in this list are evaluated
|
||||
using a logical OR operation.
|
||||
items:
|
||||
description: NamespaceSelector selects the namespaces to which
|
||||
this ACL applies. An empty map of MatchLabels matches all
|
||||
namespaces in a cluster.
|
||||
properties:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: MatchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field is
|
||||
"key", the operator is "In", and the values array contains
|
||||
only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- namespaceSelectors
|
||||
type: object
|
||||
interval:
|
||||
description: The interval at which to check the upstream for updates.
|
||||
type: string
|
||||
passCredentials:
|
||||
description: PassCredentials allows the credentials from the SecretRef
|
||||
to be passed on to a host that does not match the host as defined
|
||||
in URL. This may be required if the host of the advertised chart
|
||||
URLs in the index differ from the defined URL. Enabling this should
|
||||
be done with caution, as it can potentially result in credentials
|
||||
getting stolen in a MITM-attack.
|
||||
type: boolean
|
||||
secretRef:
|
||||
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.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
suspend:
|
||||
description: This flag tells the controller to suspend the reconciliation
|
||||
of this source.
|
||||
type: boolean
|
||||
timeout:
|
||||
default: 60s
|
||||
description: The timeout of index fetching, defaults to 60s.
|
||||
type: string
|
||||
url:
|
||||
description: The Helm repository URL, a valid URL contains at least
|
||||
a protocol and host.
|
||||
type: string
|
||||
required:
|
||||
- interval
|
||||
- url
|
||||
type: object
|
||||
status:
|
||||
default:
|
||||
observedGeneration: -1
|
||||
description: HelmRepositoryStatus defines the observed state of the HelmRepository.
|
||||
properties:
|
||||
artifact:
|
||||
description: Artifact represents the output of the last successful
|
||||
repository sync.
|
||||
properties:
|
||||
checksum:
|
||||
description: Checksum is the SHA256 checksum of the artifact.
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
description: LastUpdateTime is the timestamp corresponding to
|
||||
the last update of this artifact.
|
||||
format: date-time
|
||||
type: string
|
||||
path:
|
||||
description: Path is the relative file path of this Artifact.
|
||||
It can be used to locate the Artifact file in the root of the
|
||||
Artifact storage on the local file system of the controller
|
||||
managing the Source.
|
||||
type: string
|
||||
revision:
|
||||
description: Revision is a human readable identifier traceable
|
||||
in the origin source system. It can be a Git commit SHA, Git
|
||||
tag, a Helm index timestamp, a Helm chart version, etc.
|
||||
type: string
|
||||
url:
|
||||
description: URL is the HTTP address of this artifact. It is used
|
||||
by the consumers of the artifacts to fetch and use the artifacts.
|
||||
It is expected to be resolvable from within the cluster.
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
- url
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions holds the conditions for the HelmRepository.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource. --- This struct is intended for direct
|
||||
use as an array at the field path .status.conditions. For example,
|
||||
type FooStatus struct{ // Represents the observations of a
|
||||
foo's current state. // Known .status.conditions.type are:
|
||||
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
|
||||
\ // +patchStrategy=merge // +listType=map // +listMapKey=type
|
||||
\ Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
|
||||
\n // other fields }"
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: lastTransitionTime is the last time the condition
|
||||
transitioned from one status to another. This should be when
|
||||
the underlying condition changed. If that is not known, then
|
||||
using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: message is a human readable message indicating
|
||||
details about the transition. This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration represents the .metadata.generation
|
||||
that the condition was set based upon. For instance, if .metadata.generation
|
||||
is currently 12, but the .status.conditions[x].observedGeneration
|
||||
is 9, the condition is out of date with respect to the current
|
||||
state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: reason contains a programmatic identifier indicating
|
||||
the reason for the condition's last transition. Producers
|
||||
of specific condition types may define expected values and
|
||||
meanings for this field, and whether the values are considered
|
||||
a guaranteed API. The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
--- Many .condition.type values are consistent across resources
|
||||
like Available, but because arbitrary conditions can be useful
|
||||
(see .node.status.conditions), the ability to deconflict is
|
||||
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
lastHandledReconcileAt:
|
||||
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.
|
||||
format: int64
|
||||
type: integer
|
||||
url:
|
||||
description: URL is the fetch link for the last index fetched.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
|
|
@ -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"
|
||||
import sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
|
||||
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
|
||||
|
|
|
@ -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/v1beta2"
|
||||
. "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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -18,20 +18,18 @@ package controllers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
"github.com/fluxcd/pkg/runtime/logger"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
kuberecorder "k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/tools/reference"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
@ -40,16 +38,52 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
helper "github.com/fluxcd/pkg/runtime/controller"
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"github.com/fluxcd/pkg/runtime/metrics"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
"github.com/fluxcd/pkg/runtime/predicates"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
serror "github.com/fluxcd/source-controller/internal/error"
|
||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
||||
"github.com/fluxcd/source-controller/internal/util"
|
||||
"github.com/fluxcd/source-controller/pkg/git"
|
||||
"github.com/fluxcd/source-controller/pkg/git/strategy"
|
||||
"github.com/fluxcd/source-controller/pkg/sourceignore"
|
||||
)
|
||||
|
||||
// gitRepoReadyConditions contains all the conditions information needed
|
||||
// for GitRepository Ready status conditions summary calculation.
|
||||
var gitRepoReadyConditions = summarize.Conditions{
|
||||
Target: meta.ReadyCondition,
|
||||
Owned: []string{
|
||||
sourcev1.SourceVerifiedCondition,
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.ReadyCondition,
|
||||
meta.ReconcilingCondition,
|
||||
meta.StalledCondition,
|
||||
},
|
||||
Summarize: []string{
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.SourceVerifiedCondition,
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.StalledCondition,
|
||||
meta.ReconcilingCondition,
|
||||
},
|
||||
NegativePolarity: []string{
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.StalledCondition,
|
||||
meta.ReconcilingCondition,
|
||||
},
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories/finalizers,verbs=get;create;update;patch;delete
|
||||
|
@ -58,12 +92,13 @@ import (
|
|||
// GitRepositoryReconciler reconciles a GitRepository object
|
||||
type GitRepositoryReconciler struct {
|
||||
client.Client
|
||||
requeueDependency time.Duration
|
||||
Scheme *runtime.Scheme
|
||||
kuberecorder.EventRecorder
|
||||
helper.Metrics
|
||||
|
||||
Storage *Storage
|
||||
EventRecorder kuberecorder.EventRecorder
|
||||
ExternalEventRecorder *events.Recorder
|
||||
MetricsRecorder *metrics.Recorder
|
||||
ControllerName string
|
||||
|
||||
requeueDependency time.Duration
|
||||
}
|
||||
|
||||
type GitRepositoryReconcilerOptions struct {
|
||||
|
@ -71,6 +106,10 @@ type GitRepositoryReconcilerOptions struct {
|
|||
DependencyRequeueInterval time.Duration
|
||||
}
|
||||
|
||||
// gitRepoReconcilerFunc is the function type for all the Git repository
|
||||
// reconciler functions.
|
||||
type gitRepoReconcilerFunc func(ctx context.Context, obj *sourcev1.GitRepository, commit *git.Commit, includes *artifactSet, dir string) (sreconcile.Result, error)
|
||||
|
||||
func (r *GitRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return r.SetupWithManagerAndOptions(mgr, GitRepositoryReconcilerOptions{})
|
||||
}
|
||||
|
@ -86,409 +125,531 @@ func (r *GitRepositoryReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager, o
|
|||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, retErr error) {
|
||||
start := time.Now()
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
var repository sourcev1.GitRepository
|
||||
if err := r.Get(ctx, req.NamespacedName, &repository); err != nil {
|
||||
// Fetch the GitRepository
|
||||
obj := &sourcev1.GitRepository{}
|
||||
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
// Record suspended status metric
|
||||
defer r.recordSuspension(ctx, repository)
|
||||
r.RecordSuspend(ctx, obj, obj.Spec.Suspend)
|
||||
|
||||
// Add our finalizer if it does not exist
|
||||
if !controllerutil.ContainsFinalizer(&repository, sourcev1.SourceFinalizer) {
|
||||
patch := client.MergeFrom(repository.DeepCopy())
|
||||
controllerutil.AddFinalizer(&repository, sourcev1.SourceFinalizer)
|
||||
if err := r.Patch(ctx, &repository, patch); err != nil {
|
||||
log.Error(err, "unable to register finalizer")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Examine if the object is under deletion
|
||||
if !repository.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
return r.reconcileDelete(ctx, repository)
|
||||
}
|
||||
|
||||
// Return early if the object is suspended.
|
||||
if repository.Spec.Suspend {
|
||||
log.Info("Reconciliation is suspended for this object")
|
||||
// Return early if the object is suspended
|
||||
if obj.Spec.Suspend {
|
||||
log.Info("reconciliation is suspended for this object")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// check dependencies
|
||||
if len(repository.Spec.Include) > 0 {
|
||||
if err := r.checkDependencies(repository); err != nil {
|
||||
repository = sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, 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
|
||||
}
|
||||
// we can't rely on exponential backoff because it will prolong the execution too much,
|
||||
// instead we requeue on a fix interval.
|
||||
msg := fmt.Sprintf("Dependencies do not meet ready condition, retrying in %s", r.requeueDependency.String())
|
||||
log.Info(msg)
|
||||
r.event(ctx, repository, events.EventSeverityInfo, msg)
|
||||
r.recordReadiness(ctx, repository)
|
||||
return ctrl.Result{RequeueAfter: r.requeueDependency}, nil
|
||||
}
|
||||
log.Info("All dependencies area ready, proceeding with reconciliation")
|
||||
}
|
||||
|
||||
// record reconciliation duration
|
||||
if r.MetricsRecorder != nil {
|
||||
objRef, err := reference.GetReference(r.Scheme, &repository)
|
||||
// Initialize the patch helper with the current version of the object.
|
||||
patchHelper, err := patch.NewHelper(obj, r.Client)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
defer r.MetricsRecorder.RecordDuration(*objRef, start)
|
||||
}
|
||||
|
||||
// set initial status
|
||||
if resetRepository, ok := r.resetStatus(repository); ok {
|
||||
repository = resetRepository
|
||||
if err := r.updateStatus(ctx, req, repository.Status); err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
r.recordReadiness(ctx, repository)
|
||||
}
|
||||
// recResult stores the abstracted reconcile result.
|
||||
var recResult sreconcile.Result
|
||||
|
||||
// record the value of the reconciliation request, if any
|
||||
// TODO(hidde): would be better to defer this in combination with
|
||||
// always patching the status sub-resource after a reconciliation.
|
||||
if v, ok := meta.ReconcileAnnotationValue(repository.GetAnnotations()); ok {
|
||||
repository.Status.SetLastHandledReconcileRequest(v)
|
||||
}
|
||||
|
||||
// purge old artifacts from storage
|
||||
if err := r.gc(repository); err != nil {
|
||||
log.Error(err, "unable to purge old artifacts")
|
||||
}
|
||||
|
||||
// reconcile repository by pulling the latest Git commit
|
||||
reconciledRepository, reconcileErr := r.reconcile(ctx, *repository.DeepCopy())
|
||||
|
||||
// update status with the reconciliation result
|
||||
if err := r.updateStatus(ctx, req, reconciledRepository.Status); err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
// if reconciliation failed, record the failure and requeue immediately
|
||||
if reconcileErr != nil {
|
||||
r.event(ctx, reconciledRepository, events.EventSeverityError, reconcileErr.Error())
|
||||
r.recordReadiness(ctx, reconciledRepository)
|
||||
return ctrl.Result{Requeue: true}, reconcileErr
|
||||
}
|
||||
|
||||
// emit revision change event
|
||||
if repository.Status.Artifact == nil || reconciledRepository.Status.Artifact.Revision != repository.Status.Artifact.Revision {
|
||||
r.event(ctx, reconciledRepository, events.EventSeverityInfo, sourcev1.GitRepositoryReadyMessage(reconciledRepository))
|
||||
}
|
||||
r.recordReadiness(ctx, reconciledRepository)
|
||||
|
||||
log.Info(fmt.Sprintf("Reconciliation finished in %s, next run in %s",
|
||||
time.Since(start).String(),
|
||||
repository.GetInterval().Duration.String(),
|
||||
))
|
||||
|
||||
return ctrl.Result{RequeueAfter: repository.GetInterval().Duration}, nil
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) checkDependencies(repository sourcev1.GitRepository) error {
|
||||
for _, d := range repository.Spec.Include {
|
||||
dName := types.NamespacedName{Name: d.GitRepositoryRef.Name, Namespace: repository.Namespace}
|
||||
var gr sourcev1.GitRepository
|
||||
err := r.Get(context.Background(), dName, &gr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get '%s' dependency: %w", dName, err)
|
||||
}
|
||||
|
||||
if len(gr.Status.Conditions) == 0 || gr.Generation != gr.Status.ObservedGeneration {
|
||||
return fmt.Errorf("dependency '%s' is not ready", dName)
|
||||
}
|
||||
|
||||
if !apimeta.IsStatusConditionTrue(gr.Status.Conditions, meta.ReadyCondition) {
|
||||
return fmt.Errorf("dependency '%s' is not ready", dName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) reconcile(ctx context.Context, repository sourcev1.GitRepository) (sourcev1.GitRepository, error) {
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
// create tmp dir for the Git clone
|
||||
tmpGit, err := os.MkdirTemp("", repository.Name)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("tmp dir error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
// Always attempt to patch the object and status after each reconciliation
|
||||
// NOTE: The final runtime result and error are set in this block.
|
||||
defer func() {
|
||||
if err := os.RemoveAll(tmpGit); err != nil {
|
||||
log.Error(err, "failed to remove working directory", "path", tmpGit)
|
||||
summarizeHelper := summarize.NewHelper(r.EventRecorder, patchHelper)
|
||||
summarizeOpts := []summarize.Option{
|
||||
summarize.WithConditions(gitRepoReadyConditions),
|
||||
summarize.WithReconcileResult(recResult),
|
||||
summarize.WithReconcileError(retErr),
|
||||
summarize.WithIgnoreNotFound(),
|
||||
summarize.WithProcessors(
|
||||
summarize.RecordContextualError,
|
||||
summarize.RecordReconcileReq,
|
||||
),
|
||||
summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetInterval().Duration}),
|
||||
summarize.WithPatchFieldOwner(r.ControllerName),
|
||||
}
|
||||
result, retErr = summarizeHelper.SummarizeAndPatch(ctx, obj, summarizeOpts...)
|
||||
|
||||
// Always record readiness and duration metrics
|
||||
r.Metrics.RecordReadiness(ctx, obj)
|
||||
r.Metrics.RecordDuration(ctx, obj, start)
|
||||
}()
|
||||
|
||||
// Configure auth options using secret
|
||||
// Add finalizer first if not exist to avoid the race condition
|
||||
// between init and delete
|
||||
if !controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer) {
|
||||
controllerutil.AddFinalizer(obj, sourcev1.SourceFinalizer)
|
||||
recResult = sreconcile.ResultRequeue
|
||||
return
|
||||
}
|
||||
|
||||
// Examine if the object is under deletion
|
||||
if !obj.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
recResult, retErr = r.reconcileDelete(ctx, obj)
|
||||
return
|
||||
}
|
||||
|
||||
// Reconcile actual object
|
||||
reconcilers := []gitRepoReconcilerFunc{
|
||||
r.reconcileStorage,
|
||||
r.reconcileSource,
|
||||
r.reconcileInclude,
|
||||
r.reconcileArtifact,
|
||||
}
|
||||
recResult, retErr = r.reconcile(ctx, obj, reconcilers)
|
||||
return
|
||||
}
|
||||
|
||||
// reconcile steps iterates through the actual reconciliation tasks for objec,
|
||||
// it returns early on the first step that returns ResultRequeue or produces an
|
||||
// error.
|
||||
func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.GitRepository, reconcilers []gitRepoReconcilerFunc) (sreconcile.Result, error) {
|
||||
if obj.Generation != obj.Status.ObservedGeneration {
|
||||
conditions.MarkReconciling(obj, "NewGeneration", "reconciling new object generation (%d)", obj.Generation)
|
||||
}
|
||||
|
||||
var commit git.Commit
|
||||
var includes artifactSet
|
||||
|
||||
// Create temp dir for Git clone
|
||||
tmpDir, err := util.TempDirForObj("", obj)
|
||||
if err != nil {
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to create temporary directory: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Run the sub-reconcilers and build the result of reconciliation.
|
||||
var res sreconcile.Result
|
||||
var resErr error
|
||||
for _, rec := range reconcilers {
|
||||
recResult, err := rec(ctx, obj, &commit, &includes, tmpDir)
|
||||
// Exit immediately on ResultRequeue.
|
||||
if recResult == sreconcile.ResultRequeue {
|
||||
return sreconcile.ResultRequeue, nil
|
||||
}
|
||||
// If an error is received, prioritize the returned results because an
|
||||
// error also means immediate requeue.
|
||||
if err != nil {
|
||||
resErr = err
|
||||
res = recResult
|
||||
break
|
||||
}
|
||||
// Prioritize requeue request in the result.
|
||||
res = sreconcile.LowestRequeuingResult(res, recResult)
|
||||
}
|
||||
return res, resErr
|
||||
}
|
||||
|
||||
// reconcileStorage ensures the current state of the storage matches the desired and previously observed state.
|
||||
//
|
||||
// All artifacts for the resource except for the current one are garbage collected from the storage.
|
||||
// If the artifact in the Status object of the resource disappeared from storage, it is removed from the object.
|
||||
// If the object does not have an artifact in its Status object, a v1beta1.ArtifactUnavailableCondition is set.
|
||||
// If the hostname of any of the URLs on the object do not match the current storage server hostname, they are updated.
|
||||
func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context,
|
||||
obj *sourcev1.GitRepository, _ *git.Commit, includes *artifactSet, dir string) (sreconcile.Result, error) {
|
||||
// Garbage collect previous advertised artifact(s) from storage
|
||||
_ = r.garbageCollect(ctx, obj)
|
||||
|
||||
// Determine if the advertised artifact is still in storage
|
||||
if artifact := obj.GetArtifact(); artifact != nil && !r.Storage.ArtifactExist(*artifact) {
|
||||
obj.Status.Artifact = nil
|
||||
obj.Status.URL = ""
|
||||
}
|
||||
|
||||
// Record that we do not have an artifact
|
||||
if obj.GetArtifact() == nil {
|
||||
conditions.MarkReconciling(obj, "NoArtifact", "no artifact for resource in storage")
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// Always update URLs to ensure hostname is up-to-date
|
||||
// TODO(hidde): we may want to send out an event only if we notice the URL has changed
|
||||
r.Storage.SetArtifactURL(obj.GetArtifact())
|
||||
obj.Status.URL = r.Storage.SetHostname(obj.Status.URL)
|
||||
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileSource ensures the upstream Git repository can be reached and checked out using the declared configuration,
|
||||
// and observes its state.
|
||||
//
|
||||
// The repository is checked out to the given dir using the defined configuration, and in case of an error during the
|
||||
// checkout process (including transient errors), it records v1beta1.FetchFailedCondition=True and returns early.
|
||||
// On a successful checkout it removes v1beta1.FetchFailedCondition, and compares the current revision of HEAD to the
|
||||
// artifact on the object, and records v1beta1.ArtifactOutdatedCondition if they differ.
|
||||
// If instructed, the signature of the commit is verified if and recorded as v1beta1.SourceVerifiedCondition. If the
|
||||
// signature can not be verified or the verification fails, the Condition=False and it returns early.
|
||||
// If both the checkout and signature verification are successful, the given artifact pointer is set to a new artifact
|
||||
// with the available metadata.
|
||||
func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
|
||||
obj *sourcev1.GitRepository, commit *git.Commit, _ *artifactSet, dir string) (sreconcile.Result, error) {
|
||||
// Configure authentication strategy to access the source
|
||||
var authOpts *git.AuthOptions
|
||||
if repository.Spec.SecretRef != nil {
|
||||
var err error
|
||||
if obj.Spec.SecretRef != nil {
|
||||
// Attempt to retrieve secret
|
||||
name := types.NamespacedName{
|
||||
Namespace: repository.GetNamespace(),
|
||||
Name: repository.Spec.SecretRef.Name,
|
||||
Namespace: obj.GetNamespace(),
|
||||
Name: obj.Spec.SecretRef.Name,
|
||||
}
|
||||
var secret corev1.Secret
|
||||
if err := r.Client.Get(ctx, name, &secret); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to get secret '%s': %w", name.String(), err),
|
||||
Reason: sourcev1.AuthenticationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, e.Err.Error())
|
||||
// Return error as the world as observed may change
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{}
|
||||
err = r.Client.Get(ctx, name, secret)
|
||||
// Configure strategy with secret
|
||||
authOpts, err = git.AuthOptionsFromSecret(obj.Spec.URL, &secret)
|
||||
} else {
|
||||
// Set the minimal auth options for valid transport.
|
||||
authOpts, err = git.AuthOptionsWithoutSecret(obj.Spec.URL)
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("auth secret error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to configure auth strategy for Git implementation '%s': %w", obj.Spec.GitImplementation, err),
|
||||
Reason: sourcev1.AuthenticationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, e.Err.Error())
|
||||
// Return error as the contents of the secret may change
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
authOpts, err = git.AuthOptionsFromSecret(repository.Spec.URL, secret)
|
||||
if err != nil {
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
|
||||
}
|
||||
}
|
||||
checkoutOpts := git.CheckoutOptions{RecurseSubmodules: repository.Spec.RecurseSubmodules}
|
||||
if ref := repository.Spec.Reference; ref != nil {
|
||||
// Configure checkout strategy
|
||||
checkoutOpts := git.CheckoutOptions{RecurseSubmodules: obj.Spec.RecurseSubmodules}
|
||||
if ref := obj.Spec.Reference; ref != nil {
|
||||
checkoutOpts.Branch = ref.Branch
|
||||
checkoutOpts.Commit = ref.Commit
|
||||
checkoutOpts.Tag = ref.Tag
|
||||
checkoutOpts.SemVer = ref.SemVer
|
||||
}
|
||||
checkoutStrategy, err := strategy.CheckoutStrategyForImplementation(ctx,
|
||||
git.Implementation(repository.Spec.GitImplementation), checkoutOpts)
|
||||
git.Implementation(obj.Spec.GitImplementation), checkoutOpts)
|
||||
if err != nil {
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
e := &serror.Stalling{
|
||||
Err: fmt.Errorf("failed to configure checkout strategy for Git implementation '%s': %w", obj.Spec.GitImplementation, err),
|
||||
Reason: sourcev1.GitOperationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, e.Err.Error())
|
||||
// Do not return err as recovery without changes is impossible
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
gitCtx, cancel := context.WithTimeout(ctx, repository.Spec.Timeout.Duration)
|
||||
// Checkout HEAD of reference in object
|
||||
gitCtx, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
|
||||
defer cancel()
|
||||
|
||||
commit, err := checkoutStrategy.Checkout(gitCtx, tmpGit, repository.Spec.URL, authOpts)
|
||||
c, err := checkoutStrategy.Checkout(gitCtx, dir, obj.Spec.URL, authOpts)
|
||||
if err != nil {
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.GitOperationFailedReason, err.Error()), err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to checkout and determine revision: %w", err),
|
||||
Reason: sourcev1.GitOperationFailedReason,
|
||||
}
|
||||
artifact := r.Storage.NewArtifactFor(repository.Kind, repository.GetObjectMeta(), commit.String(), fmt.Sprintf("%s.tar.gz", commit.Hash.String()))
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, e.Err.Error())
|
||||
// Coin flip on transient or persistent error, return error and hope for the best
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
// Assign the commit to the shared commit reference.
|
||||
*commit = *c
|
||||
ctrl.LoggerFrom(ctx).V(logger.DebugLevel).Info("git repository checked out", "url", obj.Spec.URL, "revision", commit.String())
|
||||
conditions.Delete(obj, sourcev1.FetchFailedCondition)
|
||||
|
||||
// copy all included repository into the artifact
|
||||
includedArtifacts := []*sourcev1.Artifact{}
|
||||
for _, incl := range repository.Spec.Include {
|
||||
dName := types.NamespacedName{Name: incl.GitRepositoryRef.Name, Namespace: repository.Namespace}
|
||||
var gr sourcev1.GitRepository
|
||||
err := r.Get(context.Background(), dName, &gr)
|
||||
// Verify commit signature
|
||||
if result, err := r.verifyCommitSignature(ctx, obj, *commit); err != nil || result == sreconcile.ResultEmpty {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Mark observations about the revision on the object
|
||||
if !obj.GetArtifact().HasRevision(commit.String()) {
|
||||
message := fmt.Sprintf("new upstream revision '%s'", commit.String())
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", message)
|
||||
conditions.MarkReconciling(obj, "NewRevision", message)
|
||||
}
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileArtifact archives a new artifact to the storage, if the current observation on the object does not match the
|
||||
// given data.
|
||||
//
|
||||
// The inspection of the given data to the object is differed, ensuring any stale observations as
|
||||
// v1beta1.ArtifactUnavailableCondition and v1beta1.ArtifactOutdatedCondition are always deleted.
|
||||
// If the given artifact and/or includes do not differ from the object's current, it returns early.
|
||||
// Source ignore patterns are loaded, and the given directory is archived.
|
||||
// On a successful archive, the artifact and includes in the status of the given object are set, and the symlink in the
|
||||
// storage is updated to its path.
|
||||
func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
|
||||
obj *sourcev1.GitRepository, commit *git.Commit, includes *artifactSet, dir string) (sreconcile.Result, error) {
|
||||
// Create potential new artifact with current available metadata
|
||||
artifact := r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), commit.String(), fmt.Sprintf("%s.tar.gz", commit.Hash.String()))
|
||||
|
||||
// Always restore the Ready condition in case it got removed due to a transient error
|
||||
defer func() {
|
||||
if obj.GetArtifact().HasRevision(artifact.Revision) && !includes.Diff(obj.Status.IncludedArtifacts) {
|
||||
conditions.Delete(obj, sourcev1.ArtifactOutdatedCondition)
|
||||
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason,
|
||||
"stored artifact for revision '%s'", artifact.Revision)
|
||||
}
|
||||
}()
|
||||
|
||||
// The artifact is up-to-date
|
||||
if obj.GetArtifact().HasRevision(artifact.Revision) && !includes.Diff(obj.Status.IncludedArtifacts) {
|
||||
ctrl.LoggerFrom(ctx).Info("artifact up-to-date", "revision", artifact.Revision)
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// Mark reconciling because the artifact and remote source are different.
|
||||
// and they have to be reconciled.
|
||||
conditions.MarkReconciling(obj, "NewRevision", "new upstream revision '%s'", artifact.Revision)
|
||||
|
||||
// Ensure target path exists and is a directory
|
||||
if f, err := os.Stat(dir); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to stat target path: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
return sreconcile.ResultEmpty, e
|
||||
} else if !f.IsDir() {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("invalid target path: '%s' is not a directory", dir),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Ensure artifact directory exists and acquire lock
|
||||
if err := r.Storage.MkdirAll(artifact); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to create artifact directory: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
unlock, err := r.Storage.Lock(artifact)
|
||||
if err != nil {
|
||||
return sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()), err
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to acquire lock for artifact: %w", err),
|
||||
Reason: meta.FailedReason,
|
||||
}
|
||||
includedArtifacts = append(includedArtifacts, gr.GetArtifact())
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
// Load ignore rules for archiving
|
||||
ps, err := sourceignore.LoadIgnorePatterns(dir, nil)
|
||||
if err != nil {
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to load source ignore patterns from repository: %w", err),
|
||||
Reason: "SourceIgnoreError",
|
||||
}
|
||||
}
|
||||
if obj.Spec.Ignore != nil {
|
||||
ps = append(ps, sourceignore.ReadPatterns(strings.NewReader(*obj.Spec.Ignore), nil)...)
|
||||
}
|
||||
|
||||
// return early on unchanged revision and unchanged included repositories
|
||||
if apimeta.IsStatusConditionTrue(repository.Status.Conditions, meta.ReadyCondition) && repository.GetArtifact().HasRevision(artifact.Revision) && !hasArtifactUpdated(repository.Status.IncludedArtifacts, includedArtifacts) {
|
||||
if artifact.URL != repository.GetArtifact().URL {
|
||||
r.Storage.SetArtifactURL(repository.GetArtifact())
|
||||
repository.Status.URL = r.Storage.SetHostname(repository.Status.URL)
|
||||
// Archive directory to storage
|
||||
if err := r.Storage.Archive(&artifact, dir, SourceIgnoreFilter(ps, nil)); err != nil {
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("unable to archive artifact to storage: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
return repository, nil
|
||||
}
|
||||
r.AnnotatedEventf(obj, map[string]string{
|
||||
"revision": artifact.Revision,
|
||||
"checksum": artifact.Checksum,
|
||||
}, corev1.EventTypeNormal, "NewArtifact", "stored artifact for commit '%s'", commit.ShortMessage())
|
||||
|
||||
// Record it on the object
|
||||
obj.Status.Artifact = artifact.DeepCopy()
|
||||
obj.Status.IncludedArtifacts = *includes
|
||||
|
||||
// Update symlink on a "best effort" basis
|
||||
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
|
||||
if err != nil {
|
||||
r.eventLogf(ctx, obj, corev1.EventTypeWarning, sourcev1.StorageOperationFailedReason,
|
||||
"failed to update status URL symlink: %s", err)
|
||||
}
|
||||
if url != "" {
|
||||
obj.Status.URL = url
|
||||
}
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileInclude reconciles the declared includes from the object by copying their artifact (sub)contents to the
|
||||
// declared paths in the given directory.
|
||||
//
|
||||
// If an include is unavailable, it marks the object with v1beta1.IncludeUnavailableCondition and returns early.
|
||||
// If the copy operations are successful, it deletes the v1beta1.IncludeUnavailableCondition from the object.
|
||||
// If the artifactSet differs from the current set, it marks the object with v1beta1.ArtifactOutdatedCondition.
|
||||
func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context,
|
||||
obj *sourcev1.GitRepository, _ *git.Commit, includes *artifactSet, dir string) (sreconcile.Result, error) {
|
||||
artifacts := make(artifactSet, len(obj.Spec.Include))
|
||||
for i, incl := range obj.Spec.Include {
|
||||
// Do this first as it is much cheaper than copy operations
|
||||
toPath, err := securejoin.SecureJoin(dir, incl.GetToPath())
|
||||
if err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("path calculation for include '%s' failed: %w", incl.GitRepositoryRef.Name, err),
|
||||
Reason: "IllegalPath",
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "IllegalPath", e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// verify PGP signature
|
||||
if repository.Spec.Verification != nil {
|
||||
// Retrieve the included GitRepository
|
||||
dep := &sourcev1.GitRepository{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Namespace: obj.Namespace, Name: incl.GitRepositoryRef.Name}, dep); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("could not get resource for include '%s': %w", incl.GitRepositoryRef.Name, err),
|
||||
Reason: "NotFound",
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NotFound", e.Err.Error())
|
||||
return sreconcile.ResultEmpty, err
|
||||
}
|
||||
|
||||
// Confirm include has an artifact
|
||||
if dep.GetArtifact() == nil {
|
||||
e := &serror.Stalling{
|
||||
Err: fmt.Errorf("no artifact available for include '%s'", incl.GitRepositoryRef.Name),
|
||||
Reason: "NoArtifact",
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NoArtifact", e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Copy artifact (sub)contents to configured directory
|
||||
if err := r.Storage.CopyToPath(dep.GetArtifact(), incl.GetFromPath(), toPath); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to copy '%s' include from %s to %s: %w", incl.GitRepositoryRef.Name, incl.GetFromPath(), incl.GetToPath(), err),
|
||||
Reason: "CopyFailure",
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "CopyFailure", e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
artifacts[i] = dep.GetArtifact().DeepCopy()
|
||||
}
|
||||
|
||||
// We now know all includes are available
|
||||
conditions.Delete(obj, sourcev1.IncludeUnavailableCondition)
|
||||
|
||||
// Observe if the artifacts still match the previous included ones
|
||||
if artifacts.Diff(obj.Status.IncludedArtifacts) {
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "IncludeChange",
|
||||
"included artifacts differ from last observed includes")
|
||||
}
|
||||
|
||||
// Persist the artifactSet.
|
||||
*includes = artifacts
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileDelete handles the delete of an object. It first garbage collects all artifacts for the object from the
|
||||
// artifact storage, if successful, the finalizer is removed from the object.
|
||||
func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.GitRepository) (sreconcile.Result, error) {
|
||||
// Garbage collect the resource's artifacts
|
||||
if err := r.garbageCollect(ctx, obj); err != nil {
|
||||
// Return the error so we retry the failed garbage collection
|
||||
return sreconcile.ResultEmpty, err
|
||||
}
|
||||
|
||||
// Remove our finalizer from the list
|
||||
controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer)
|
||||
|
||||
// Stop reconciliation as the object is being deleted
|
||||
return sreconcile.ResultEmpty, nil
|
||||
}
|
||||
|
||||
// verifyCommitSignature verifies the signature of the given commit if a verification mode is configured on the object.
|
||||
func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj *sourcev1.GitRepository, commit git.Commit) (sreconcile.Result, error) {
|
||||
// Check if there is a commit verification is configured and remove any old observations if there is none
|
||||
if obj.Spec.Verification == nil || obj.Spec.Verification.Mode == "" {
|
||||
conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// Get secret with GPG data
|
||||
publicKeySecret := types.NamespacedName{
|
||||
Namespace: repository.Namespace,
|
||||
Name: repository.Spec.Verification.SecretRef.Name,
|
||||
Namespace: obj.Namespace,
|
||||
Name: obj.Spec.Verification.SecretRef.Name,
|
||||
}
|
||||
secret := &corev1.Secret{}
|
||||
if err := r.Client.Get(ctx, publicKeySecret, secret); err != nil {
|
||||
err = fmt.Errorf("PGP public keys secret error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.VerificationFailedReason, err.Error()), err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("PGP public keys secret error: %w", err),
|
||||
Reason: "VerificationError",
|
||||
}
|
||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, meta.FailedReason, e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
var keyRings []string
|
||||
for _, v := range secret.Data {
|
||||
keyRings = append(keyRings, string(v))
|
||||
}
|
||||
if _, err = commit.Verify(keyRings...); err != nil {
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.VerificationFailedReason, err.Error()), err
|
||||
// Verify commit with GPG data from secret
|
||||
if _, err := commit.Verify(keyRings...); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("signature verification of commit '%s' failed: %w", commit.Hash.String(), err),
|
||||
Reason: "InvalidCommitSignature",
|
||||
}
|
||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, meta.FailedReason, e.Err.Error())
|
||||
// Return error in the hope the secret changes
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// create artifact dir
|
||||
err = r.Storage.MkdirAll(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("mkdir dir error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
err = r.Storage.CopyToPath(includedArtifacts[i], incl.GetFromPath(), toPath)
|
||||
if err != nil {
|
||||
return sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()), err
|
||||
}
|
||||
}
|
||||
|
||||
// acquire lock
|
||||
unlock, err := r.Storage.Lock(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to acquire lock: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
// archive artifact and check integrity
|
||||
ignoreDomain := strings.Split(tmpGit, string(filepath.Separator))
|
||||
ps, err := sourceignore.LoadIgnorePatterns(tmpGit, ignoreDomain)
|
||||
if err != nil {
|
||||
err = fmt.Errorf(".sourceignore error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
if repository.Spec.Ignore != nil {
|
||||
ps = append(ps, sourceignore.ReadPatterns(strings.NewReader(*repository.Spec.Ignore), ignoreDomain)...)
|
||||
}
|
||||
if err := r.Storage.Archive(&artifact, tmpGit, SourceIgnoreFilter(ps, ignoreDomain)); err != nil {
|
||||
err = fmt.Errorf("storage archive error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// update latest symlink
|
||||
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("storage symlink error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Fetched revision: %s", artifact.Revision)
|
||||
return sourcev1.GitRepositoryReady(repository, artifact, includedArtifacts, url, sourcev1.GitOperationSucceedReason, message), nil
|
||||
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason,
|
||||
"verified signature of commit '%s'", commit.Hash.String())
|
||||
r.eventLogf(ctx, obj, events.EventTypeTrace, "VerifiedCommit",
|
||||
"verified signature of commit '%s'", commit.Hash.String())
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, repository sourcev1.GitRepository) (ctrl.Result, error) {
|
||||
if err := r.gc(repository); err != nil {
|
||||
r.event(ctx, repository, events.EventSeverityError,
|
||||
fmt.Sprintf("garbage collection for deleted resource failed: %s", err.Error()))
|
||||
// Return the error so we retry the failed garbage collection
|
||||
return ctrl.Result{}, err
|
||||
// garbageCollect performs a garbage collection for the given v1beta1.GitRepository. It removes all but the current
|
||||
// artifact except for when the deletion timestamp is set, which will result in the removal of all artifacts for the
|
||||
// resource.
|
||||
func (r *GitRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.GitRepository) error {
|
||||
if !obj.DeletionTimestamp.IsZero() {
|
||||
if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
|
||||
return &serror.Event{
|
||||
Err: fmt.Errorf("garbage collection for deleted resource failed: %w", err),
|
||||
Reason: "GarbageCollectionFailed",
|
||||
}
|
||||
|
||||
// Record deleted status
|
||||
r.recordReadiness(ctx, repository)
|
||||
|
||||
// Remove our finalizer from the list and update it
|
||||
controllerutil.RemoveFinalizer(&repository, sourcev1.SourceFinalizer)
|
||||
if err := r.Update(ctx, &repository); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
} else if deleted != "" {
|
||||
r.eventLogf(ctx, obj, events.EventTypeTrace, "GarbageCollectionSucceeded",
|
||||
"garbage collected artifacts for deleted resource")
|
||||
}
|
||||
|
||||
// Stop reconciliation as the object is being deleted
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// resetStatus returns a modified v1beta1.GitRepository and a boolean indicating
|
||||
// if the status field has been reset.
|
||||
func (r *GitRepositoryReconciler) resetStatus(repository sourcev1.GitRepository) (sourcev1.GitRepository, bool) {
|
||||
// We do not have an artifact, or it does no longer exist
|
||||
if repository.GetArtifact() == nil || !r.Storage.ArtifactExist(*repository.GetArtifact()) {
|
||||
repository = sourcev1.GitRepositoryProgressing(repository)
|
||||
repository.Status.Artifact = nil
|
||||
return repository, true
|
||||
obj.Status.Artifact = nil
|
||||
return nil
|
||||
}
|
||||
if repository.Generation != repository.Status.ObservedGeneration {
|
||||
return sourcev1.GitRepositoryProgressing(repository), true
|
||||
if obj.GetArtifact() != nil {
|
||||
if deleted, err := r.Storage.RemoveAllButCurrent(*obj.GetArtifact()); err != nil {
|
||||
return &serror.Event{
|
||||
Err: fmt.Errorf("garbage collection of old artifacts failed: %w", err),
|
||||
}
|
||||
return repository, false
|
||||
}
|
||||
|
||||
// gc performs a garbage collection for the given v1beta1.GitRepository.
|
||||
// It removes all but the current artifact except for when the
|
||||
// deletion timestamp is set, which will result in the removal of
|
||||
// all artifacts for the resource.
|
||||
func (r *GitRepositoryReconciler) gc(repository sourcev1.GitRepository) error {
|
||||
if !repository.DeletionTimestamp.IsZero() {
|
||||
return r.Storage.RemoveAll(r.Storage.NewArtifactFor(repository.Kind, repository.GetObjectMeta(), "", "*"))
|
||||
} else if len(deleted) > 0 {
|
||||
r.eventLogf(ctx, obj, events.EventTypeTrace, "GarbageCollectionSucceeded",
|
||||
"garbage collected old artifacts")
|
||||
}
|
||||
if repository.GetArtifact() != nil {
|
||||
return r.Storage.RemoveAllButCurrent(*repository.GetArtifact())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) recordReadiness(ctx context.Context, repository sourcev1.GitRepository) {
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
if r.MetricsRecorder == nil {
|
||||
return
|
||||
}
|
||||
objRef, err := reference.GetReference(r.Scheme, &repository)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to record readiness metric")
|
||||
return
|
||||
}
|
||||
if rc := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); rc != nil {
|
||||
r.MetricsRecorder.RecordCondition(*objRef, *rc, !repository.DeletionTimestamp.IsZero())
|
||||
// eventLog records event and logs at the same time. This log is different from
|
||||
// the debug log in the event recorder in the sense that this is a simple log,
|
||||
// the event recorder debug log contains complete details about the event.
|
||||
func (r *GitRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Object, eventType string, reason string, messageFmt string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(messageFmt, args...)
|
||||
// Log and emit event.
|
||||
if eventType == corev1.EventTypeWarning {
|
||||
ctrl.LoggerFrom(ctx).Error(errors.New(reason), msg)
|
||||
} else {
|
||||
r.MetricsRecorder.RecordCondition(*objRef, metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionUnknown,
|
||||
}, !repository.DeletionTimestamp.IsZero())
|
||||
ctrl.LoggerFrom(ctx).Info(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) recordSuspension(ctx context.Context, gitrepository sourcev1.GitRepository) {
|
||||
if r.MetricsRecorder == nil {
|
||||
return
|
||||
}
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
objRef, err := reference.GetReference(r.Scheme, &gitrepository)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to record suspended metric")
|
||||
return
|
||||
}
|
||||
|
||||
if !gitrepository.DeletionTimestamp.IsZero() {
|
||||
r.MetricsRecorder.RecordSuspend(*objRef, false)
|
||||
} else {
|
||||
r.MetricsRecorder.RecordSuspend(*objRef, gitrepository.Spec.Suspend)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) updateStatus(ctx context.Context, req ctrl.Request, newStatus sourcev1.GitRepositoryStatus) error {
|
||||
var repository sourcev1.GitRepository
|
||||
if err := r.Get(ctx, req.NamespacedName, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patch := client.MergeFrom(repository.DeepCopy())
|
||||
repository.Status = newStatus
|
||||
|
||||
return r.Status().Patch(ctx, &repository, patch)
|
||||
r.Eventf(obj, eventType, reason, msg)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -18,19 +18,18 @@ package controllers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
helmgetter "helm.sh/helm/v3/pkg/getter"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
kuberecorder "k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/tools/reference"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
|
@ -38,15 +37,45 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
helper "github.com/fluxcd/pkg/runtime/controller"
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"github.com/fluxcd/pkg/runtime/metrics"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
"github.com/fluxcd/pkg/runtime/predicates"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
serror "github.com/fluxcd/source-controller/internal/error"
|
||||
"github.com/fluxcd/source-controller/internal/helm/getter"
|
||||
"github.com/fluxcd/source-controller/internal/helm/repository"
|
||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
||||
)
|
||||
|
||||
// helmRepoReadyConditions contains all the conditions information needed
|
||||
// for HelmRepository Ready status conditions summary calculation.
|
||||
var helmRepoReadyConditions = summarize.Conditions{
|
||||
Target: meta.ReadyCondition,
|
||||
Owned: []string{
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.ReadyCondition,
|
||||
meta.ReconcilingCondition,
|
||||
meta.StalledCondition,
|
||||
},
|
||||
Summarize: []string{
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.StalledCondition,
|
||||
meta.ReconcilingCondition,
|
||||
},
|
||||
NegativePolarity: []string{
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.StalledCondition,
|
||||
meta.ReconcilingCondition,
|
||||
},
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmrepositories,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmrepositories/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmrepositories/finalizers,verbs=get;create;update;patch;delete
|
||||
|
@ -55,18 +84,23 @@ import (
|
|||
// HelmRepositoryReconciler reconciles a HelmRepository object
|
||||
type HelmRepositoryReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
Storage *Storage
|
||||
kuberecorder.EventRecorder
|
||||
helper.Metrics
|
||||
|
||||
Getters helmgetter.Providers
|
||||
EventRecorder kuberecorder.EventRecorder
|
||||
ExternalEventRecorder *events.Recorder
|
||||
MetricsRecorder *metrics.Recorder
|
||||
Storage *Storage
|
||||
ControllerName string
|
||||
}
|
||||
|
||||
type HelmRepositoryReconcilerOptions struct {
|
||||
MaxConcurrentReconciles int
|
||||
}
|
||||
|
||||
// helmRepoReconcilerFunc is the function type for all the helm repository
|
||||
// reconciler functions. The reconciler functions are grouped together and
|
||||
// executed serially to perform the main operation of the reconciler.
|
||||
type helmRepoReconcilerFunc func(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error)
|
||||
|
||||
func (r *HelmRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return r.SetupWithManagerAndOptions(mgr, HelmRepositoryReconcilerOptions{})
|
||||
}
|
||||
|
@ -79,327 +113,407 @@ func (r *HelmRepositoryReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager,
|
|||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, retErr error) {
|
||||
start := time.Now()
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
var repository sourcev1.HelmRepository
|
||||
if err := r.Get(ctx, req.NamespacedName, &repository); err != nil {
|
||||
// Fetch the HelmRepository
|
||||
obj := &sourcev1.HelmRepository{}
|
||||
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
// Record suspended status metric
|
||||
defer r.recordSuspension(ctx, repository)
|
||||
r.RecordSuspend(ctx, obj, obj.Spec.Suspend)
|
||||
|
||||
// Add our finalizer if it does not exist
|
||||
if !controllerutil.ContainsFinalizer(&repository, sourcev1.SourceFinalizer) {
|
||||
patch := client.MergeFrom(repository.DeepCopy())
|
||||
controllerutil.AddFinalizer(&repository, sourcev1.SourceFinalizer)
|
||||
if err := r.Patch(ctx, &repository, patch); err != nil {
|
||||
log.Error(err, "unable to register finalizer")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Examine if the object is under deletion
|
||||
if !repository.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
return r.reconcileDelete(ctx, repository)
|
||||
}
|
||||
|
||||
// Return early if the object is suspended.
|
||||
if repository.Spec.Suspend {
|
||||
log.Info("Reconciliation is suspended for this object")
|
||||
// Return early if the object is suspended
|
||||
if obj.Spec.Suspend {
|
||||
log.Info("reconciliation is suspended for this object")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// record reconciliation duration
|
||||
if r.MetricsRecorder != nil {
|
||||
objRef, err := reference.GetReference(r.Scheme, &repository)
|
||||
// Initialize the patch helper with the current version of the object.
|
||||
patchHelper, err := patch.NewHelper(obj, r.Client)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
defer r.MetricsRecorder.RecordDuration(*objRef, start)
|
||||
}
|
||||
|
||||
// set initial status
|
||||
if resetRepository, ok := r.resetStatus(repository); ok {
|
||||
repository = resetRepository
|
||||
if err := r.updateStatus(ctx, req, repository.Status); err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
r.recordReadiness(ctx, repository)
|
||||
}
|
||||
// recResult stores the abstracted reconcile result.
|
||||
var recResult sreconcile.Result
|
||||
|
||||
// record the value of the reconciliation request, if any
|
||||
// TODO(hidde): would be better to defer this in combination with
|
||||
// always patching the status sub-resource after a reconciliation.
|
||||
if v, ok := meta.ReconcileAnnotationValue(repository.GetAnnotations()); ok {
|
||||
repository.Status.SetLastHandledReconcileRequest(v)
|
||||
}
|
||||
|
||||
// purge old artifacts from storage
|
||||
if err := r.gc(repository); err != nil {
|
||||
log.Error(err, "unable to purge old artifacts")
|
||||
}
|
||||
|
||||
// reconcile repository by downloading the index.yaml file
|
||||
reconciledRepository, reconcileErr := r.reconcile(ctx, *repository.DeepCopy())
|
||||
|
||||
// update status with the reconciliation result
|
||||
if err := r.updateStatus(ctx, req, reconciledRepository.Status); err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
// if reconciliation failed, record the failure and requeue immediately
|
||||
if reconcileErr != nil {
|
||||
r.event(ctx, reconciledRepository, events.EventSeverityError, reconcileErr.Error())
|
||||
r.recordReadiness(ctx, reconciledRepository)
|
||||
return ctrl.Result{Requeue: true}, reconcileErr
|
||||
}
|
||||
|
||||
// emit revision change event
|
||||
if repository.Status.Artifact == nil || reconciledRepository.Status.Artifact.Revision != repository.Status.Artifact.Revision {
|
||||
r.event(ctx, reconciledRepository, events.EventSeverityInfo, sourcev1.HelmRepositoryReadyMessage(reconciledRepository))
|
||||
}
|
||||
r.recordReadiness(ctx, reconciledRepository)
|
||||
|
||||
log.Info(fmt.Sprintf("Reconciliation finished in %s, next run in %s",
|
||||
time.Since(start).String(),
|
||||
repository.GetInterval().Duration.String(),
|
||||
))
|
||||
|
||||
return ctrl.Result{RequeueAfter: repository.GetInterval().Duration}, nil
|
||||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, repo sourcev1.HelmRepository) (sourcev1.HelmRepository, error) {
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
clientOpts := []helmgetter.Option{
|
||||
helmgetter.WithURL(repo.Spec.URL),
|
||||
helmgetter.WithTimeout(repo.Spec.Timeout.Duration),
|
||||
helmgetter.WithPassCredentialsAll(repo.Spec.PassCredentials),
|
||||
}
|
||||
if repo.Spec.SecretRef != nil {
|
||||
name := types.NamespacedName{
|
||||
Namespace: repo.GetNamespace(),
|
||||
Name: repo.Spec.SecretRef.Name,
|
||||
}
|
||||
|
||||
var secret corev1.Secret
|
||||
err := r.Client.Get(ctx, name, &secret)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("auth secret error: %w", err)
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.AuthenticationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
authDir, err := os.MkdirTemp("", repo.Kind+"-"+repo.Namespace+"-"+repo.Name+"-")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to create temporary working directory for credentials: %w", err)
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.AuthenticationFailedReason, err.Error()), err
|
||||
}
|
||||
// Always attempt to patch the object after each reconciliation.
|
||||
// NOTE: The final runtime result and error are set in this block.
|
||||
defer func() {
|
||||
if err := os.RemoveAll(authDir); err != nil {
|
||||
log.Error(err, "failed to remove working directory", "path", authDir)
|
||||
summarizeHelper := summarize.NewHelper(r.EventRecorder, patchHelper)
|
||||
summarizeOpts := []summarize.Option{
|
||||
summarize.WithConditions(helmRepoReadyConditions),
|
||||
summarize.WithReconcileResult(recResult),
|
||||
summarize.WithReconcileError(retErr),
|
||||
summarize.WithIgnoreNotFound(),
|
||||
summarize.WithProcessors(
|
||||
summarize.RecordContextualError,
|
||||
summarize.RecordReconcileReq,
|
||||
),
|
||||
summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetInterval().Duration}),
|
||||
summarize.WithPatchFieldOwner(r.ControllerName),
|
||||
}
|
||||
result, retErr = summarizeHelper.SummarizeAndPatch(ctx, obj, summarizeOpts...)
|
||||
|
||||
// Always record readiness and duration metrics
|
||||
r.Metrics.RecordReadiness(ctx, obj)
|
||||
r.Metrics.RecordDuration(ctx, obj, start)
|
||||
}()
|
||||
|
||||
opts, err := getter.ClientOptionsFromSecret(authDir, secret)
|
||||
// Add finalizer first if not exist to avoid the race condition
|
||||
// between init and delete
|
||||
if !controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer) {
|
||||
controllerutil.AddFinalizer(obj, sourcev1.SourceFinalizer)
|
||||
recResult = sreconcile.ResultRequeue
|
||||
return
|
||||
}
|
||||
|
||||
// Examine if the object is under deletion
|
||||
if !obj.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
recResult, retErr = r.reconcileDelete(ctx, obj)
|
||||
return
|
||||
}
|
||||
|
||||
// Reconcile actual object
|
||||
reconcilers := []helmRepoReconcilerFunc{
|
||||
r.reconcileStorage,
|
||||
r.reconcileSource,
|
||||
r.reconcileArtifact,
|
||||
}
|
||||
recResult, retErr = r.reconcile(ctx, obj, reconcilers)
|
||||
return
|
||||
}
|
||||
|
||||
// reconcile iterates through the sub-reconcilers and processes the source
|
||||
// object. The sub-reconcilers are run sequentially. The result and error of
|
||||
// the sub-reconciliation are collected and returned. For multiple results
|
||||
// from different sub-reconcilers, the results are combined to return the
|
||||
// result with the shortest requeue period.
|
||||
func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.HelmRepository, reconcilers []helmRepoReconcilerFunc) (sreconcile.Result, error) {
|
||||
if obj.Generation != obj.Status.ObservedGeneration {
|
||||
conditions.MarkReconciling(obj, "NewGeneration", "reconciling new object generation (%d)", obj.Generation)
|
||||
}
|
||||
|
||||
var chartRepo repository.ChartRepository
|
||||
var artifact sourcev1.Artifact
|
||||
|
||||
// Run the sub-reconcilers and build the result of reconciliation.
|
||||
var res sreconcile.Result
|
||||
var resErr error
|
||||
for _, rec := range reconcilers {
|
||||
recResult, err := rec(ctx, obj, &artifact, &chartRepo)
|
||||
// Exit immediately on ResultRequeue.
|
||||
if recResult == sreconcile.ResultRequeue {
|
||||
return sreconcile.ResultRequeue, nil
|
||||
}
|
||||
// If an error is received, prioritize the returned results because an
|
||||
// error also means immediate requeue.
|
||||
if err != nil {
|
||||
err = fmt.Errorf("auth options error: %w", err)
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.AuthenticationFailedReason, err.Error()), err
|
||||
resErr = err
|
||||
res = recResult
|
||||
break
|
||||
}
|
||||
// Prioritize requeue request in the result for successful results.
|
||||
res = sreconcile.LowestRequeuingResult(res, recResult)
|
||||
}
|
||||
return res, resErr
|
||||
}
|
||||
|
||||
// reconcileStorage ensures the current state of the storage matches the desired and previously observed state.
|
||||
//
|
||||
// All artifacts for the resource except for the current one are garbage collected from the storage.
|
||||
// If the artifact in the Status object of the resource disappeared from storage, it is removed from the object.
|
||||
// If the hostname of the URLs on the object do not match the current storage server hostname, they are updated.
|
||||
func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
|
||||
// Garbage collect previous advertised artifact(s) from storage
|
||||
_ = r.garbageCollect(ctx, obj)
|
||||
|
||||
// Determine if the advertised artifact is still in storage
|
||||
if artifact := obj.GetArtifact(); artifact != nil && !r.Storage.ArtifactExist(*artifact) {
|
||||
obj.Status.Artifact = nil
|
||||
obj.Status.URL = ""
|
||||
}
|
||||
|
||||
// Record that we do not have an artifact
|
||||
if obj.GetArtifact() == nil {
|
||||
conditions.MarkReconciling(obj, "NoArtifact", "no artifact for resource in storage")
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// Always update URLs to ensure hostname is up-to-date
|
||||
// TODO(hidde): we may want to send out an event only if we notice the URL has changed
|
||||
r.Storage.SetArtifactURL(obj.GetArtifact())
|
||||
obj.Status.URL = r.Storage.SetHostname(obj.Status.URL)
|
||||
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileSource ensures the upstream Helm repository can be reached and downloaded out using the declared
|
||||
// configuration, and stores a new artifact in the storage.
|
||||
//
|
||||
// The Helm repository index is downloaded using the defined configuration, and in case of an error during this process
|
||||
// (including transient errors), it records v1beta1.FetchFailedCondition=True and returns early.
|
||||
// If the download is successful, the given artifact pointer is set to a new artifact with the available metadata, and
|
||||
// the index pointer is set to the newly downloaded index.
|
||||
func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
|
||||
// Configure Helm client to access repository
|
||||
clientOpts := []helmgetter.Option{
|
||||
helmgetter.WithTimeout(obj.Spec.Timeout.Duration),
|
||||
helmgetter.WithURL(obj.Spec.URL),
|
||||
helmgetter.WithPassCredentialsAll(obj.Spec.PassCredentials),
|
||||
}
|
||||
|
||||
// Configure any authentication related options
|
||||
if obj.Spec.SecretRef != nil {
|
||||
// Attempt to retrieve secret
|
||||
name := types.NamespacedName{
|
||||
Namespace: obj.GetNamespace(),
|
||||
Name: obj.Spec.SecretRef.Name,
|
||||
}
|
||||
var secret corev1.Secret
|
||||
if err := r.Client.Get(ctx, name, &secret); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to get secret '%s': %w", name.String(), err),
|
||||
Reason: sourcev1.AuthenticationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Get client options from secret
|
||||
tmpDir, err := os.MkdirTemp("", fmt.Sprintf("%s-%s-auth-", obj.Name, obj.Namespace))
|
||||
if err != nil {
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to create temporary directory for credentials: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Construct actual options
|
||||
opts, err := getter.ClientOptionsFromSecret(tmpDir, secret)
|
||||
if err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to configure Helm client with secret data: %w", err),
|
||||
Reason: sourcev1.AuthenticationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, e.Err.Error())
|
||||
// Return err as the content of the secret may change.
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
clientOpts = append(clientOpts, opts...)
|
||||
}
|
||||
|
||||
chartRepo, err := repository.NewChartRepository(repo.Spec.URL, "", r.Getters, clientOpts)
|
||||
// Construct Helm chart repository with options and download index
|
||||
newChartRepo, err := repository.NewChartRepository(obj.Spec.URL, "", r.Getters, clientOpts)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *url.Error:
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.URLInvalidReason, err.Error()), err
|
||||
e := &serror.Stalling{
|
||||
Err: fmt.Errorf("invalid Helm repository URL: %w", err),
|
||||
Reason: sourcev1.URLInvalidReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.URLInvalidReason, e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
default:
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.IndexationFailedReason, err.Error()), err
|
||||
e := &serror.Stalling{
|
||||
Err: fmt.Errorf("failed to construct Helm client: %w", err),
|
||||
Reason: meta.FailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, meta.FailedReason, e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
}
|
||||
checksum, err := chartRepo.CacheIndex()
|
||||
checksum, err := newChartRepo.CacheIndex()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to download repository index: %w", err)
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.IndexationFailedReason, err.Error()), err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to fetch Helm repository index: %w", err),
|
||||
Reason: meta.FailedReason,
|
||||
}
|
||||
defer chartRepo.RemoveCache()
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, meta.FailedReason, e.Err.Error())
|
||||
// Coin flip on transient or persistent error, return error and hope for the best
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
*chartRepo = *newChartRepo
|
||||
|
||||
artifact := r.Storage.NewArtifactFor(repo.Kind,
|
||||
repo.ObjectMeta.GetObjectMeta(),
|
||||
"",
|
||||
// Load the cached repository index to ensure it passes validation.
|
||||
if err := chartRepo.LoadFromCache(); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to load Helm repository from cache: %w", err),
|
||||
Reason: sourcev1.FetchFailedCondition,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.IndexationFailedReason, e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
defer chartRepo.Unload()
|
||||
|
||||
// Mark observations about the revision on the object.
|
||||
if !obj.GetArtifact().HasRevision(checksum) {
|
||||
message := fmt.Sprintf("new index revision '%s'", checksum)
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", message)
|
||||
conditions.MarkReconciling(obj, "NewRevision", message)
|
||||
}
|
||||
|
||||
conditions.Delete(obj, sourcev1.FetchFailedCondition)
|
||||
|
||||
// Create potential new artifact.
|
||||
*artifact = r.Storage.NewArtifactFor(obj.Kind,
|
||||
obj.ObjectMeta.GetObjectMeta(),
|
||||
chartRepo.Checksum,
|
||||
fmt.Sprintf("index-%s.yaml", checksum))
|
||||
|
||||
// Return early on unchanged index
|
||||
if apimeta.IsStatusConditionTrue(repo.Status.Conditions, meta.ReadyCondition) &&
|
||||
(repo.GetArtifact() != nil && repo.GetArtifact().Checksum == checksum) {
|
||||
if artifact.URL != repo.GetArtifact().URL {
|
||||
r.Storage.SetArtifactURL(repo.GetArtifact())
|
||||
repo.Status.URL = r.Storage.SetHostname(repo.Status.URL)
|
||||
}
|
||||
return repo, nil
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileArtifact stores a new artifact in the storage, if the current observation on the object does not match the
|
||||
// given data.
|
||||
//
|
||||
// The inspection of the given data to the object is differed, ensuring any stale observations as
|
||||
// v1beta1.ArtifactUnavailableCondition and v1beta1.ArtifactOutdatedCondition are always deleted.
|
||||
// If the given artifact does not differ from the object's current, it returns early.
|
||||
// On a successful write of a new artifact, the artifact in the status of the given object is set, and the symlink in
|
||||
// the storage is updated to its path.
|
||||
func (r *HelmRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
|
||||
// Always restore the Ready condition in case it got removed due to a transient error.
|
||||
defer func() {
|
||||
if obj.GetArtifact().HasRevision(artifact.Revision) {
|
||||
conditions.Delete(obj, sourcev1.ArtifactOutdatedCondition)
|
||||
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason,
|
||||
"stored artifact for revision '%s'", artifact.Revision)
|
||||
}
|
||||
|
||||
// Load the cached repository index to ensure it passes validation
|
||||
if err := chartRepo.LoadFromCache(); err != nil {
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.IndexationFailedReason, err.Error()), err
|
||||
if err := chartRepo.RemoveCache(); err != nil {
|
||||
ctrl.LoggerFrom(ctx).Error(err, "failed to remove temporary cached index file")
|
||||
}
|
||||
// The repository checksum is the SHA256 of the loaded bytes, after sorting
|
||||
artifact.Revision = chartRepo.Checksum
|
||||
chartRepo.Unload()
|
||||
}()
|
||||
|
||||
if obj.GetArtifact().HasRevision(artifact.Revision) {
|
||||
ctrl.LoggerFrom(ctx).Info("artifact up-to-date", "revision", artifact.Revision)
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// Mark reconciling because the artifact and remote source are different.
|
||||
// and they have to be reconciled.
|
||||
conditions.MarkReconciling(obj, "NewRevision", "new index revision '%s'", artifact.Revision)
|
||||
|
||||
// Create artifact dir
|
||||
err = r.Storage.MkdirAll(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create repository index directory: %w", err)
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
if err := r.Storage.MkdirAll(*artifact); err != nil {
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to create artifact directory: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire lock
|
||||
unlock, err := r.Storage.Lock(artifact)
|
||||
// Acquire lock.
|
||||
unlock, err := r.Storage.Lock(*artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to acquire lock: %w", err)
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to acquire lock for artifact: %w", err),
|
||||
Reason: meta.FailedReason,
|
||||
}
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
// Save artifact to storage
|
||||
if err = r.Storage.CopyFromPath(&artifact, chartRepo.CachePath); err != nil {
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
// Save artifact to storage.
|
||||
if err = r.Storage.CopyFromPath(artifact, chartRepo.CachePath); err != nil {
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("unable to save artifact to storage: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
}
|
||||
|
||||
// Update index symlink
|
||||
indexURL, err := r.Storage.Symlink(artifact, "index.yaml")
|
||||
// Calculate the artifact size to be included in the NewArtifact event.
|
||||
fi, err := os.Stat(chartRepo.CachePath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("storage error: %w", err)
|
||||
return sourcev1.HelmRepositoryNotReady(repo, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("unable to read the artifact: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
}
|
||||
size := units.HumanSize(float64(fi.Size()))
|
||||
|
||||
r.AnnotatedEventf(obj, map[string]string{
|
||||
"revision": artifact.Revision,
|
||||
"checksum": artifact.Checksum,
|
||||
}, corev1.EventTypeNormal, "NewArtifact", "fetched index of size %s from '%s'", size, chartRepo.URL)
|
||||
|
||||
// Record it on the object.
|
||||
obj.Status.Artifact = artifact.DeepCopy()
|
||||
|
||||
// Update index symlink.
|
||||
indexURL, err := r.Storage.Symlink(*artifact, "index.yaml")
|
||||
if err != nil {
|
||||
r.eventLogf(ctx, obj, corev1.EventTypeWarning, sourcev1.StorageOperationFailedReason,
|
||||
"failed to update status URL symlink: %s", err)
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Fetched revision: %s", artifact.Revision)
|
||||
return sourcev1.HelmRepositoryReady(repo, artifact, indexURL, sourcev1.IndexationSucceededReason, message), nil
|
||||
if indexURL != "" {
|
||||
obj.Status.URL = indexURL
|
||||
}
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) reconcileDelete(ctx context.Context, repository sourcev1.HelmRepository) (ctrl.Result, error) {
|
||||
// Our finalizer is still present, so lets handle garbage collection
|
||||
if err := r.gc(repository); err != nil {
|
||||
r.event(ctx, repository, events.EventSeverityError,
|
||||
fmt.Sprintf("garbage collection for deleted resource failed: %s", err.Error()))
|
||||
// reconcileDelete handles the delete of an object. It first garbage collects all artifacts for the object from the
|
||||
// artifact storage, if successful, the finalizer is removed from the object.
|
||||
func (r *HelmRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.HelmRepository) (sreconcile.Result, error) {
|
||||
// Garbage collect the resource's artifacts
|
||||
if err := r.garbageCollect(ctx, obj); err != nil {
|
||||
// Return the error so we retry the failed garbage collection
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, err
|
||||
}
|
||||
|
||||
// Record deleted status
|
||||
r.recordReadiness(ctx, repository)
|
||||
|
||||
// Remove our finalizer from the list and update it
|
||||
controllerutil.RemoveFinalizer(&repository, sourcev1.SourceFinalizer)
|
||||
if err := r.Update(ctx, &repository); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
// Remove our finalizer from the list
|
||||
controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer)
|
||||
|
||||
// Stop reconciliation as the object is being deleted
|
||||
return ctrl.Result{}, nil
|
||||
return sreconcile.ResultEmpty, nil
|
||||
}
|
||||
|
||||
// resetStatus returns a modified v1beta1.HelmRepository and a boolean indicating
|
||||
// if the status field has been reset.
|
||||
func (r *HelmRepositoryReconciler) resetStatus(repository sourcev1.HelmRepository) (sourcev1.HelmRepository, bool) {
|
||||
// We do not have an artifact, or it does no longer exist
|
||||
if repository.GetArtifact() == nil || !r.Storage.ArtifactExist(*repository.GetArtifact()) {
|
||||
repository = sourcev1.HelmRepositoryProgressing(repository)
|
||||
repository.Status.Artifact = nil
|
||||
return repository, true
|
||||
// garbageCollect performs a garbage collection for the given v1beta1.HelmRepository. It removes all but the current
|
||||
// artifact except for when the deletion timestamp is set, which will result in the removal of all artifacts for the
|
||||
// resource.
|
||||
func (r *HelmRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.HelmRepository) error {
|
||||
if !obj.DeletionTimestamp.IsZero() {
|
||||
if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
|
||||
return &serror.Event{
|
||||
Err: fmt.Errorf("garbage collection for deleted resource failed: %w", err),
|
||||
Reason: "GarbageCollectionFailed",
|
||||
}
|
||||
if repository.Generation != repository.Status.ObservedGeneration {
|
||||
return sourcev1.HelmRepositoryProgressing(repository), true
|
||||
} else if deleted != "" {
|
||||
r.eventLogf(ctx, obj, events.EventTypeTrace, "GarbageCollectionSucceeded",
|
||||
"garbage collected artifacts for deleted resource")
|
||||
}
|
||||
return repository, false
|
||||
}
|
||||
|
||||
// gc performs a garbage collection for the given v1beta1.HelmRepository.
|
||||
// It removes all but the current artifact except for when the
|
||||
// deletion timestamp is set, which will result in the removal of
|
||||
// all artifacts for the resource.
|
||||
func (r *HelmRepositoryReconciler) gc(repository sourcev1.HelmRepository) error {
|
||||
if !repository.DeletionTimestamp.IsZero() {
|
||||
return r.Storage.RemoveAll(r.Storage.NewArtifactFor(repository.Kind, repository.GetObjectMeta(), "", "*"))
|
||||
obj.Status.Artifact = nil
|
||||
return nil
|
||||
}
|
||||
if obj.GetArtifact() != nil {
|
||||
if deleted, err := r.Storage.RemoveAllButCurrent(*obj.GetArtifact()); err != nil {
|
||||
return &serror.Event{
|
||||
Err: fmt.Errorf("garbage collection of old artifacts failed: %w", err),
|
||||
Reason: "GarbageCollectionFailed",
|
||||
}
|
||||
} else if len(deleted) > 0 {
|
||||
r.eventLogf(ctx, obj, events.EventTypeTrace, "GarbageCollectionSucceeded",
|
||||
"garbage collected old artifacts")
|
||||
}
|
||||
if repository.GetArtifact() != nil {
|
||||
return r.Storage.RemoveAllButCurrent(*repository.GetArtifact())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) recordReadiness(ctx context.Context, repository sourcev1.HelmRepository) {
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
if r.MetricsRecorder == nil {
|
||||
return
|
||||
}
|
||||
objRef, err := reference.GetReference(r.Scheme, &repository)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to record readiness metric")
|
||||
return
|
||||
}
|
||||
if rc := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); rc != nil {
|
||||
r.MetricsRecorder.RecordCondition(*objRef, *rc, !repository.DeletionTimestamp.IsZero())
|
||||
// eventLog records event and logs at the same time. This log is different from
|
||||
// the debug log in the event recorder in the sense that this is a simple log,
|
||||
// the event recorder debug log contains complete details about the event.
|
||||
func (r *HelmRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Object, eventType string, reason string, messageFmt string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(messageFmt, args...)
|
||||
// Log and emit event.
|
||||
if eventType == corev1.EventTypeWarning {
|
||||
ctrl.LoggerFrom(ctx).Error(errors.New(reason), msg)
|
||||
} else {
|
||||
r.MetricsRecorder.RecordCondition(*objRef, metav1.Condition{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionUnknown,
|
||||
}, !repository.DeletionTimestamp.IsZero())
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) updateStatus(ctx context.Context, req ctrl.Request, newStatus sourcev1.HelmRepositoryStatus) error {
|
||||
var repository sourcev1.HelmRepository
|
||||
if err := r.Get(ctx, req.NamespacedName, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patch := client.MergeFrom(repository.DeepCopy())
|
||||
repository.Status = newStatus
|
||||
|
||||
return r.Status().Patch(ctx, &repository, patch)
|
||||
}
|
||||
|
||||
func (r *HelmRepositoryReconciler) recordSuspension(ctx context.Context, hr sourcev1.HelmRepository) {
|
||||
if r.MetricsRecorder == nil {
|
||||
return
|
||||
}
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
objRef, err := reference.GetReference(r.Scheme, &hr)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to record suspended metric")
|
||||
return
|
||||
}
|
||||
|
||||
if !hr.DeletionTimestamp.IsZero() {
|
||||
r.MetricsRecorder.RecordSuspend(*objRef, false)
|
||||
} else {
|
||||
r.MetricsRecorder.RecordSuspend(*objRef, hr.Spec.Suspend)
|
||||
ctrl.LoggerFrom(ctx).Info(msg)
|
||||
}
|
||||
r.Eventf(obj, eventType, reason, msg)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,7 +20,7 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
)
|
||||
|
||||
type SourceRevisionChangePredicate struct {
|
||||
|
|
|
@ -36,7 +36,7 @@ import (
|
|||
"github.com/fluxcd/pkg/lockedfile"
|
||||
|
||||
"github.com/fluxcd/pkg/untar"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
"github.com/fluxcd/source-controller/internal/fs"
|
||||
"github.com/fluxcd/source-controller/pkg/sourceignore"
|
||||
)
|
||||
|
@ -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.
|
||||
|
@ -101,13 +105,20 @@ func (s *Storage) MkdirAll(artifact sourcev1.Artifact) error {
|
|||
}
|
||||
|
||||
// RemoveAll calls os.RemoveAll for the given v1beta1.Artifact base dir.
|
||||
func (s *Storage) RemoveAll(artifact sourcev1.Artifact) error {
|
||||
func (s *Storage) RemoveAll(artifact sourcev1.Artifact) (string, error) {
|
||||
var deletedDir string
|
||||
dir := filepath.Dir(s.LocalPath(artifact))
|
||||
return os.RemoveAll(dir)
|
||||
// Check if the dir exists.
|
||||
_, err := os.Stat(dir)
|
||||
if err == nil {
|
||||
deletedDir = dir
|
||||
}
|
||||
return deletedDir, os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
// RemoveAllButCurrent removes all files for the given v1beta1.Artifact base dir, excluding the current one.
|
||||
func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) error {
|
||||
func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) ([]string, error) {
|
||||
deletedFiles := []string{}
|
||||
localPath := s.LocalPath(artifact)
|
||||
dir := filepath.Dir(localPath)
|
||||
var errors []string
|
||||
|
@ -120,15 +131,18 @@ func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) error {
|
|||
if path != localPath && !info.IsDir() && info.Mode()&os.ModeSymlink != os.ModeSymlink {
|
||||
if err := os.Remove(path); err != nil {
|
||||
errors = append(errors, info.Name())
|
||||
} else {
|
||||
// Collect the successfully deleted file paths.
|
||||
deletedFiles = append(deletedFiles, path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf("failed to remove files: %s", strings.Join(errors, " "))
|
||||
return deletedFiles, fmt.Errorf("failed to remove files: %s", strings.Join(errors, " "))
|
||||
}
|
||||
return nil
|
||||
return deletedFiles, nil
|
||||
}
|
||||
|
||||
// ArtifactExist returns a boolean indicating whether the v1beta1.Artifact exists in storage and is a regular file.
|
||||
|
|
|
@ -28,8 +28,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
)
|
||||
|
||||
func createStoragePath() (string, error) {
|
||||
|
@ -293,10 +294,96 @@ func TestStorageRemoveAllButCurrent(t *testing.T) {
|
|||
t.Fatalf("Valid path did not successfully return: %v", err)
|
||||
}
|
||||
|
||||
if err := s.RemoveAllButCurrent(sourcev1.Artifact{Path: path.Join(dir, "really", "nonexistent")}); err == nil {
|
||||
if _, err := s.RemoveAllButCurrent(sourcev1.Artifact{Path: path.Join(dir, "really", "nonexistent")}); err == nil {
|
||||
t.Fatal("Did not error while pruning non-existent path")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("collect names of deleted items", func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
t.Cleanup(func() { os.RemoveAll(dir) })
|
||||
|
||||
s, err := NewStorage(dir, "hostname", time.Minute)
|
||||
g.Expect(err).ToNot(HaveOccurred(), "failed to create new storage")
|
||||
|
||||
artifact := sourcev1.Artifact{
|
||||
Path: path.Join("foo", "bar", "artifact1.tar.gz"),
|
||||
}
|
||||
|
||||
// Create artifact dir and artifacts.
|
||||
artifactDir := path.Join(dir, "foo", "bar")
|
||||
g.Expect(os.MkdirAll(artifactDir, 0755)).NotTo(HaveOccurred())
|
||||
current := []string{
|
||||
path.Join(artifactDir, "artifact1.tar.gz"),
|
||||
}
|
||||
wantDeleted := []string{
|
||||
path.Join(artifactDir, "file1.txt"),
|
||||
path.Join(artifactDir, "file2.txt"),
|
||||
}
|
||||
createFile := func(files []string) {
|
||||
for _, c := range files {
|
||||
f, err := os.Create(c)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(f.Close()).ToNot(HaveOccurred())
|
||||
}
|
||||
}
|
||||
createFile(current)
|
||||
createFile(wantDeleted)
|
||||
_, err = s.Symlink(artifact, "latest.tar.gz")
|
||||
g.Expect(err).ToNot(HaveOccurred(), "failed to create symlink")
|
||||
|
||||
deleted, err := s.RemoveAllButCurrent(artifact)
|
||||
g.Expect(err).ToNot(HaveOccurred(), "failed to remove all but current")
|
||||
g.Expect(deleted).To(Equal(wantDeleted))
|
||||
})
|
||||
}
|
||||
|
||||
func TestStorageRemoveAll(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
artifactPath string
|
||||
createArtifactPath bool
|
||||
wantDeleted string
|
||||
}{
|
||||
{
|
||||
name: "delete non-existent path",
|
||||
artifactPath: path.Join("foo", "bar", "artifact1.tar.gz"),
|
||||
createArtifactPath: false,
|
||||
wantDeleted: "",
|
||||
},
|
||||
{
|
||||
name: "delete existing path",
|
||||
artifactPath: path.Join("foo", "bar", "artifact1.tar.gz"),
|
||||
createArtifactPath: true,
|
||||
wantDeleted: path.Join("foo", "bar"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
t.Cleanup(func() { os.RemoveAll(dir) })
|
||||
|
||||
s, err := NewStorage(dir, "hostname", time.Minute)
|
||||
g.Expect(err).ToNot(HaveOccurred(), "failed to create new storage")
|
||||
|
||||
artifact := sourcev1.Artifact{
|
||||
Path: tt.artifactPath,
|
||||
}
|
||||
|
||||
if tt.createArtifactPath {
|
||||
g.Expect(os.MkdirAll(path.Join(dir, tt.artifactPath), 0755)).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
deleted, err := s.RemoveAll(artifact)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(deleted).To(ContainSubstring(tt.wantDeleted), "unexpected deleted path")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageCopyFromPath(t *testing.T) {
|
||||
|
@ -317,15 +404,11 @@ func TestStorageCopyFromPath(t *testing.T) {
|
|||
}
|
||||
|
||||
createFile := func(file *File) (absPath string, err error) {
|
||||
defer func() {
|
||||
if err != nil && dir != "" {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
}()
|
||||
dir, err = os.MkdirTemp("", "test-files-")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.Cleanup(cleanupStoragePath(dir))
|
||||
absPath = filepath.Join(dir, file.Name)
|
||||
if err = os.MkdirAll(filepath.Dir(absPath), 0755); err != nil {
|
||||
return
|
||||
|
|
|
@ -17,178 +17,171 @@ 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"
|
||||
"k8s.io/client-go/tools/record"
|
||||
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"
|
||||
"github.com/fluxcd/pkg/runtime/controller"
|
||||
"github.com/fluxcd/pkg/runtime/testenv"
|
||||
"github.com/fluxcd/pkg/testserver"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
// +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()
|
||||
)
|
||||
|
||||
const timeout = time.Second * 60
|
||||
|
||||
func TestAPIs(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
RunSpecsWithDefaultAndCustomReporters(t,
|
||||
"Controller Suite",
|
||||
[]Reporter{printer.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
done := make(chan interface{})
|
||||
go func() {
|
||||
close(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")},
|
||||
}
|
||||
}
|
||||
|
||||
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{
|
||||
var (
|
||||
testGetters = 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")
|
||||
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
err = k8sManager.Start(ctx)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}()
|
||||
|
||||
k8sClient = k8sManager.GetClient()
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
|
||||
Eventually(done, timeout).Should(BeClosed())
|
||||
}, timeout.Seconds())
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
cancel()
|
||||
By("tearing down the test environment")
|
||||
if storage != nil {
|
||||
err := os.RemoveAll(storage.BasePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
},
|
||||
}
|
||||
err := testEnv.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
)
|
||||
|
||||
var (
|
||||
tlsPublicKey []byte
|
||||
tlsPrivateKey []byte
|
||||
tlsCA []byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func loadExampleKeys() (err error) {
|
||||
examplePublicKey, err = os.ReadFile("testdata/certs/server.pem")
|
||||
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
|
||||
testServer, err = testserver.NewTempArtifactServer()
|
||||
if err != nil {
|
||||
return err
|
||||
panic(fmt.Sprintf("Failed to create a temporary storage server: %v", err))
|
||||
}
|
||||
examplePrivateKey, err = os.ReadFile("testdata/certs/server-key.pem")
|
||||
fmt.Println("Starting the test storage server")
|
||||
testServer.Start()
|
||||
|
||||
testStorage, err = newTestStorage(testServer.HTTPServer)
|
||||
if err != nil {
|
||||
return err
|
||||
panic(fmt.Sprintf("Failed to create a test storage: %v", err))
|
||||
}
|
||||
exampleCA, err = os.ReadFile("testdata/certs/ca.pem")
|
||||
return err
|
||||
|
||||
testMetricsH = controller.MustMakeMetrics(testEnv)
|
||||
|
||||
if err := (&GitRepositoryReconciler{
|
||||
Client: testEnv,
|
||||
EventRecorder: record.NewFakeRecorder(32),
|
||||
Metrics: testMetricsH,
|
||||
Storage: testStorage,
|
||||
}).SetupWithManager(testEnv); err != nil {
|
||||
panic(fmt.Sprintf("Failed to start GitRepositoryReconciler: %v", err))
|
||||
}
|
||||
|
||||
if err := (&BucketReconciler{
|
||||
Client: testEnv,
|
||||
EventRecorder: record.NewFakeRecorder(32),
|
||||
Metrics: testMetricsH,
|
||||
Storage: testStorage,
|
||||
}).SetupWithManager(testEnv); err != nil {
|
||||
panic(fmt.Sprintf("Failed to start BucketReconciler: %v", err))
|
||||
}
|
||||
|
||||
if err := (&HelmRepositoryReconciler{
|
||||
Client: testEnv,
|
||||
EventRecorder: record.NewFakeRecorder(32),
|
||||
Metrics: testMetricsH,
|
||||
Getters: testGetters,
|
||||
Storage: testStorage,
|
||||
}).SetupWithManager(testEnv); err != nil {
|
||||
panic(fmt.Sprintf("Failed to start HelmRepositoryReconciler: %v", err))
|
||||
}
|
||||
|
||||
if err := (&HelmChartReconciler{
|
||||
Client: testEnv,
|
||||
EventRecorder: record.NewFakeRecorder(32),
|
||||
Metrics: testMetricsH,
|
||||
Getters: testGetters,
|
||||
Storage: testStorage,
|
||||
}).SetupWithManager(testEnv); err != nil {
|
||||
panic(fmt.Sprintf("Failed to start HelmRepositoryReconciler: %v", err))
|
||||
}
|
||||
|
||||
go func() {
|
||||
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()
|
||||
|
||||
code := m.Run()
|
||||
|
||||
fmt.Println("Stopping the test environment")
|
||||
if err := testEnv.Stop(); err != nil {
|
||||
panic(fmt.Sprintf("Failed to stop the test environment: %v", err))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
tlsCA, err = os.ReadFile("testdata/certs/ca.pem")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func newTestStorage(s *testserver.HTTPServer) (*Storage, error) {
|
||||
storage, err := NewStorage(s.Root(), s.URL(), timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return storage, nil
|
||||
}
|
||||
|
||||
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890")
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
**.txt
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: dummy
|
|
@ -2,22 +2,22 @@
|
|||
<p>Packages:</p>
|
||||
<ul class="simple">
|
||||
<li>
|
||||
<a href="#source.toolkit.fluxcd.io%2fv1beta1">source.toolkit.fluxcd.io/v1beta1</a>
|
||||
<a href="#source.toolkit.fluxcd.io%2fv1beta2">source.toolkit.fluxcd.io/v1beta2</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="source.toolkit.fluxcd.io/v1beta1">source.toolkit.fluxcd.io/v1beta1</h2>
|
||||
<p>Package v1beta1 contains API Schema definitions for the source v1beta1 API group</p>
|
||||
<h2 id="source.toolkit.fluxcd.io/v1beta2">source.toolkit.fluxcd.io/v1beta2</h2>
|
||||
<p>Package v1beta2 contains API Schema definitions for the source v1beta2 API group</p>
|
||||
Resource Types:
|
||||
<ul class="simple"><li>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.Bucket">Bucket</a>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.Bucket">Bucket</a>
|
||||
</li><li>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepository">GitRepository</a>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepository">GitRepository</a>
|
||||
</li><li>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmChart">HelmChart</a>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmChart">HelmChart</a>
|
||||
</li><li>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmRepository">HelmRepository</a>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>
|
||||
</li></ul>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.Bucket">Bucket
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.Bucket">Bucket
|
||||
</h3>
|
||||
<p>Bucket is the Schema for the buckets API</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -35,7 +35,7 @@ Resource Types:
|
|||
<code>apiVersion</code><br>
|
||||
string</td>
|
||||
<td>
|
||||
<code>source.toolkit.fluxcd.io/v1beta1</code>
|
||||
<code>source.toolkit.fluxcd.io/v1beta2</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -65,7 +65,7 @@ Refer to the Kubernetes API documentation for the fields of the
|
|||
<td>
|
||||
<code>spec</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.BucketSpec">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.BucketSpec">
|
||||
BucketSpec
|
||||
</a>
|
||||
</em>
|
||||
|
@ -171,7 +171,7 @@ Kubernetes meta/v1.Duration
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>The timeout for download operations, defaults to 60s.</p>
|
||||
<p>The timeout for fetch operations, defaults to 60s.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -221,7 +221,7 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
|||
<td>
|
||||
<code>status</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.BucketStatus">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.BucketStatus">
|
||||
BucketStatus
|
||||
</a>
|
||||
</em>
|
||||
|
@ -233,7 +233,7 @@ BucketStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.GitRepository">GitRepository
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.GitRepository">GitRepository
|
||||
</h3>
|
||||
<p>GitRepository is the Schema for the gitrepositories API</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -251,7 +251,7 @@ BucketStatus
|
|||
<code>apiVersion</code><br>
|
||||
string</td>
|
||||
<td>
|
||||
<code>source.toolkit.fluxcd.io/v1beta1</code>
|
||||
<code>source.toolkit.fluxcd.io/v1beta2</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -281,7 +281,7 @@ Refer to the Kubernetes API documentation for the fields of the
|
|||
<td>
|
||||
<code>spec</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositorySpec">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositorySpec">
|
||||
GitRepositorySpec
|
||||
</a>
|
||||
</em>
|
||||
|
@ -313,10 +313,8 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
|||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>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.</p>
|
||||
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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -350,7 +348,7 @@ Kubernetes meta/v1.Duration
|
|||
<td>
|
||||
<code>ref</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositoryRef">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryRef">
|
||||
GitRepositoryRef
|
||||
</a>
|
||||
</em>
|
||||
|
@ -365,14 +363,14 @@ master branch.</p>
|
|||
<td>
|
||||
<code>verify</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositoryVerification">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryVerification">
|
||||
GitRepositoryVerification
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Verify OpenPGP signature for the Git commit HEAD points to.</p>
|
||||
<p>Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -384,9 +382,8 @@ string
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -398,7 +395,8 @@ bool
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>This flag tells the controller to suspend the reconciliation of this source.</p>
|
||||
<p>Suspend tells the controller to suspend the reconciliation of this source.
|
||||
This flag tells the controller to suspend the reconciliation of this source.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -423,8 +421,7 @@ bool
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>When enabled, after the clone is created, initializes all submodules within,
|
||||
using their default settings.
|
||||
<p>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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -432,13 +429,14 @@ This option is available only when using the ‘go-git’ GitImplementat
|
|||
<td>
|
||||
<code>include</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositoryInclude">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryInclude">
|
||||
[]GitRepositoryInclude
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Extra git repositories to map into the repository</p>
|
||||
<p>Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for
|
||||
this resource.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -462,7 +460,7 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
|||
<td>
|
||||
<code>status</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositoryStatus">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryStatus">
|
||||
GitRepositoryStatus
|
||||
</a>
|
||||
</em>
|
||||
|
@ -474,7 +472,7 @@ GitRepositoryStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.HelmChart">HelmChart
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmChart">HelmChart
|
||||
</h3>
|
||||
<p>HelmChart is the Schema for the helmcharts API</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -492,7 +490,7 @@ GitRepositoryStatus
|
|||
<code>apiVersion</code><br>
|
||||
string</td>
|
||||
<td>
|
||||
<code>source.toolkit.fluxcd.io/v1beta1</code>
|
||||
<code>source.toolkit.fluxcd.io/v1beta2</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -522,7 +520,7 @@ Refer to the Kubernetes API documentation for the fields of the
|
|||
<td>
|
||||
<code>spec</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmChartSpec">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmChartSpec">
|
||||
HelmChartSpec
|
||||
</a>
|
||||
</em>
|
||||
|
@ -559,7 +557,7 @@ and Bucket sources. Defaults to latest when omitted.</p>
|
|||
<td>
|
||||
<code>sourceRef</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.LocalHelmChartSourceReference">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.LocalHelmChartSourceReference">
|
||||
LocalHelmChartSourceReference
|
||||
</a>
|
||||
</em>
|
||||
|
@ -659,7 +657,7 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
|||
<td>
|
||||
<code>status</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmChartStatus">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmChartStatus">
|
||||
HelmChartStatus
|
||||
</a>
|
||||
</em>
|
||||
|
@ -671,7 +669,7 @@ HelmChartStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.HelmRepository">HelmRepository
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository
|
||||
</h3>
|
||||
<p>HelmRepository is the Schema for the helmrepositories API</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -689,7 +687,7 @@ HelmChartStatus
|
|||
<code>apiVersion</code><br>
|
||||
string</td>
|
||||
<td>
|
||||
<code>source.toolkit.fluxcd.io/v1beta1</code>
|
||||
<code>source.toolkit.fluxcd.io/v1beta2</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -719,7 +717,7 @@ Refer to the Kubernetes API documentation for the fields of the
|
|||
<td>
|
||||
<code>spec</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmRepositorySpec">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepositorySpec">
|
||||
HelmRepositorySpec
|
||||
</a>
|
||||
</em>
|
||||
|
@ -799,7 +797,7 @@ Kubernetes meta/v1.Duration
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>The timeout of index downloading, defaults to 60s.</p>
|
||||
<p>The timeout of index fetching, defaults to 60s.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -835,7 +833,7 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
|||
<td>
|
||||
<code>status</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmRepositoryStatus">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepositoryStatus">
|
||||
HelmRepositoryStatus
|
||||
</a>
|
||||
</em>
|
||||
|
@ -847,16 +845,16 @@ HelmRepositoryStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.Artifact">Artifact
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.Artifact">Artifact
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.BucketStatus">BucketStatus</a>,
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositoryStatus">GitRepositoryStatus</a>,
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmChartStatus">HelmChartStatus</a>,
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmRepositoryStatus">HelmRepositoryStatus</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.BucketStatus">BucketStatus</a>,
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryStatus">GitRepositoryStatus</a>,
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmChartStatus">HelmChartStatus</a>,
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepositoryStatus">HelmRepositoryStatus</a>)
|
||||
</p>
|
||||
<p>Artifact represents the output of a source synchronisation.</p>
|
||||
<p>Artifact represents the output of a Source synchronisation.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
<div class="md-typeset__table">
|
||||
<table>
|
||||
|
@ -875,7 +873,9 @@ string
|
|||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Path is the relative file path of this artifact.</p>
|
||||
<p>Path is the relative file path of this Artifact.
|
||||
It can be used to locate the Artifact file in the root of the Artifact
|
||||
storage on the local file system of the controller managing the Source.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -886,7 +886,9 @@ string
|
|||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>URL is the HTTP address of this artifact.</p>
|
||||
<p>URL is the HTTP address of this artifact.
|
||||
It is used by the consumers of the artifacts to fetch and use the
|
||||
artifacts. It is expected to be resolvable from within the cluster.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -933,11 +935,11 @@ artifact.</p>
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.BucketSpec">BucketSpec
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.BucketSpec">BucketSpec
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.Bucket">Bucket</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.Bucket">Bucket</a>)
|
||||
</p>
|
||||
<p>BucketSpec defines the desired state of an S3 compatible bucket</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1047,7 +1049,7 @@ Kubernetes meta/v1.Duration
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>The timeout for download operations, defaults to 60s.</p>
|
||||
<p>The timeout for fetch operations, defaults to 60s.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1094,11 +1096,11 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.BucketStatus">BucketStatus
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.BucketStatus">BucketStatus
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.Bucket">Bucket</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.Bucket">Bucket</a>)
|
||||
</p>
|
||||
<p>BucketStatus defines the observed state of a bucket</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1146,14 +1148,14 @@ string
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>URL is the download link for the artifact output of the last Bucket sync.</p>
|
||||
<p>URL is the fetch link for the artifact output of the last Bucket sync.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>artifact</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.Artifact">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.Artifact">
|
||||
Artifact
|
||||
</a>
|
||||
</em>
|
||||
|
@ -1182,11 +1184,11 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.GitRepositoryInclude">GitRepositoryInclude
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.GitRepositoryInclude">GitRepositoryInclude
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositorySpec">GitRepositorySpec</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositorySpec">GitRepositorySpec</a>)
|
||||
</p>
|
||||
<p>GitRepositoryInclude defines a source with a from and to path.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1240,11 +1242,11 @@ string
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.GitRepositoryRef">GitRepositoryRef
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.GitRepositoryRef">GitRepositoryRef
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositorySpec">GitRepositorySpec</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositorySpec">GitRepositorySpec</a>)
|
||||
</p>
|
||||
<p>GitRepositoryRef defines the Git ref used for pull and checkout operations.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1309,11 +1311,11 @@ string
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.GitRepositorySpec">GitRepositorySpec
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.GitRepositorySpec">GitRepositorySpec
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepository">GitRepository</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepository">GitRepository</a>)
|
||||
</p>
|
||||
<p>GitRepositorySpec defines the desired state of a Git repository.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1349,10 +1351,8 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
|||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>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.</p>
|
||||
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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1386,7 +1386,7 @@ Kubernetes meta/v1.Duration
|
|||
<td>
|
||||
<code>ref</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositoryRef">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryRef">
|
||||
GitRepositoryRef
|
||||
</a>
|
||||
</em>
|
||||
|
@ -1401,14 +1401,14 @@ master branch.</p>
|
|||
<td>
|
||||
<code>verify</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositoryVerification">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryVerification">
|
||||
GitRepositoryVerification
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Verify OpenPGP signature for the Git commit HEAD points to.</p>
|
||||
<p>Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1420,9 +1420,8 @@ string
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1434,7 +1433,8 @@ bool
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>This flag tells the controller to suspend the reconciliation of this source.</p>
|
||||
<p>Suspend tells the controller to suspend the reconciliation of this source.
|
||||
This flag tells the controller to suspend the reconciliation of this source.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1459,8 +1459,7 @@ bool
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>When enabled, after the clone is created, initializes all submodules within,
|
||||
using their default settings.
|
||||
<p>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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1468,13 +1467,14 @@ This option is available only when using the ‘go-git’ GitImplementat
|
|||
<td>
|
||||
<code>include</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositoryInclude">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryInclude">
|
||||
[]GitRepositoryInclude
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Extra git repositories to map into the repository</p>
|
||||
<p>Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for
|
||||
this resource.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1495,11 +1495,11 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.GitRepositoryStatus">GitRepositoryStatus
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.GitRepositoryStatus">GitRepositoryStatus
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepository">GitRepository</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepository">GitRepository</a>)
|
||||
</p>
|
||||
<p>GitRepositoryStatus defines the observed state of a Git repository.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1547,15 +1547,14 @@ string
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>URL is the download link for the artifact output of the last repository
|
||||
sync.</p>
|
||||
<p>URL is the fetch link for the artifact output of the last repository sync.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>artifact</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.Artifact">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.Artifact">
|
||||
Artifact
|
||||
</a>
|
||||
</em>
|
||||
|
@ -1569,8 +1568,8 @@ Artifact
|
|||
<td>
|
||||
<code>includedArtifacts</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.*github.com/fluxcd/source-controller/api/v1beta1.Artifact">
|
||||
[]*github.com/fluxcd/source-controller/api/v1beta1.Artifact
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.*github.com/fluxcd/source-controller/api/v1beta2.Artifact">
|
||||
[]*github.com/fluxcd/source-controller/api/v1beta2.Artifact
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
|
@ -1598,11 +1597,11 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.GitRepositoryVerification">GitRepositoryVerification
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.GitRepositoryVerification">GitRepositoryVerification
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.GitRepositorySpec">GitRepositorySpec</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositorySpec">GitRepositorySpec</a>)
|
||||
</p>
|
||||
<p>GitRepositoryVerification defines the OpenPGP signature verification process.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1623,7 +1622,7 @@ string
|
|||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Mode describes what git object should be verified, currently (‘head’).</p>
|
||||
<p>Mode describes what Git object should be verified, currently (‘head’).</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1636,18 +1635,18 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
|||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>The secret name containing the public keys of all trusted Git authors.</p>
|
||||
<p>SecretRef containing the public keys of all trusted Git authors.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.HelmChartSpec">HelmChartSpec
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmChartSpec">HelmChartSpec
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmChart">HelmChart</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmChart">HelmChart</a>)
|
||||
</p>
|
||||
<p>HelmChartSpec defines the desired state of a Helm chart.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1688,7 +1687,7 @@ and Bucket sources. Defaults to latest when omitted.</p>
|
|||
<td>
|
||||
<code>sourceRef</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.LocalHelmChartSourceReference">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.LocalHelmChartSourceReference">
|
||||
LocalHelmChartSourceReference
|
||||
</a>
|
||||
</em>
|
||||
|
@ -1785,11 +1784,11 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.HelmChartStatus">HelmChartStatus
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmChartStatus">HelmChartStatus
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmChart">HelmChart</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmChart">HelmChart</a>)
|
||||
</p>
|
||||
<p>HelmChartStatus defines the observed state of the HelmChart.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1816,6 +1815,32 @@ int64
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>observedSourceArtifactRevision</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ObservedSourceArtifactRevision is the last observed Artifact.Revision
|
||||
of the Source reference.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>observedChartName</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ObservedChartName is the last observed chart name as defined by the
|
||||
resolved chart reference.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>conditions</code><br>
|
||||
<em>
|
||||
<a href="https://godoc.org/k8s.io/apimachinery/pkg/apis/meta/v1#Condition">
|
||||
|
@ -1837,14 +1862,14 @@ string
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>URL is the download link for the last chart pulled.</p>
|
||||
<p>URL is the fetch link for the last chart pulled.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>artifact</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.Artifact">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.Artifact">
|
||||
Artifact
|
||||
</a>
|
||||
</em>
|
||||
|
@ -1873,11 +1898,11 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.HelmRepositorySpec">HelmRepositorySpec
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmRepositorySpec">HelmRepositorySpec
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmRepository">HelmRepository</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>)
|
||||
</p>
|
||||
<p>HelmRepositorySpec defines the reference to a Helm repository.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -1961,7 +1986,7 @@ Kubernetes meta/v1.Duration
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>The timeout of index downloading, defaults to 60s.</p>
|
||||
<p>The timeout of index fetching, defaults to 60s.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1994,11 +2019,11 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.HelmRepositoryStatus">HelmRepositoryStatus
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmRepositoryStatus">HelmRepositoryStatus
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmRepository">HelmRepository</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>)
|
||||
</p>
|
||||
<p>HelmRepositoryStatus defines the observed state of the HelmRepository.</p>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
|
@ -2046,14 +2071,14 @@ string
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>URL is the download link for the last index fetched.</p>
|
||||
<p>URL is the fetch link for the last index fetched.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>artifact</code><br>
|
||||
<em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.Artifact">
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.Artifact">
|
||||
Artifact
|
||||
</a>
|
||||
</em>
|
||||
|
@ -2082,11 +2107,11 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.LocalHelmChartSourceReference">LocalHelmChartSourceReference
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.LocalHelmChartSourceReference">LocalHelmChartSourceReference
|
||||
</h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta1.HelmChartSpec">HelmChartSpec</a>)
|
||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmChartSpec">HelmChartSpec</a>)
|
||||
</p>
|
||||
<p>LocalHelmChartSourceReference contains enough information to let you locate
|
||||
the typed referenced object at namespace level.</p>
|
||||
|
@ -2139,7 +2164,7 @@ string
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta1.Source">Source
|
||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.Source">Source
|
||||
</h3>
|
||||
<p>Source interface must be supported by all API types.</p>
|
||||
<div class="admonition note">
|
||||
|
|
49
go.mod
49
go.mod
|
@ -9,14 +9,17 @@ require (
|
|||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7
|
||||
github.com/cyphar/filepath-securejoin v0.2.2
|
||||
github.com/darkowlzz/controller-check v0.0.0-20220119215126-648356cef22c
|
||||
github.com/docker/go-units v0.4.0
|
||||
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.12.0
|
||||
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.1
|
||||
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.21.2
|
||||
|
@ -25,24 +28,25 @@ require (
|
|||
github.com/go-logr/logr v1.2.2
|
||||
github.com/libgit2/git2go/v33 v33.0.6
|
||||
github.com/minio/minio-go/v7 v7.0.15
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.17.0
|
||||
github.com/otiai10/copy v1.7.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
google.golang.org/api v0.54.0
|
||||
google.golang.org/api v0.62.0
|
||||
gotest.tools v2.2.0+incompatible
|
||||
helm.sh/helm/v3 v3.7.2
|
||||
k8s.io/api v0.23.3
|
||||
k8s.io/apimachinery v0.23.3
|
||||
k8s.io/client-go v0.23.3
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
|
||||
sigs.k8s.io/cli-utils v0.28.0
|
||||
sigs.k8s.io/controller-runtime v0.11.1
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.90.0 // indirect
|
||||
cloud.google.com/go v0.99.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
|
||||
|
@ -60,6 +64,7 @@ require (
|
|||
github.com/bugsnag/bugsnag-go v2.1.2+incompatible // indirect
|
||||
github.com/bugsnag/panicwrap v1.3.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/containerd/containerd v1.5.7 // indirect
|
||||
github.com/containerd/continuity v0.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
|
@ -69,15 +74,13 @@ require (
|
|||
github.com/docker/docker-credential-helpers v0.6.3 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/fatih/color v1.13.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
|
||||
|
@ -95,12 +98,12 @@ require (
|
|||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.1.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gosuri/uitable v0.0.4 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
|
@ -109,18 +112,18 @@ require (
|
|||
github.com/jmoiron/sqlx v1.3.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/jstemmer/go-junit-report v0.9.1 // indirect
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.13.5 // indirect
|
||||
github.com/klauspost/cpuid v1.3.1 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/lib/pq v1.10.0 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/minio/md5-simd v1.1.0 // indirect
|
||||
|
@ -136,7 +139,6 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opencontainers/runc v1.0.2 // indirect
|
||||
|
@ -155,8 +157,8 @@ require (
|
|||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/sosedoff/gitkit v0.2.1-0.20200818155723-72ebbcf5056d // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.2.1 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||
|
@ -171,36 +173,31 @@ require (
|
|||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
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-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 // indirect
|
||||
google.golang.org/grpc v1.40.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||
google.golang.org/grpc v1.42.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/gorp.v1 v1.7.2 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
k8s.io/apiextensions-apiserver v0.23.3 // indirect
|
||||
k8s.io/apiserver v0.23.3 // indirect
|
||||
k8s.io/cli-runtime v0.23.0 // indirect
|
||||
k8s.io/cli-runtime v0.23.2 // indirect
|
||||
k8s.io/component-base v0.23.3 // indirect
|
||||
k8s.io/klog/v2 v2.40.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf // indirect
|
||||
k8s.io/kubectl v0.22.4 // indirect
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
|
||||
k8s.io/kubectl v0.23.2 // indirect
|
||||
oras.land/oras-go v0.4.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.10.1 // indirect
|
||||
|
|
187
go.sum
187
go.sum
|
@ -22,8 +22,13 @@ cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E
|
|||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0 h1:MjvSkUq8RuAb+2JLDi5VQmmExRJPUQ3JLCWpRB6fmdw=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
|
||||
cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY=
|
||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
|
@ -33,6 +38,7 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7
|
|||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
|
@ -68,6 +74,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
|
@ -122,7 +129,9 @@ github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.m
|
|||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
|
@ -156,6 +165,7 @@ github.com/bugsnag/panicwrap v1.3.4 h1:A6sXFtDGsgU/4BLf5JT0o5uYg3EeKgGx3Sfs+/uk3
|
|||
github.com/bugsnag/panicwrap v1.3.4/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
|
@ -163,6 +173,7 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
|
|||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
|
@ -171,11 +182,19 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
|||
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
||||
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
|
@ -222,6 +241,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
|
|||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
|
@ -232,6 +252,8 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S
|
|||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
|
||||
github.com/darkowlzz/controller-check v0.0.0-20220119215126-648356cef22c h1:pyp/Dvd1gYP/D3z1zs46h0YhYzFp0hjxw0XVIO9+vh4=
|
||||
github.com/darkowlzz/controller-check v0.0.0-20220119215126-648356cef22c/go.mod h1:haYO9UW76kUUKpIBbv3ydaU5wZ/7r0yqp61PGzVRSYU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -285,7 +307,10 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
|
|||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
|
||||
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
|
@ -295,14 +320,17 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
|
|||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
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/go.mod h1:ki5wJE4nuFOZt78q0RSYkrKwINgIBPynuswZhnTOSoI=
|
||||
github.com/fluxcd/pkg/apis/meta v0.12.0 h1:Ssyltj6E9A7y32sZrzjog0m+bIsFM/3lHHfmpxesUAU=
|
||||
github.com/fluxcd/pkg/apis/meta v0.12.0/go.mod h1:SPrSWMwDK7Ls2/4GadzhjDjPFbKrzzgzuZ0oDO3jzso=
|
||||
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=
|
||||
|
@ -311,12 +339,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.1 h1:/MVSjP/pySd7tNP5FaYMkWerfxf8NZmO7SlDkTUjkjU=
|
||||
github.com/fluxcd/pkg/runtime v0.13.1/go.mod h1:G0EuJZJi/ZOjrWiclF4bBmkbzKhWssKuzSsmz3kVCMg=
|
||||
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=
|
||||
|
@ -500,13 +530,13 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
|||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
|
||||
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
|
@ -527,18 +557,28 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
|
|||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo=
|
||||
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
|
||||
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-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
|
@ -546,15 +586,23 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
|||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
|
@ -578,14 +626,13 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
|
|||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
|
@ -620,6 +667,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||
|
@ -632,6 +681,7 @@ github.com/libgit2/git2go/v33 v33.0.6/go.mod h1:KdpqkU+6+++4oHna/MIOgx4GCQ92IPCd
|
|||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
|
@ -649,11 +699,20 @@ github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kN
|
|||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
|
@ -667,6 +726,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
|||
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/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
|
||||
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
||||
|
@ -676,6 +737,7 @@ github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKU
|
|||
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.1.1 h1:Bp6x9R1Wn16SIz3OfeDr0b7RnCG2OB66Y7PQyC/cvq4=
|
||||
|
@ -691,6 +753,7 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
|||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
|
||||
|
@ -766,9 +829,11 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
|
|||
github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI=
|
||||
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||
|
@ -781,11 +846,13 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
|
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
|
@ -799,6 +866,7 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q
|
|||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
|
@ -809,6 +877,7 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
|
|||
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
|
@ -827,8 +896,10 @@ github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc/go.mod h1:HFLT6
|
|||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
|
@ -846,9 +917,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
|
|||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
|
@ -857,17 +926,20 @@ github.com/sosedoff/gitkit v0.2.1-0.20200818155723-72ebbcf5056d/go.mod h1:A+o6Za
|
|||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
|
||||
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
|
@ -879,6 +951,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/spyzhov/ajson v0.4.2/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@ -898,6 +972,7 @@ github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ
|
|||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
|
@ -941,8 +1016,11 @@ go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
|||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
|
||||
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
|
||||
|
@ -995,6 +1073,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
@ -1033,7 +1112,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
|
|||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
|
@ -1045,8 +1123,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -1068,6 +1146,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -1095,11 +1174,13 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
@ -1122,6 +1203,7 @@ golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/go.mod h1:KelEdhl1UZF7XfJ
|
|||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -1144,6 +1226,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1158,9 +1241,12 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1168,6 +1254,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1204,6 +1291,7 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1222,8 +1310,15 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
|
||||
|
@ -1271,6 +1366,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
|||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
@ -1316,7 +1412,6 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff h1:VX/uD7MK0AHXGiScH3fsieUQUcpmRERPDYtqZdJnA+Q=
|
||||
golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -1353,8 +1448,14 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk
|
|||
google.golang.org/api v0.49.0/go.mod h1:BECiH72wsfwUvOVn3+btPD5WHi0LzavZReBndi42L18=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0 h1:ECJUVngj71QI6XEm7b1sAf8BljU5inEhMbKPR8Lxhhk=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
|
||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||
google.golang.org/api v0.62.0 h1:PhGymJMXfGBzc4lBRmrx9+1w4w2wEzURHNGF/sD/xGc=
|
||||
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -1420,9 +1521,21 @@ google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm
|
|||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 h1:NHN4wOCScVzKhPenJ2dt+BTs3X/XkBVI/Rh4iDt55T8=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
|
@ -1449,8 +1562,10 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
|
|||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
|
@ -1485,8 +1600,9 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
|||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
|
@ -1526,6 +1642,7 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||
k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
|
||||
k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk=
|
||||
k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg=
|
||||
k8s.io/api v0.23.2/go.mod h1:sYuDb3flCtRPI8ghn6qFrcK5ZBu2mhbElxRE95qpwlI=
|
||||
k8s.io/api v0.23.3 h1:KNrME8KHGr12Ozjf8ytOewKzZh6hl/hHUZeHddT3a38=
|
||||
k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ=
|
||||
k8s.io/apiextensions-apiserver v0.22.4/go.mod h1:kH9lxD8dbJ+k0ZizGET55lFgdGjO8t45fgZnCVdZEpw=
|
||||
|
@ -1535,7 +1652,7 @@ k8s.io/apiextensions-apiserver v0.23.3/go.mod h1:/ZpRXdgKZA6DvIVPEmXDCZJN53YIQEU
|
|||
k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
|
||||
k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0=
|
||||
k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc=
|
||||
k8s.io/apimachinery v0.23.1/go.mod h1:SADt2Kl8/sttJ62RRsi9MIV4o8f5S3coArm0Iu3fBno=
|
||||
k8s.io/apimachinery v0.23.2/go.mod h1:zDqeV0AK62LbCI0CI7KbWCAYdLg+E+8UXJ0rIz5gmS8=
|
||||
k8s.io/apimachinery v0.23.3 h1:7IW6jxNzrXTsP0c8yXz2E5Yx/WTzVPTsHIx/2Vm0cIk=
|
||||
k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
|
||||
k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
|
||||
|
@ -1544,22 +1661,26 @@ k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4=
|
|||
k8s.io/apiserver v0.23.3 h1:gWY1DmA0AdAGR/H+Q/1FtyGkFq8xqSaZOw7oLopmO8k=
|
||||
k8s.io/apiserver v0.23.3/go.mod h1:3HhsTmC+Pn+Jctw+Ow0LHA4dQ4oXrQ4XJDzrVDG64T4=
|
||||
k8s.io/cli-runtime v0.22.4/go.mod h1:x35r0ERHXr/MrbR1C6MPJxQ3xKG6+hXi9m2xLzlMPZA=
|
||||
k8s.io/cli-runtime v0.23.0 h1:UONt0BV2+edjUVAXuR1nnOAL2CB9r+Gl9yk4UBQpKfs=
|
||||
k8s.io/cli-runtime v0.23.0/go.mod h1:B5N3YH0KP1iKr6gEuJ/RRmGjO0mJQ/f/JrsmEiPQAlU=
|
||||
k8s.io/cli-runtime v0.23.2 h1:4zOZX78mFSakwe4gef81XDBu94Yu0th6bfveTOx8ZQk=
|
||||
k8s.io/cli-runtime v0.23.2/go.mod h1:Ag70akCDvwux4HxY+nH2J3UqE2e6iwSSdG1HE6p1VTU=
|
||||
k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
|
||||
k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA=
|
||||
k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA=
|
||||
k8s.io/client-go v0.23.2/go.mod h1:k3YbsWg6GWdHF1THHTQP88X9RhB1DWPo3Dq7KfU/D1c=
|
||||
k8s.io/client-go v0.23.3 h1:23QYUmCQ/W6hW78xIwm3XqZrrKZM+LWDqW2zfo+szJs=
|
||||
k8s.io/client-go v0.23.3/go.mod h1:47oMd+YvAOqZM7pcQ6neJtBiFH7alOyfunYN48VsmwE=
|
||||
k8s.io/code-generator v0.22.4/go.mod h1:qjYl54pQ/emhkT0UxbufbREYJMWsHNNV/jSVwhYZQGw=
|
||||
k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE=
|
||||
k8s.io/code-generator v0.23.2/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
|
||||
k8s.io/code-generator v0.23.3/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
|
||||
k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
|
||||
k8s.io/component-base v0.22.4/go.mod h1:MrSaQy4a3tFVViff8TZL6JHYSewNCLshZCwHYM58v5A=
|
||||
k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI=
|
||||
k8s.io/component-base v0.23.2/go.mod h1:wS9Z03MO3oJ0RU8bB/dbXTiluGju+SC/F5i660gxB8c=
|
||||
k8s.io/component-base v0.23.3 h1:q+epprVdylgecijVGVdf4MbizEL2feW4ssd7cdo6LVY=
|
||||
k8s.io/component-base v0.23.3/go.mod h1:1Smc4C60rWG7d3HjSYpIwEbySQ3YWg0uzH5a2AtaTLg=
|
||||
k8s.io/component-helpers v0.22.4/go.mod h1:A50qTyczDFbhZDifIfS2zFrHuPk9UNOWPpvNZ+3RSIs=
|
||||
k8s.io/component-helpers v0.23.2/go.mod h1:J6CMwiaf0izLoNwiLl2OymB4+rGTsTpWp6PL/AqOM4U=
|
||||
k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
|
@ -1577,9 +1698,11 @@ k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2R
|
|||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
|
||||
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf h1:M9XBsiMslw2lb2ZzglC0TOkBPK5NQi0/noUrdnoFwUg=
|
||||
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
|
||||
k8s.io/kubectl v0.22.4 h1:ECUO1QWyZ70DiIKEfgBx+8i9D98uspVOwgc1APs/07w=
|
||||
k8s.io/kubectl v0.22.4/go.mod h1:ok2qRT6y2Gy4+y+mniJVyUMKeBHP4OWS9Rdtf/QTM5I=
|
||||
k8s.io/kubectl v0.23.2 h1:YakGzFN1csIOW/Us5VsLxjcu5Q6Vh5rqcvukcNuBwFk=
|
||||
k8s.io/kubectl v0.23.2/go.mod h1:zWm5wt8PdRmHiVhE9a7q7XYW4WFX9StkZGnC18+1v3M=
|
||||
k8s.io/metrics v0.22.4/go.mod h1:6F/iwuYb1w2QDCoHkeMFLf4pwHBcYKLm4mPtVHKYrIw=
|
||||
k8s.io/metrics v0.23.2/go.mod h1:idJHc+lLK5teHUC6Z2+d6qTKA12d5FLDxmC/DHiUYKc=
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
|
@ -1599,6 +1722,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyz
|
|||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.27/go.mod h1:tq2nT0Kx7W+/f2JVE+zxYtUhdjuELJkVpNz+x/QN5R4=
|
||||
sigs.k8s.io/cli-utils v0.28.0 h1:gsvwqygoXlW2y8CmKdflQJNZp1Yhi4geATW3/Ei7oYc=
|
||||
sigs.k8s.io/cli-utils v0.28.0/go.mod h1:WDVRa5/eQBKntG++uyKdyT+xU7MLdCR4XsgseqL5uX4=
|
||||
sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=
|
||||
sigs.k8s.io/controller-runtime v0.11.1 h1:7YIHT2QnHJArj/dk9aUkYhfqfK5cIxPOX5gPECfdZLU=
|
||||
sigs.k8s.io/controller-runtime v0.11.1/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=
|
||||
|
@ -1609,7 +1734,9 @@ sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH8
|
|||
sigs.k8s.io/kustomize/api v0.10.1 h1:KgU7hfYoscuqag84kxtzKdEC3mKMb99DPI3a0eaV1d0=
|
||||
sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ=
|
||||
sigs.k8s.io/kustomize/kustomize/v4 v4.2.0/go.mod h1:MOkR6fmhwG7hEDRXBYELTi5GSFcLwfqwzTRHW3kv5go=
|
||||
sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
Copyright 2022 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.
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
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
|
||||
|
||||
import "time"
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Waiting is the reconciliation wait state error. It contains an error, wait
|
||||
// duration and a reason for the wait.
|
||||
type Waiting struct {
|
||||
// RequeueAfter is the wait duration after which to requeue.
|
||||
RequeueAfter time.Duration
|
||||
// Reason is the reason for the wait.
|
||||
Reason string
|
||||
// Err is the error that caused the wait.
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error implement error interface.
|
||||
func (we *Waiting) Error() string {
|
||||
return we.Err.Error()
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error.
|
||||
func (we *Waiting) Unwrap() error {
|
||||
return we.Err
|
||||
}
|
|
@ -115,15 +115,16 @@ func (o BuildOptions) GetValuesFiles() []string {
|
|||
return o.ValuesFiles
|
||||
}
|
||||
|
||||
// Build contains the Builder.Build result, including specific
|
||||
// Build contains the (partial) Builder.Build result, including specific
|
||||
// information about the built chart like ResolvedDependencies.
|
||||
type Build struct {
|
||||
// Path is the absolute path to the packaged chart.
|
||||
Path string
|
||||
// Name of the packaged chart.
|
||||
// Name of the chart.
|
||||
Name string
|
||||
// Version of the packaged chart.
|
||||
// Version of the chart.
|
||||
Version string
|
||||
// Path is the absolute path to the packaged chart.
|
||||
// Can be empty, in which case a failure should be assumed.
|
||||
Path string
|
||||
// ValuesFiles is the list of files used to compose the chart's
|
||||
// default "values.yaml".
|
||||
ValuesFiles []string
|
||||
|
@ -138,30 +139,45 @@ type Build struct {
|
|||
|
||||
// Summary returns a human-readable summary of the Build.
|
||||
func (b *Build) Summary() string {
|
||||
if b == nil || b.Name == "" || b.Version == "" {
|
||||
return "No chart build."
|
||||
if !b.HasMetadata() {
|
||||
return "no chart build"
|
||||
}
|
||||
|
||||
var s strings.Builder
|
||||
|
||||
var action = "Pulled"
|
||||
var action = "new"
|
||||
if b.Path != "" {
|
||||
action = "pulled"
|
||||
if b.Packaged {
|
||||
action = "Packaged"
|
||||
action = "packaged"
|
||||
}
|
||||
}
|
||||
s.WriteString(fmt.Sprintf("%s '%s' chart with version '%s'", action, b.Name, b.Version))
|
||||
|
||||
if b.Packaged && len(b.ValuesFiles) > 0 {
|
||||
s.WriteString(fmt.Sprintf(", with merged values files %v", b.ValuesFiles))
|
||||
if len(b.ValuesFiles) > 0 {
|
||||
s.WriteString(fmt.Sprintf(" and merged values files %v", b.ValuesFiles))
|
||||
}
|
||||
|
||||
if b.Packaged && b.ResolvedDependencies > 0 {
|
||||
s.WriteString(fmt.Sprintf(", resolving %d dependencies before packaging", b.ResolvedDependencies))
|
||||
}
|
||||
|
||||
s.WriteString(".")
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// HasMetadata returns if the Build contains chart metadata.
|
||||
//
|
||||
// NOTE: This may return True while the build did not Complete successfully.
|
||||
// Which means it was able to successfully collect the metadata from the chart,
|
||||
// but failed further into the process.
|
||||
func (b *Build) HasMetadata() bool {
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
return b.Name != "" && b.Version != ""
|
||||
}
|
||||
|
||||
// Complete returns if the Build completed successfully.
|
||||
func (b *Build) Complete() bool {
|
||||
return b.HasMetadata() && b.Path != ""
|
||||
}
|
||||
|
||||
// String returns the Path of the Build.
|
||||
func (b *Build) String() string {
|
||||
if b == nil {
|
||||
|
|
|
@ -77,10 +77,10 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
|||
// to a chart
|
||||
curMeta, err := LoadChartMetadata(localRef.Path)
|
||||
if err != nil {
|
||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
||||
return nil, &BuildError{Reason: ErrChartReference, Err: err}
|
||||
}
|
||||
if err = curMeta.Validate(); err != nil {
|
||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
||||
return nil, &BuildError{Reason: ErrChartReference, Err: err}
|
||||
}
|
||||
|
||||
result := &Build{}
|
||||
|
@ -101,6 +101,9 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
|||
result.Version = ver.String()
|
||||
}
|
||||
|
||||
isChartDir := pathIsDir(localRef.Path)
|
||||
requiresPackaging := isChartDir || opts.VersionMetadata != "" || len(opts.GetValuesFiles()) != 0
|
||||
|
||||
// If all the following is true, we do not need to package the chart:
|
||||
// - Chart name from cached chart matches resolved name
|
||||
// - Chart version from cached chart matches calculated version
|
||||
|
@ -112,7 +115,9 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
|||
if err = curMeta.Validate(); err == nil {
|
||||
if result.Name == curMeta.Name && result.Version == curMeta.Version {
|
||||
result.Path = opts.CachedChart
|
||||
result.ValuesFiles = opts.ValuesFiles
|
||||
result.ValuesFiles = opts.GetValuesFiles()
|
||||
result.Packaged = requiresPackaging
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
@ -121,10 +126,9 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
|||
|
||||
// If the chart at the path is already packaged and no custom values files
|
||||
// options are set, we can copy the chart without making modifications
|
||||
isChartDir := pathIsDir(localRef.Path)
|
||||
if !isChartDir && len(opts.GetValuesFiles()) == 0 {
|
||||
if !requiresPackaging {
|
||||
if err = copyFileToPath(localRef.Path, p); err != nil {
|
||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
||||
return result, &BuildError{Reason: ErrChartPull, Err: err}
|
||||
}
|
||||
result.Path = p
|
||||
return result, nil
|
||||
|
@ -134,7 +138,7 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
|||
var mergedValues map[string]interface{}
|
||||
if len(opts.GetValuesFiles()) > 0 {
|
||||
if mergedValues, err = mergeFileValues(localRef.WorkDir, opts.ValuesFiles); err != nil {
|
||||
return nil, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||
return result, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +147,7 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
|||
// or because we have merged values and need to repackage
|
||||
chart, err := loader.Load(localRef.Path)
|
||||
if err != nil {
|
||||
return nil, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||
return result, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||
}
|
||||
// Set earlier resolved version (with metadata)
|
||||
chart.Metadata.Version = result.Version
|
||||
|
@ -151,7 +155,7 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
|||
// Overwrite default values with merged values, if any
|
||||
if ok, err = OverwriteChartDefaultValues(chart, mergedValues); ok || err != nil {
|
||||
if err != nil {
|
||||
return nil, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||
return result, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||
}
|
||||
result.ValuesFiles = opts.GetValuesFiles()
|
||||
}
|
||||
|
@ -160,19 +164,19 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
|||
if isChartDir {
|
||||
if b.dm == nil {
|
||||
err = fmt.Errorf("local chart builder requires dependency manager for unpackaged charts")
|
||||
return nil, &BuildError{Reason: ErrDependencyBuild, Err: err}
|
||||
return result, &BuildError{Reason: ErrDependencyBuild, Err: err}
|
||||
}
|
||||
if result.ResolvedDependencies, err = b.dm.Build(ctx, ref, chart); err != nil {
|
||||
return nil, &BuildError{Reason: ErrDependencyBuild, Err: err}
|
||||
return result, &BuildError{Reason: ErrDependencyBuild, Err: err}
|
||||
}
|
||||
}
|
||||
|
||||
// Package the chart
|
||||
if err = packageToPath(chart, p); err != nil {
|
||||
return nil, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||
return result, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||
}
|
||||
result.Path = p
|
||||
result.Packaged = true
|
||||
result.Packaged = requiresPackaging
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
@ -186,7 +190,7 @@ func mergeFileValues(baseDir string, paths []string) (map[string]interface{}, er
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if f, err := os.Stat(secureP); os.IsNotExist(err) || !f.Mode().IsRegular() {
|
||||
if f, err := os.Stat(secureP); err != nil || !f.Mode().IsRegular() {
|
||||
return nil, fmt.Errorf("no values file found at path '%s' (reference '%s')",
|
||||
strings.TrimPrefix(secureP, baseDir), p)
|
||||
}
|
||||
|
|
|
@ -82,12 +82,13 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
|||
cv, err := b.remote.Get(remoteRef.Name, remoteRef.Version)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get chart version for remote reference: %w", err)
|
||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
||||
return nil, &BuildError{Reason: ErrChartReference, Err: err}
|
||||
}
|
||||
|
||||
result := &Build{}
|
||||
result.Name = cv.Name
|
||||
result.Version = cv.Version
|
||||
|
||||
// Set build specific metadata if instructed
|
||||
if opts.VersionMetadata != "" {
|
||||
ver, err := semver.NewVersion(result.Version)
|
||||
|
@ -102,6 +103,8 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
|||
result.Version = ver.String()
|
||||
}
|
||||
|
||||
requiresPackaging := len(opts.GetValuesFiles()) != 0 || opts.VersionMetadata != ""
|
||||
|
||||
// If all the following is true, we do not need to download and/or build the chart:
|
||||
// - Chart name from cached chart matches resolved name
|
||||
// - Chart version from cached chart matches calculated version
|
||||
|
@ -114,6 +117,7 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
|||
if result.Name == curMeta.Name && result.Version == curMeta.Version {
|
||||
result.Path = opts.CachedChart
|
||||
result.ValuesFiles = opts.GetValuesFiles()
|
||||
result.Packaged = requiresPackaging
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
@ -124,12 +128,12 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
|||
res, err := b.remote.DownloadChart(cv)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to download chart for remote reference: %w", err)
|
||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
||||
return result, &BuildError{Reason: ErrChartPull, Err: err}
|
||||
}
|
||||
|
||||
// Use literal chart copy from remote if no custom values files options are
|
||||
// set or build option version metadata isn't set.
|
||||
if len(opts.GetValuesFiles()) == 0 && opts.VersionMetadata == "" {
|
||||
// set or version metadata isn't set.
|
||||
if !requiresPackaging {
|
||||
if err = validatePackageAndWriteToPath(res, p); err != nil {
|
||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
||||
}
|
||||
|
@ -141,14 +145,14 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
|||
var chart *helmchart.Chart
|
||||
if chart, err = loader.LoadArchive(res); err != nil {
|
||||
err = fmt.Errorf("failed to load downloaded chart: %w", err)
|
||||
return nil, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||
return result, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||
}
|
||||
chart.Metadata.Version = result.Version
|
||||
|
||||
mergedValues, err := mergeChartValues(chart, opts.ValuesFiles)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to merge chart values: %w", err)
|
||||
return nil, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||
return result, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||
}
|
||||
// Overwrite default values with merged values, if any
|
||||
if ok, err = OverwriteChartDefaultValues(chart, mergedValues); ok || err != nil {
|
||||
|
|
|
@ -138,12 +138,32 @@ func TestChartBuildResult_Summary(t *testing.T) {
|
|||
want string
|
||||
}{
|
||||
{
|
||||
name: "Simple",
|
||||
name: "Build with metadata",
|
||||
build: &Build{
|
||||
Name: "chart",
|
||||
Version: "1.2.3-rc.1+bd6bf40",
|
||||
},
|
||||
want: "Pulled 'chart' chart with version '1.2.3-rc.1+bd6bf40'.",
|
||||
want: "new 'chart' chart with version '1.2.3-rc.1+bd6bf40'",
|
||||
},
|
||||
{
|
||||
name: "Pulled chart",
|
||||
build: &Build{
|
||||
Name: "chart",
|
||||
Version: "1.2.3-rc.1+bd6bf40",
|
||||
Path: "chart.tgz",
|
||||
},
|
||||
want: "pulled 'chart' chart with version '1.2.3-rc.1+bd6bf40'",
|
||||
},
|
||||
{
|
||||
name: "Packaged chart",
|
||||
build: &Build{
|
||||
Name: "chart",
|
||||
Version: "arbitrary-version",
|
||||
Packaged: true,
|
||||
ValuesFiles: []string{"a.yaml", "b.yaml"},
|
||||
Path: "chart.tgz",
|
||||
},
|
||||
want: "packaged 'chart' chart with version 'arbitrary-version' and merged values files [a.yaml b.yaml]",
|
||||
},
|
||||
{
|
||||
name: "With values files",
|
||||
|
@ -152,28 +172,19 @@ func TestChartBuildResult_Summary(t *testing.T) {
|
|||
Version: "arbitrary-version",
|
||||
Packaged: true,
|
||||
ValuesFiles: []string{"a.yaml", "b.yaml"},
|
||||
Path: "chart.tgz",
|
||||
},
|
||||
want: "Packaged 'chart' chart with version 'arbitrary-version', with merged values files [a.yaml b.yaml].",
|
||||
},
|
||||
{
|
||||
name: "With dependencies",
|
||||
build: &Build{
|
||||
Name: "chart",
|
||||
Version: "arbitrary-version",
|
||||
Packaged: true,
|
||||
ResolvedDependencies: 5,
|
||||
},
|
||||
want: "Packaged 'chart' chart with version 'arbitrary-version', resolving 5 dependencies before packaging.",
|
||||
want: "packaged 'chart' chart with version 'arbitrary-version' and merged values files [a.yaml b.yaml]",
|
||||
},
|
||||
{
|
||||
name: "Empty build",
|
||||
build: &Build{},
|
||||
want: "No chart build.",
|
||||
want: "no chart build",
|
||||
},
|
||||
{
|
||||
name: "Nil build",
|
||||
build: nil,
|
||||
want: "No chart build.",
|
||||
want: "no chart build",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
|
|
@ -22,22 +22,29 @@ import (
|
|||
)
|
||||
|
||||
// BuildErrorReason is the descriptive reason for a BuildError.
|
||||
type BuildErrorReason string
|
||||
type BuildErrorReason struct {
|
||||
// Reason is the programmatic build error reason in CamelCase.
|
||||
Reason string
|
||||
|
||||
// Summary is the human build error reason, used to provide
|
||||
// the Error string, and further context to the BuildError.
|
||||
Summary string
|
||||
}
|
||||
|
||||
// Error returns the string representation of BuildErrorReason.
|
||||
func (e BuildErrorReason) Error() string {
|
||||
return string(e)
|
||||
return e.Summary
|
||||
}
|
||||
|
||||
// BuildError contains a wrapped Err and a Reason indicating why it occurred.
|
||||
type BuildError struct {
|
||||
Reason error
|
||||
Reason BuildErrorReason
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error returns Err as a string, prefixed with the Reason to provide context.
|
||||
func (e *BuildError) Error() string {
|
||||
if e.Reason == nil {
|
||||
if e.Reason.Error() == "" {
|
||||
return e.Err.Error()
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", e.Reason.Error(), e.Err.Error())
|
||||
|
@ -49,7 +56,7 @@ func (e *BuildError) Error() string {
|
|||
// err := &BuildError{Reason: ErrChartPull, Err: errors.New("arbitrary transport error")}
|
||||
// errors.Is(err, ErrChartPull)
|
||||
func (e *BuildError) Is(target error) bool {
|
||||
if e.Reason != nil && e.Reason == target {
|
||||
if e.Reason == target {
|
||||
return true
|
||||
}
|
||||
return errors.Is(e.Err, target)
|
||||
|
@ -60,11 +67,21 @@ func (e *BuildError) Unwrap() error {
|
|||
return e.Err
|
||||
}
|
||||
|
||||
func IsPersistentBuildErrorReason(err error) bool {
|
||||
switch err {
|
||||
case ErrChartReference, ErrChartMetadataPatch, ErrValuesFilesMerge:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
ErrChartReference = BuildErrorReason("chart reference error")
|
||||
ErrChartPull = BuildErrorReason("chart pull error")
|
||||
ErrChartMetadataPatch = BuildErrorReason("chart metadata patch error")
|
||||
ErrValuesFilesMerge = BuildErrorReason("values files merge error")
|
||||
ErrDependencyBuild = BuildErrorReason("dependency build error")
|
||||
ErrChartPackage = BuildErrorReason("chart package error")
|
||||
ErrChartReference = BuildErrorReason{Reason: "InvalidChartReference", Summary: "invalid chart reference"}
|
||||
ErrChartPull = BuildErrorReason{Reason: "ChartPullError", Summary: "chart pull error"}
|
||||
ErrChartMetadataPatch = BuildErrorReason{Reason: "MetadataPatchError", Summary: "chart metadata patch error"}
|
||||
ErrValuesFilesMerge = BuildErrorReason{Reason: "ValuesFilesError", Summary: "values files merge error"}
|
||||
ErrDependencyBuild = BuildErrorReason{Reason: "DependencyBuildError", Summary: "dependency build error"}
|
||||
ErrChartPackage = BuildErrorReason{Reason: "ChartPackageError", Summary: "chart package error"}
|
||||
ErrUnknown = BuildErrorReason{Reason: "Unknown", Summary: "unknown build error"}
|
||||
)
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
func TestBuildErrorReason_Error(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
err := BuildErrorReason("reason")
|
||||
err := BuildErrorReason{"Reason", "reason"}
|
||||
g.Expect(err.Error()).To(Equal("reason"))
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ func TestBuildError_Error(t *testing.T) {
|
|||
{
|
||||
name: "with reason",
|
||||
err: &BuildError{
|
||||
Reason: BuildErrorReason("reason"),
|
||||
Reason: BuildErrorReason{"Reason", "reason"},
|
||||
Err: errors.New("error"),
|
||||
},
|
||||
want: "reason: error",
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
Copyright 2022 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 object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrObservedGenerationNotFound = errors.New("observed generation not found")
|
||||
ErrLastHandledReconcileAtNotFound = errors.New("last handled reconcile at not found")
|
||||
ErrRequeueIntervalNotFound = errors.New("requeue interval not found")
|
||||
)
|
||||
|
||||
// toUnstructured converts a runtime object into Unstructured.
|
||||
// Based on https://github.com/fluxcd/pkg/blob/b4a14854c75753ea9431693b39c4be672f246552/runtime/patch/utils.go#L55.
|
||||
func toUnstructured(obj runtime.Object) (*unstructured.Unstructured, error) {
|
||||
// If the incoming object is already unstructured, perform a deep copy first
|
||||
// otherwise DefaultUnstructuredConverter ends up returning the inner map without
|
||||
// making a copy.
|
||||
if _, ok := obj.(runtime.Unstructured); ok {
|
||||
obj = obj.DeepCopyObject()
|
||||
}
|
||||
rawMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &unstructured.Unstructured{Object: rawMap}, nil
|
||||
}
|
||||
|
||||
// GetStatusLastHandledReconcileAt returns the status.lastHandledReconcileAt
|
||||
// value of a given runtime object, if present.
|
||||
func GetStatusLastHandledReconcileAt(obj runtime.Object) (string, error) {
|
||||
u, err := toUnstructured(obj)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ra, found, err := unstructured.NestedString(u.Object, "status", "lastHandledReconcileAt")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !found {
|
||||
return "", ErrLastHandledReconcileAtNotFound
|
||||
}
|
||||
return ra, nil
|
||||
}
|
||||
|
||||
// SetStatusLastHandledReconcileAt sets the status.lastHandledReconcileAt value
|
||||
// of a given runtime object.
|
||||
func SetStatusLastHandledReconcileAt(obj runtime.Object, val string) error {
|
||||
content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u := unstructured.Unstructured{}
|
||||
u.SetUnstructuredContent(content)
|
||||
if err := unstructured.SetNestedField(u.Object, val, "status", "lastHandledReconcileAt"); err != nil {
|
||||
return err
|
||||
}
|
||||
return runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, obj)
|
||||
}
|
||||
|
||||
// GetStatusObservedGeneration returns the status.observedGeneration of a given
|
||||
// runtime object.
|
||||
func GetStatusObservedGeneration(obj runtime.Object) (int64, error) {
|
||||
u, err := toUnstructured(obj)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
og, found, err := unstructured.NestedInt64(u.Object, "status", "observedGeneration")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !found {
|
||||
return 0, ErrObservedGenerationNotFound
|
||||
}
|
||||
return og, nil
|
||||
}
|
||||
|
||||
// GetRequeueInterval returns the spec.interval of a given runtime object, if
|
||||
// present.
|
||||
func GetRequeueInterval(obj runtime.Object) (time.Duration, error) {
|
||||
period := time.Second
|
||||
u, err := toUnstructured(obj)
|
||||
if err != nil {
|
||||
return period, err
|
||||
}
|
||||
interval, found, err := unstructured.NestedString(u.Object, "spec", "interval")
|
||||
if err != nil {
|
||||
return period, err
|
||||
}
|
||||
if !found {
|
||||
return period, ErrRequeueIntervalNotFound
|
||||
}
|
||||
return time.ParseDuration(interval)
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Copyright 2022 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 object
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
)
|
||||
|
||||
func TestGetStatusLastHandledReconcileAt(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
// Get unset status lastHandledReconcileAt.
|
||||
obj := &sourcev1.GitRepository{}
|
||||
_, err := GetStatusLastHandledReconcileAt(obj)
|
||||
g.Expect(err).To(Equal(ErrLastHandledReconcileAtNotFound))
|
||||
|
||||
// Get set status lastHandledReconcileAt.
|
||||
obj.Status.LastHandledReconcileAt = "foo"
|
||||
ra, err := GetStatusLastHandledReconcileAt(obj)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(ra).To(Equal("foo"))
|
||||
}
|
||||
|
||||
func TestSetStatusLastHandledReconcileAt(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
obj := &sourcev1.GitRepository{}
|
||||
err := SetStatusLastHandledReconcileAt(obj, "now")
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(obj.Status.LastHandledReconcileAt).To(Equal("now"))
|
||||
}
|
||||
|
||||
func TestGetStatusObservedGeneration(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
// Get unset status observedGeneration.
|
||||
obj := &sourcev1.GitRepository{}
|
||||
_, err := GetStatusObservedGeneration(obj)
|
||||
g.Expect(err).To(Equal(ErrObservedGenerationNotFound))
|
||||
|
||||
// Get set status observedGeneration.
|
||||
obj.Status.ObservedGeneration = 7
|
||||
og, err := GetStatusObservedGeneration(obj)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(og).To(Equal(int64(7)))
|
||||
}
|
||||
|
||||
func TestGetRequeueInterval(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
// Get empty requeue interval value.
|
||||
obj := &sourcev1.GitRepository{}
|
||||
pd, err := GetRequeueInterval(obj)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(pd).To(Equal(time.Duration(0)))
|
||||
|
||||
// Get set requeue interval value.
|
||||
obj.Spec.Interval = metav1.Duration{Duration: 3 * time.Second}
|
||||
pd, err = GetRequeueInterval(obj)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(pd).To(Equal(3 * time.Second))
|
||||
|
||||
// Get non-existent requeue interval value.
|
||||
obj2 := &corev1.Secret{}
|
||||
_, err = GetRequeueInterval(obj2)
|
||||
g.Expect(err).To(Equal(ErrRequeueIntervalNotFound))
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
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 (
|
||||
"time"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
|
||||
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. It is
|
||||
// also used when returning an error, since the error overshadows result.
|
||||
ResultEmpty Result = iota
|
||||
// ResultRequeue indicates a reconcile result which should immediately
|
||||
// requeue.
|
||||
ResultRequeue
|
||||
// ResultSuccess indicates a reconcile success result.
|
||||
// For a reconciler that requeues regularly at a fixed interval, runtime
|
||||
// result with a fixed RequeueAfter is success result.
|
||||
// For a reconciler that doesn't requeue on successful reconciliation,
|
||||
// an empty runtime result is success result.
|
||||
// It is usually returned at the end of a reconciler/sub-reconciler.
|
||||
ResultSuccess
|
||||
)
|
||||
|
||||
// RuntimeResultBuilder defines an interface for runtime result builders. This
|
||||
// can be implemented to build custom results based on the context of the
|
||||
// reconciler.
|
||||
type RuntimeResultBuilder interface {
|
||||
BuildRuntimeResult(rr Result, err error) ctrl.Result
|
||||
}
|
||||
|
||||
// AlwaysRequeueResultBuilder implements a RuntimeResultBuilder for always
|
||||
// requeuing reconcilers. A successful reconciliation result for such
|
||||
// reconcilers contains a fixed RequeueAfter value.
|
||||
type AlwaysRequeueResultBuilder struct {
|
||||
// RequeueAfter is the fixed period at which the reconciler requeues on
|
||||
// successful execution.
|
||||
RequeueAfter time.Duration
|
||||
}
|
||||
|
||||
// BuildRuntimeResult converts a given Result and error into the
|
||||
// return values of a controller's Reconcile function.
|
||||
func (r AlwaysRequeueResultBuilder) BuildRuntimeResult(rr Result, err error) ctrl.Result {
|
||||
// Handle special errors that contribute to expressing the result.
|
||||
if e, ok := err.(*serror.Waiting); ok {
|
||||
return ctrl.Result{RequeueAfter: e.RequeueAfter}
|
||||
}
|
||||
|
||||
switch rr {
|
||||
case ResultRequeue:
|
||||
return ctrl.Result{Requeue: true}
|
||||
case ResultSuccess:
|
||||
return ctrl.Result{RequeueAfter: r.RequeueAfter}
|
||||
default:
|
||||
return ctrl.Result{}
|
||||
}
|
||||
}
|
||||
|
||||
// ComputeReconcileResult analyzes the reconcile results (result + error),
|
||||
// updates the status conditions of the object with any corrections and returns
|
||||
// object patch configuration, runtime result and runtime error. The caller is
|
||||
// responsible for using the patch configuration while patching the object in
|
||||
// the API server.
|
||||
// The RuntimeResultBuilder is used to define how the ctrl.Result is computed.
|
||||
func ComputeReconcileResult(obj conditions.Setter, res Result, recErr error, rb RuntimeResultBuilder) ([]patch.Option, ctrl.Result, error) {
|
||||
var pOpts []patch.Option
|
||||
|
||||
// Compute the runtime result.
|
||||
var result ctrl.Result
|
||||
if rb != nil {
|
||||
result = rb.BuildRuntimeResult(res, recErr)
|
||||
}
|
||||
|
||||
// Remove reconciling condition on successful reconciliation.
|
||||
if recErr == nil && res == ResultSuccess {
|
||||
conditions.Delete(obj, meta.ReconcilingCondition)
|
||||
}
|
||||
|
||||
// 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, result, nil
|
||||
}
|
||||
// NOTE: Non-empty result with stalling error indicates that the
|
||||
// returned result is incorrect.
|
||||
case *serror.Waiting:
|
||||
// The reconcile resulted in waiting error, remove stalled condition if
|
||||
// present.
|
||||
conditions.Delete(obj, meta.StalledCondition)
|
||||
// The reconciler needs to wait and retry. Return no error.
|
||||
return pOpts, result, nil
|
||||
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, result, 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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
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/v1beta2"
|
||||
serror "github.com/fluxcd/source-controller/internal/error"
|
||||
)
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This test uses AlwaysRequeueResultBuilder as the RuntimeResultBuilder.
|
||||
func TestComputeReconcileResult(t *testing.T) {
|
||||
testSuccessInterval := time.Minute
|
||||
tests := []struct {
|
||||
name string
|
||||
result Result
|
||||
beforeFunc func(obj conditions.Setter)
|
||||
recErr error
|
||||
wantResult ctrl.Result
|
||||
wantErr bool
|
||||
assertConditions []metav1.Condition
|
||||
afterFunc func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions)
|
||||
}{
|
||||
{
|
||||
name: "successful result",
|
||||
result: ResultSuccess,
|
||||
recErr: nil,
|
||||
wantResult: ctrl.Result{RequeueAfter: testSuccessInterval},
|
||||
wantErr: false,
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeTrue())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "successful result, Reconciling=True, remove Reconciling",
|
||||
result: ResultSuccess,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkReconciling(obj, "NewRevision", "new revision")
|
||||
},
|
||||
recErr: nil,
|
||||
wantResult: ctrl.Result{RequeueAfter: testSuccessInterval},
|
||||
wantErr: false,
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeTrue())
|
||||
t.Expect(conditions.IsUnknown(obj, meta.ReconcilingCondition)).To(BeTrue())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "successful result, Stalled=True, remove Stalled",
|
||||
result: ResultSuccess,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkStalled(obj, "SomeReason", "some message")
|
||||
},
|
||||
recErr: nil,
|
||||
wantResult: ctrl.Result{RequeueAfter: testSuccessInterval},
|
||||
wantErr: false,
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeTrue())
|
||||
t.Expect(conditions.IsUnknown(obj, meta.StalledCondition)).To(BeTrue())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "requeue result",
|
||||
result: ResultRequeue,
|
||||
recErr: nil,
|
||||
wantResult: ctrl.Result{Requeue: true},
|
||||
wantErr: false,
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeFalse())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "requeue result",
|
||||
result: ResultRequeue,
|
||||
recErr: nil,
|
||||
wantResult: ctrl.Result{Requeue: true},
|
||||
wantErr: false,
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeFalse())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "stalling error",
|
||||
result: ResultEmpty,
|
||||
recErr: &serror.Stalling{Err: fmt.Errorf("some error"), Reason: "some reason"},
|
||||
wantResult: ctrl.Result{},
|
||||
wantErr: false,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.StalledCondition, "some reason", "some error"),
|
||||
},
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeTrue())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "waiting error",
|
||||
result: ResultEmpty,
|
||||
recErr: &serror.Waiting{Err: fmt.Errorf("some error"), Reason: "some reason"},
|
||||
wantResult: ctrl.Result{},
|
||||
wantErr: false,
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeFalse())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "random error",
|
||||
result: ResultEmpty,
|
||||
recErr: fmt.Errorf("some error"),
|
||||
wantResult: ctrl.Result{},
|
||||
wantErr: true,
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeFalse())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "random error, Stalled=True, remove Stalled",
|
||||
result: ResultEmpty,
|
||||
recErr: fmt.Errorf("some error"),
|
||||
wantResult: ctrl.Result{},
|
||||
wantErr: true,
|
||||
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
|
||||
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeFalse())
|
||||
t.Expect(conditions.IsUnknown(obj, meta.StalledCondition)).To(BeTrue())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
obj := &sourcev1.GitRepository{}
|
||||
obj.Name = "test-git-repo"
|
||||
obj.Namespace = "default"
|
||||
obj.Spec.Interval = metav1.Duration{Duration: testSuccessInterval}
|
||||
|
||||
if tt.beforeFunc != nil {
|
||||
tt.beforeFunc(obj)
|
||||
}
|
||||
|
||||
rb := AlwaysRequeueResultBuilder{RequeueAfter: obj.Spec.Interval.Duration}
|
||||
pOpts, result, err := ComputeReconcileResult(obj, tt.result, tt.recErr, rb)
|
||||
g.Expect(err != nil).To(Equal(tt.wantErr))
|
||||
g.Expect(result).To(Equal(tt.wantResult))
|
||||
|
||||
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions(tt.assertConditions))
|
||||
|
||||
opts := &patch.HelperOptions{}
|
||||
for _, o := range pOpts {
|
||||
o.ApplyToHelper(opts)
|
||||
}
|
||||
tt.afterFunc(g, obj, opts)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
Copyright 2022 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 summarize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/types"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/fluxcd/source-controller/internal/object"
|
||||
)
|
||||
|
||||
// HaveStatusObservedGeneration returns a custom matcher to check if a
|
||||
// runtime.Object has a given status observedGeneration value.
|
||||
func HaveStatusObservedGeneration(expected int64) types.GomegaMatcher {
|
||||
return &haveStatusObservedGeneration{
|
||||
expected: expected,
|
||||
}
|
||||
}
|
||||
|
||||
type haveStatusObservedGeneration struct {
|
||||
expected int64
|
||||
actual int64
|
||||
}
|
||||
|
||||
func (m *haveStatusObservedGeneration) Match(actual interface{}) (success bool, err error) {
|
||||
obj, ok := actual.(runtime.Object)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("actual should be a runtime object")
|
||||
}
|
||||
|
||||
og, err := object.GetStatusObservedGeneration(obj)
|
||||
if err != nil && err != object.ErrObservedGenerationNotFound {
|
||||
return false, err
|
||||
}
|
||||
m.actual = og
|
||||
|
||||
return Equal(m.expected).Match(og)
|
||||
}
|
||||
|
||||
func (m *haveStatusObservedGeneration) FailureMessage(actual interface{}) (message string) {
|
||||
return fmt.Sprintf("expected\n\t%d\nto match\n\t%d\n", m.actual, m.expected)
|
||||
}
|
||||
|
||||
func (m *haveStatusObservedGeneration) NegatedFailureMessage(actual interface{}) (message string) {
|
||||
return fmt.Sprintf("expected\n\t%d\nto not match\n\t%d\n", m.actual, m.expected)
|
||||
}
|
||||
|
||||
// HaveStatusLastHandledReconcileAt returns a custom matcher to check if a
|
||||
// runtime.Object has a given status lastHandledReconcileAt value.
|
||||
func HaveStatusLastHandledReconcileAt(expected string) types.GomegaMatcher {
|
||||
return &haveStatusLastHandledReconcileAt{
|
||||
expected: expected,
|
||||
}
|
||||
}
|
||||
|
||||
type haveStatusLastHandledReconcileAt struct {
|
||||
expected string
|
||||
actual string
|
||||
}
|
||||
|
||||
func (m *haveStatusLastHandledReconcileAt) Match(actual interface{}) (success bool, err error) {
|
||||
obj, ok := actual.(runtime.Object)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("actual should be a runtime object")
|
||||
}
|
||||
|
||||
ra, err := object.GetStatusLastHandledReconcileAt(obj)
|
||||
if err != nil && err != object.ErrLastHandledReconcileAtNotFound {
|
||||
return false, err
|
||||
}
|
||||
m.actual = ra
|
||||
|
||||
return Equal(m.expected).Match(ra)
|
||||
}
|
||||
|
||||
func (m *haveStatusLastHandledReconcileAt) FailureMessage(actual interface{}) (message string) {
|
||||
return fmt.Sprintf("expected\n\t%s\nto match\n\t%s\n", m.actual, m.expected)
|
||||
}
|
||||
|
||||
func (m *haveStatusLastHandledReconcileAt) NegatedFailureMessage(actual interface{}) (message string) {
|
||||
return fmt.Sprintf("expected\n\t%s\nto not match\n\t%s\n", m.actual, m.expected)
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Copyright 2022 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 summarize
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kuberecorder "k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
serror "github.com/fluxcd/source-controller/internal/error"
|
||||
"github.com/fluxcd/source-controller/internal/object"
|
||||
"github.com/fluxcd/source-controller/internal/reconcile"
|
||||
)
|
||||
|
||||
// ResultProcessor processes the results of reconciliation (the object, result
|
||||
// and error). Any errors during processing need not result in the
|
||||
// reconciliation failure. The errors can be recorded as logs and events.
|
||||
type ResultProcessor func(context.Context, kuberecorder.EventRecorder, client.Object, reconcile.Result, error)
|
||||
|
||||
// RecordContextualError is a ResultProcessor that records the contextual errors
|
||||
// based on their types.
|
||||
// An event is recorded for the errors that are returned to the runtime. The
|
||||
// runtime handles the logging of the error.
|
||||
// An event is recorded and an error is logged for errors that are known to be
|
||||
// swallowed, not returned to the runtime.
|
||||
func RecordContextualError(ctx context.Context, recorder kuberecorder.EventRecorder, obj client.Object, _ reconcile.Result, err error) {
|
||||
switch e := err.(type) {
|
||||
case *serror.Event:
|
||||
recorder.Eventf(obj, corev1.EventTypeWarning, e.Reason, e.Error())
|
||||
case *serror.Waiting:
|
||||
// Waiting errors are not returned to the runtime. Log it explicitly.
|
||||
ctrl.LoggerFrom(ctx).Info("reconciliation waiting", "reason", e.Err, "duration", e.RequeueAfter)
|
||||
recorder.Event(obj, corev1.EventTypeNormal, 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())
|
||||
}
|
||||
}
|
||||
|
||||
// RecordReconcileReq is a ResultProcessor that checks the reconcile
|
||||
// annotation value and sets it in the object status as
|
||||
// status.lastHandledReconcileAt.
|
||||
func RecordReconcileReq(ctx context.Context, recorder kuberecorder.EventRecorder, obj client.Object, _ reconcile.Result, _ error) {
|
||||
if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok {
|
||||
object.SetStatusLastHandledReconcileAt(obj, v)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
Copyright 2022 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 summarize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
"github.com/fluxcd/source-controller/internal/object"
|
||||
"github.com/fluxcd/source-controller/internal/reconcile"
|
||||
)
|
||||
|
||||
func TestRecordReconcileReq(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
beforeFunc func(obj client.Object)
|
||||
afterFunc func(t *WithT, obj client.Object)
|
||||
}{
|
||||
{
|
||||
name: "no reconcile req",
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).To(HaveStatusLastHandledReconcileAt(""))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no reconcile req, noop on existing value",
|
||||
beforeFunc: func(obj client.Object) {
|
||||
object.SetStatusLastHandledReconcileAt(obj, "zzz")
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).To(HaveStatusLastHandledReconcileAt("zzz"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with reconcile req",
|
||||
beforeFunc: func(obj client.Object) {
|
||||
annotations := map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "now",
|
||||
}
|
||||
obj.SetAnnotations(annotations)
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).To(HaveStatusLastHandledReconcileAt("now"))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
obj := &sourcev1.GitRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test-obj",
|
||||
},
|
||||
}
|
||||
|
||||
if tt.beforeFunc != nil {
|
||||
tt.beforeFunc(obj)
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
RecordReconcileReq(ctx, record.NewFakeRecorder(32), obj, reconcile.ResultEmpty, nil)
|
||||
|
||||
if tt.afterFunc != nil {
|
||||
tt.afterFunc(g, obj)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
Copyright 2022 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 summarize
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
kerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
kuberecorder "k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
|
||||
"github.com/fluxcd/source-controller/internal/reconcile"
|
||||
)
|
||||
|
||||
// Conditions contains all the conditions information needed to summarize the
|
||||
// target condition.
|
||||
type Conditions struct {
|
||||
// Target is the target condition, e.g.: Ready.
|
||||
Target string
|
||||
// Owned conditions are the conditions owned by the reconciler for this
|
||||
// target condition.
|
||||
Owned []string
|
||||
// Summarize conditions are the conditions that the target condition depends
|
||||
// on.
|
||||
Summarize []string
|
||||
// NegativePolarity conditions are the conditions in Summarize with negative
|
||||
// polarity.
|
||||
NegativePolarity []string
|
||||
}
|
||||
|
||||
// Helper is SummarizeAndPatch helper.
|
||||
type Helper struct {
|
||||
recorder kuberecorder.EventRecorder
|
||||
patchHelper *patch.Helper
|
||||
}
|
||||
|
||||
// NewHelper returns an initialized Helper.
|
||||
func NewHelper(recorder kuberecorder.EventRecorder, patchHelper *patch.Helper) *Helper {
|
||||
return &Helper{
|
||||
recorder: recorder,
|
||||
patchHelper: patchHelper,
|
||||
}
|
||||
}
|
||||
|
||||
// HelperOptions contains options for SummarizeAndPatch.
|
||||
// Summarizing and patching at the very end of a reconciliation involves
|
||||
// computing the result of the reconciler. This requires providing the
|
||||
// ReconcileResult, ReconcileError and a ResultBuilder in the context of the
|
||||
// reconciliation.
|
||||
// For using this to perform intermediate patching in the middle of a
|
||||
// reconciliation, no ReconcileResult, ReconcileError or ResultBuilder should
|
||||
// be provided. Only Conditions summary would be calculated and patched.
|
||||
type HelperOptions struct {
|
||||
// Conditions are conditions that needs to be summarized and persisted on
|
||||
// the object.
|
||||
Conditions []Conditions
|
||||
// Processors are chain of ResultProcessors for processing the results. This
|
||||
// can be used to analyze and modify the results. This enables injecting
|
||||
// custom middlewares in the SummarizeAndPatch operation.
|
||||
Processors []ResultProcessor
|
||||
// IgnoreNotFound can be used to ignores any resource not found error during
|
||||
// patching.
|
||||
IgnoreNotFound bool
|
||||
// ReconcileResult is the abstracted result of reconciliation.
|
||||
ReconcileResult reconcile.Result
|
||||
// ReconcileError is the reconciliation error.
|
||||
ReconcileError error
|
||||
// ResultBuilder defines how the reconciliation result is computed.
|
||||
ResultBuilder reconcile.RuntimeResultBuilder
|
||||
// PatchFieldOwner defines the field owner configuration for the Kubernetes
|
||||
// patch operation.
|
||||
PatchFieldOwner string
|
||||
}
|
||||
|
||||
// Option is configuration that modifies SummarizeAndPatch.
|
||||
type Option func(*HelperOptions)
|
||||
|
||||
// WithConditions sets the Conditions for which summary is calculated in
|
||||
// SummarizeAndPatch.
|
||||
func WithConditions(condns ...Conditions) Option {
|
||||
return func(s *HelperOptions) {
|
||||
s.Conditions = append(s.Conditions, condns...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithProcessors can be used to inject middlewares in the SummarizeAndPatch
|
||||
// process, to be executed before the result calculation and patching.
|
||||
func WithProcessors(rps ...ResultProcessor) Option {
|
||||
return func(s *HelperOptions) {
|
||||
s.Processors = append(s.Processors, rps...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithIgnoreNotFound skips any resource not found error during patching.
|
||||
func WithIgnoreNotFound() Option {
|
||||
return func(s *HelperOptions) {
|
||||
s.IgnoreNotFound = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithResultBuilder sets the strategy for result computation in
|
||||
// SummarizeAndPatch.
|
||||
func WithResultBuilder(rb reconcile.RuntimeResultBuilder) Option {
|
||||
return func(s *HelperOptions) {
|
||||
s.ResultBuilder = rb
|
||||
}
|
||||
}
|
||||
|
||||
// WithReconcileResult sets the value of input result used to calculate the
|
||||
// results of reconciliation in SummarizeAndPatch.
|
||||
func WithReconcileResult(rr reconcile.Result) Option {
|
||||
return func(s *HelperOptions) {
|
||||
s.ReconcileResult = rr
|
||||
}
|
||||
}
|
||||
|
||||
// WithReconcileError sets the value of input error used to calculate the
|
||||
// results reconciliation in SummarizeAndPatch.
|
||||
func WithReconcileError(re error) Option {
|
||||
return func(s *HelperOptions) {
|
||||
s.ReconcileError = re
|
||||
}
|
||||
}
|
||||
|
||||
// WithPatchFieldOwner sets the FieldOwner in the patch helper.
|
||||
func WithPatchFieldOwner(fieldOwner string) Option {
|
||||
return func(s *HelperOptions) {
|
||||
s.PatchFieldOwner = fieldOwner
|
||||
}
|
||||
}
|
||||
|
||||
// SummarizeAndPatch summarizes and patches the result to the target object.
|
||||
// When used at the very end of a reconciliation, the result builder must be
|
||||
// specified using the Option WithResultBuilder(). The returned result and error
|
||||
// can be returned as the return values of the reconciliation.
|
||||
// When used in the middle of a reconciliation, no result builder should be set
|
||||
// and the result can be ignored.
|
||||
func (h *Helper) SummarizeAndPatch(ctx context.Context, obj conditions.Setter, options ...Option) (ctrl.Result, error) {
|
||||
// Calculate the options.
|
||||
opts := &HelperOptions{}
|
||||
for _, o := range options {
|
||||
o(opts)
|
||||
}
|
||||
// Combined the owned conditions of all the conditions for the patcher.
|
||||
ownedConditions := []string{}
|
||||
for _, c := range opts.Conditions {
|
||||
ownedConditions = append(ownedConditions, c.Owned...)
|
||||
}
|
||||
// Patch the object, prioritizing the conditions owned by the controller in
|
||||
// case of any conflicts.
|
||||
patchOpts := []patch.Option{
|
||||
patch.WithOwnedConditions{
|
||||
Conditions: ownedConditions,
|
||||
},
|
||||
}
|
||||
if opts.PatchFieldOwner != "" {
|
||||
patchOpts = append(patchOpts, patch.WithFieldOwner(opts.PatchFieldOwner))
|
||||
}
|
||||
|
||||
// Process the results of reconciliation.
|
||||
for _, processor := range opts.Processors {
|
||||
processor(ctx, h.recorder, obj, opts.ReconcileResult, opts.ReconcileError)
|
||||
}
|
||||
|
||||
var result ctrl.Result
|
||||
var recErr error
|
||||
if opts.ResultBuilder != nil {
|
||||
// Compute the reconcile results, obtain patch options and reconcile error.
|
||||
var pOpts []patch.Option
|
||||
pOpts, result, recErr = reconcile.ComputeReconcileResult(obj, opts.ReconcileResult, opts.ReconcileError, opts.ResultBuilder)
|
||||
patchOpts = append(patchOpts, pOpts...)
|
||||
}
|
||||
|
||||
// Summarize conditions. This must be performed only after computing the
|
||||
// reconcile result, since the object status is adjusted based on the
|
||||
// reconcile result and error.
|
||||
for _, c := range opts.Conditions {
|
||||
conditions.SetSummary(obj,
|
||||
c.Target,
|
||||
conditions.WithConditions(
|
||||
c.Summarize...,
|
||||
),
|
||||
conditions.WithNegativePolarityConditions(
|
||||
c.NegativePolarity...,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Finally, patch the resource.
|
||||
if err := h.patchHelper.Patch(ctx, obj, patchOpts...); err != nil {
|
||||
// Ignore patch error "not found" when the object is being deleted.
|
||||
if opts.IgnoreNotFound && !obj.GetDeletionTimestamp().IsZero() {
|
||||
err = kerrors.FilterOut(err, func(e error) bool { return apierrors.IsNotFound(e) })
|
||||
}
|
||||
recErr = kerrors.NewAggregate([]error{recErr, err})
|
||||
}
|
||||
|
||||
return result, recErr
|
||||
}
|
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
Copyright 2022 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 summarize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/darkowlzz/controller-check/status"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"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/v1beta2"
|
||||
serror "github.com/fluxcd/source-controller/internal/error"
|
||||
"github.com/fluxcd/source-controller/internal/reconcile"
|
||||
)
|
||||
|
||||
// This tests the scenario where SummarizeAndPatch is used at the very end of a
|
||||
// reconciliation.
|
||||
func TestSummarizeAndPatch(t *testing.T) {
|
||||
var testReadyConditions = Conditions{
|
||||
Target: meta.ReadyCondition,
|
||||
Owned: []string{
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.ReadyCondition,
|
||||
meta.ReconcilingCondition,
|
||||
meta.StalledCondition,
|
||||
},
|
||||
Summarize: []string{
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.StalledCondition,
|
||||
meta.ReconcilingCondition,
|
||||
},
|
||||
NegativePolarity: []string{
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.StalledCondition,
|
||||
meta.ReconcilingCondition,
|
||||
},
|
||||
}
|
||||
var testFooConditions = Conditions{
|
||||
Target: "Foo",
|
||||
Owned: []string{
|
||||
"Foo",
|
||||
"AAA",
|
||||
"BBB",
|
||||
},
|
||||
Summarize: []string{
|
||||
"AAA",
|
||||
"BBB",
|
||||
},
|
||||
NegativePolarity: []string{
|
||||
"BBB",
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
generation int64
|
||||
beforeFunc func(obj conditions.Setter)
|
||||
result reconcile.Result
|
||||
reconcileErr error
|
||||
conditions []Conditions
|
||||
wantErr bool
|
||||
afterFunc func(t *WithT, obj client.Object)
|
||||
assertConditions []metav1.Condition
|
||||
}{
|
||||
// Success/Fail indicates if a reconciliation succeeded or failed. On
|
||||
// a successful reconciliation, the object generation is expected to
|
||||
// match the observed generation in the object status.
|
||||
// All the cases have some Ready condition set, even if a test case is
|
||||
// unrelated to the conditions, because it's neseccary for a valid
|
||||
// status.
|
||||
{
|
||||
name: "Success, no extra conditions",
|
||||
generation: 4,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "test-msg")
|
||||
},
|
||||
conditions: []Conditions{testReadyConditions},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "test-msg"),
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).To(HaveStatusObservedGeneration(4))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Success, Ready=True",
|
||||
generation: 5,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "created")
|
||||
},
|
||||
conditions: []Conditions{testReadyConditions},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "created"),
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).To(HaveStatusObservedGeneration(5))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Success, removes reconciling for successful result",
|
||||
generation: 2,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkReconciling(obj, "NewRevision", "new index version")
|
||||
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "stored artifact")
|
||||
},
|
||||
conditions: []Conditions{testReadyConditions},
|
||||
result: reconcile.ResultSuccess,
|
||||
wantErr: false,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "stored artifact"),
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).To(HaveStatusObservedGeneration(2))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Success, record reconciliation request",
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
annotations := map[string]string{
|
||||
meta.ReconcileRequestAnnotation: "now",
|
||||
}
|
||||
obj.SetAnnotations(annotations)
|
||||
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "test-msg")
|
||||
},
|
||||
generation: 3,
|
||||
conditions: []Conditions{testReadyConditions},
|
||||
result: reconcile.ResultSuccess,
|
||||
wantErr: false,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "test-msg"),
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).To(HaveStatusLastHandledReconcileAt("now"))
|
||||
t.Expect(obj).To(HaveStatusObservedGeneration(3))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Fail, with multiple conditions ArtifactOutdated=True,Reconciling=True",
|
||||
generation: 7,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "new index revision")
|
||||
conditions.MarkReconciling(obj, "NewRevision", "new index revision")
|
||||
},
|
||||
conditions: []Conditions{testReadyConditions},
|
||||
reconcileErr: fmt.Errorf("failed to create dir"),
|
||||
wantErr: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.FalseCondition(meta.ReadyCondition, "NewRevision", "new index revision"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new index revision"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new index revision"),
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).ToNot(HaveStatusObservedGeneration(7))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Success, with subreconciler stalled error",
|
||||
generation: 9,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.FetchFailedCondition, "failed to construct client")
|
||||
},
|
||||
conditions: []Conditions{testReadyConditions},
|
||||
reconcileErr: &serror.Stalling{Err: fmt.Errorf("some error"), Reason: "some reason"},
|
||||
wantErr: false,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.FalseCondition(meta.ReadyCondition, sourcev1.FetchFailedCondition, "failed to construct client"),
|
||||
*conditions.TrueCondition(meta.StalledCondition, "some reason", "some error"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.FetchFailedCondition, "failed to construct client"),
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).To(HaveStatusObservedGeneration(9))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Fail, no error but requeue requested",
|
||||
generation: 3,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkFalse(obj, meta.ReadyCondition, meta.FailedReason, "test-msg")
|
||||
},
|
||||
conditions: []Conditions{testReadyConditions},
|
||||
result: reconcile.ResultRequeue,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.FalseCondition(meta.ReadyCondition, meta.FailedReason, "test-msg"),
|
||||
},
|
||||
afterFunc: func(t *WithT, obj client.Object) {
|
||||
t.Expect(obj).ToNot(HaveStatusObservedGeneration(3))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Success, multiple conditions summary",
|
||||
generation: 3,
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "test-msg")
|
||||
conditions.MarkTrue(obj, "AAA", "ZZZ", "zzz") // Positive polarity True.
|
||||
conditions.MarkTrue(obj, "BBB", "YYY", "yyy") // Negative polarity True.
|
||||
},
|
||||
conditions: []Conditions{testReadyConditions, testFooConditions},
|
||||
result: reconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "test-msg"),
|
||||
*conditions.FalseCondition("Foo", "YYY", "yyy"), // False summary.
|
||||
*conditions.TrueCondition("BBB", "YYY", "yyy"),
|
||||
*conditions.TrueCondition("AAA", "ZZZ", "zzz"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
g.Expect(sourcev1.AddToScheme(scheme))
|
||||
|
||||
builder := fakeclient.NewClientBuilder().WithScheme(scheme)
|
||||
client := builder.Build()
|
||||
obj := &sourcev1.GitRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test-",
|
||||
Generation: tt.generation,
|
||||
},
|
||||
Spec: sourcev1.GitRepositorySpec{
|
||||
Interval: metav1.Duration{Duration: 5 * time.Second},
|
||||
},
|
||||
}
|
||||
|
||||
if tt.beforeFunc != nil {
|
||||
tt.beforeFunc(obj)
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
g.Expect(client.Create(ctx, obj)).To(Succeed())
|
||||
patchHelper, err := patch.NewHelper(obj, client)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
summaryHelper := NewHelper(record.NewFakeRecorder(32), patchHelper)
|
||||
summaryOpts := []Option{
|
||||
WithReconcileResult(tt.result),
|
||||
WithReconcileError(tt.reconcileErr),
|
||||
WithConditions(tt.conditions...),
|
||||
WithIgnoreNotFound(),
|
||||
WithProcessors(RecordContextualError, RecordReconcileReq),
|
||||
WithResultBuilder(reconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.Spec.Interval.Duration}),
|
||||
}
|
||||
_, gotErr := summaryHelper.SummarizeAndPatch(ctx, obj, summaryOpts...)
|
||||
g.Expect(gotErr != nil).To(Equal(tt.wantErr))
|
||||
|
||||
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions(tt.assertConditions))
|
||||
|
||||
if tt.afterFunc != nil {
|
||||
tt.afterFunc(g, obj)
|
||||
}
|
||||
|
||||
// Check if the object status is valid as per kstatus.
|
||||
condns := &status.Conditions{NegativePolarity: testReadyConditions.NegativePolarity}
|
||||
checker := status.NewChecker(client, scheme, condns)
|
||||
checker.CheckErr(ctx, obj)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This tests the scenario where SummarizeAndPatch is used in the middle of
|
||||
// reconciliation.
|
||||
func TestSummarizeAndPatch_Intermediate(t *testing.T) {
|
||||
var testStageAConditions = Conditions{
|
||||
Target: "StageA",
|
||||
Owned: []string{"StageA", "A1", "A2", "A3"},
|
||||
Summarize: []string{"A1", "A2", "A3"},
|
||||
NegativePolarity: []string{"A3"},
|
||||
}
|
||||
var testStageBConditions = Conditions{
|
||||
Target: "StageB",
|
||||
Owned: []string{"StageB", "B1", "B2"},
|
||||
Summarize: []string{"B1", "B2"},
|
||||
NegativePolarity: []string{"B1"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
conditions []Conditions
|
||||
beforeFunc func(obj conditions.Setter)
|
||||
assertConditions []metav1.Condition
|
||||
}{
|
||||
{
|
||||
name: "single Conditions, True summary",
|
||||
conditions: []Conditions{testStageAConditions},
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkTrue(obj, "A1", "ZZZ", "zzz") // Positive polarity True.
|
||||
},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition("StageA", "ZZZ", "zzz"), // True summary.
|
||||
*conditions.TrueCondition("A1", "ZZZ", "zzz"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single Conditions, False summary",
|
||||
conditions: []Conditions{testStageAConditions},
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkTrue(obj, "A1", "ZZZ", "zzz") // Positive polarity True.
|
||||
conditions.MarkTrue(obj, "A3", "OOO", "ooo") // Negative polarity True.
|
||||
},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.FalseCondition("StageA", "OOO", "ooo"), // False summary.
|
||||
*conditions.TrueCondition("A3", "OOO", "ooo"),
|
||||
*conditions.TrueCondition("A1", "ZZZ", "zzz"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple Conditions",
|
||||
conditions: []Conditions{testStageAConditions, testStageBConditions},
|
||||
beforeFunc: func(obj conditions.Setter) {
|
||||
conditions.MarkTrue(obj, "A3", "ZZZ", "zzz") // Negative polarity True.
|
||||
conditions.MarkTrue(obj, "B2", "RRR", "rrr") // Positive polarity True.
|
||||
},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.FalseCondition("StageA", "ZZZ", "zzz"), // False summary.
|
||||
*conditions.TrueCondition("A3", "ZZZ", "zzz"),
|
||||
*conditions.TrueCondition("StageB", "RRR", "rrr"), // True summary.
|
||||
*conditions.TrueCondition("B2", "RRR", "rrr"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
g.Expect(sourcev1.AddToScheme(scheme))
|
||||
|
||||
builder := fakeclient.NewClientBuilder().WithScheme(scheme)
|
||||
kclient := builder.Build()
|
||||
|
||||
obj := &sourcev1.GitRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test-",
|
||||
},
|
||||
Spec: sourcev1.GitRepositorySpec{
|
||||
Interval: metav1.Duration{Duration: 5 * time.Second},
|
||||
},
|
||||
Status: sourcev1.GitRepositoryStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
*conditions.FalseCondition("StageA", "QQQ", "qqq"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if tt.beforeFunc != nil {
|
||||
tt.beforeFunc(obj)
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
g.Expect(kclient.Create(ctx, obj)).To(Succeed())
|
||||
patchHelper, err := patch.NewHelper(obj, kclient)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
summaryHelper := NewHelper(record.NewFakeRecorder(32), patchHelper)
|
||||
summaryOpts := []Option{
|
||||
WithConditions(tt.conditions...),
|
||||
}
|
||||
_, err = summaryHelper.SummarizeAndPatch(ctx, obj, summaryOpts...)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions(tt.assertConditions))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
// '<dir>/Kind-Namespace-Name-<random bytes><suffix>'.
|
||||
// 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())
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
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"
|
||||
"path/filepath"
|
||||
"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: filepath.Join(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",
|
||||
},
|
||||
}
|
||||
}
|
54
main.go
54
main.go
|
@ -33,17 +33,16 @@ 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"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
"github.com/fluxcd/source-controller/controllers"
|
||||
"github.com/fluxcd/source-controller/internal/helm"
|
||||
// +kubebuilder:scaffold:imports
|
||||
|
@ -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))
|
||||
|
@ -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,14 @@ func main() {
|
|||
probes.SetupChecks(mgr, setupLog)
|
||||
pprof.SetupHandlers(mgr, setupLog)
|
||||
|
||||
var eventRecorder *events.Recorder
|
||||
if eventRecorder, err = events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName); err != nil {
|
||||
setupLog.Error(err, "unable to create event recorder")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
metricsH := helper.MustMakeMetrics(mgr)
|
||||
|
||||
if storageAdvAddr == "" {
|
||||
storageAdvAddr = determineAdvStorageAddr(storageAddr, setupLog)
|
||||
}
|
||||
|
@ -170,11 +166,10 @@ func main() {
|
|||
|
||||
if err = (&controllers.GitRepositoryReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
EventRecorder: eventRecorder,
|
||||
Metrics: metricsH,
|
||||
Storage: storage,
|
||||
EventRecorder: mgr.GetEventRecorderFor(controllerName),
|
||||
ExternalEventRecorder: eventRecorder,
|
||||
MetricsRecorder: metricsRecorder,
|
||||
ControllerName: controllerName,
|
||||
}).SetupWithManagerAndOptions(mgr, controllers.GitRepositoryReconcilerOptions{
|
||||
MaxConcurrentReconciles: concurrent,
|
||||
DependencyRequeueInterval: requeueDependency,
|
||||
|
@ -184,12 +179,11 @@ func main() {
|
|||
}
|
||||
if err = (&controllers.HelmRepositoryReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
EventRecorder: eventRecorder,
|
||||
Metrics: metricsH,
|
||||
Storage: storage,
|
||||
Getters: getters,
|
||||
EventRecorder: mgr.GetEventRecorderFor(controllerName),
|
||||
ExternalEventRecorder: eventRecorder,
|
||||
MetricsRecorder: metricsRecorder,
|
||||
ControllerName: controllerName,
|
||||
}).SetupWithManagerAndOptions(mgr, controllers.HelmRepositoryReconcilerOptions{
|
||||
MaxConcurrentReconciles: concurrent,
|
||||
}); err != nil {
|
||||
|
@ -198,12 +192,11 @@ func main() {
|
|||
}
|
||||
if err = (&controllers.HelmChartReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Storage: storage,
|
||||
Getters: getters,
|
||||
EventRecorder: mgr.GetEventRecorderFor(controllerName),
|
||||
ExternalEventRecorder: eventRecorder,
|
||||
MetricsRecorder: metricsRecorder,
|
||||
EventRecorder: eventRecorder,
|
||||
Metrics: metricsH,
|
||||
ControllerName: controllerName,
|
||||
}).SetupWithManagerAndOptions(mgr, controllers.HelmChartReconcilerOptions{
|
||||
MaxConcurrentReconciles: concurrent,
|
||||
}); err != nil {
|
||||
|
@ -212,11 +205,10 @@ func main() {
|
|||
}
|
||||
if err = (&controllers.BucketReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
EventRecorder: eventRecorder,
|
||||
Metrics: metricsH,
|
||||
Storage: storage,
|
||||
EventRecorder: mgr.GetEventRecorderFor(controllerName),
|
||||
ExternalEventRecorder: eventRecorder,
|
||||
MetricsRecorder: metricsRecorder,
|
||||
ControllerName: controllerName,
|
||||
}).SetupWithManagerAndOptions(mgr, controllers.BucketReconcilerOptions{
|
||||
MaxConcurrentReconciles: concurrent,
|
||||
}); err != nil {
|
||||
|
|
|
@ -73,7 +73,8 @@ func ValidateSecret(secret map[string][]byte, name string) error {
|
|||
func (c *GCPClient) BucketExists(ctx context.Context, bucketName string) (bool, error) {
|
||||
_, err := c.Client.Bucket(bucketName).Attrs(ctx)
|
||||
if err == gcpstorage.ErrBucketNotExist {
|
||||
return false, err
|
||||
// Not returning error to be compatible with minio's API.
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -130,7 +130,7 @@ func TestBucketNotExists(t *testing.T) {
|
|||
Client: client,
|
||||
}
|
||||
exists, err := gcpClient.BucketExists(context.Background(), bucket)
|
||||
assert.Error(t, err, gcpstorage.ErrBucketNotExist.Error())
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, !exists)
|
||||
}
|
||||
|
||||
|
|
|
@ -129,3 +129,24 @@ func AuthOptionsFromSecret(URL string, secret *v1.Secret) (*AuthOptions, error)
|
|||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// AuthOptionsWithoutSecret constructs a minimal AuthOptions object from the
|
||||
// given URL and then validates the result. It returns the AuthOptions, or an
|
||||
// error.
|
||||
func AuthOptionsWithoutSecret(URL string) (*AuthOptions, error) {
|
||||
u, err := url.Parse(URL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse URL to determine auth strategy: %w", err)
|
||||
}
|
||||
|
||||
opts := &AuthOptions{
|
||||
Transport: TransportType(u.Scheme),
|
||||
Host: u.Host,
|
||||
}
|
||||
|
||||
if err = opts.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ import (
|
|||
|
||||
"github.com/fluxcd/pkg/gittestserver"
|
||||
"github.com/fluxcd/pkg/runtime/testenv"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
"github.com/fluxcd/source-controller/controllers"
|
||||
)
|
||||
|
||||
|
@ -149,7 +149,6 @@ func ensureDependencies() error {
|
|||
startEnvServer(func(m manager.Manager) {
|
||||
utilruntime.Must((&controllers.GitRepositoryReconciler{
|
||||
Client: m.GetClient(),
|
||||
Scheme: scheme.Scheme,
|
||||
Storage: storage,
|
||||
}).SetupWithManager(m))
|
||||
})
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
module github.com/fluxcd/source-controller/tests/fuzz
|
||||
|
||||
go 1.17
|
||||
|
||||
replace github.com/fluxcd/kustomize-controller/api => ../../api
|
||||
|
||||
replace github.com/fluxcd/kustomize-controller => ../../
|
||||
|
|
Loading…
Reference in New Issue