Merge pull request #1428 from fluxcd/helm-ga

Promote Helm APIs to `source.toolkit.fluxcd.io/v1` (GA)
This commit is contained in:
Stefan Prodan 2024-05-03 00:11:36 +03:00 committed by GitHub
commit cf750b57cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 5189 additions and 973 deletions

View File

@ -7,9 +7,15 @@ resources:
- group: source - group: source
kind: GitRepository kind: GitRepository
version: v1beta2 version: v1beta2
- group: source
kind: HelmRepository
version: v1
- group: source - group: source
kind: HelmRepository kind: HelmRepository
version: v1beta2 version: v1beta2
- group: source
kind: HelmChart
version: v1
- group: source - group: source
kind: HelmChart kind: HelmChart
version: v1beta2 version: v1beta2

View File

@ -16,13 +16,13 @@ and is a core component of the [GitOps toolkit](https://fluxcd.io/flux/component
## APIs ## APIs
| Kind | API Version | | Kind | API Version |
|---------------------------------------------------------|------------------------------------| |-------------------------------------------------------|------------------------------------|
| [GitRepository](docs/spec/v1/gitrepositories.md) | `source.toolkit.fluxcd.io/v1` | | [GitRepository](docs/spec/v1/gitrepositories.md) | `source.toolkit.fluxcd.io/v1` |
| [OCIRepository](docs/spec/v1beta2/ocirepositories.md) | `source.toolkit.fluxcd.io/v1beta2` | | [OCIRepository](docs/spec/v1beta2/ocirepositories.md) | `source.toolkit.fluxcd.io/v1beta2` |
| [HelmRepository](docs/spec/v1beta2/helmrepositories.md) | `source.toolkit.fluxcd.io/v1beta2` | | [HelmRepository](docs/spec/v1/helmrepositories.md) | `source.toolkit.fluxcd.io/v1` |
| [HelmChart](docs/spec/v1beta2/helmcharts.md) | `source.toolkit.fluxcd.io/v1beta2` | | [HelmChart](docs/spec/v1/helmcharts.md) | `source.toolkit.fluxcd.io/v1` |
| [Bucket](docs/spec/v1beta2/buckets.md) | `source.toolkit.fluxcd.io/v1beta2` | | [Bucket](docs/spec/v1beta2/buckets.md) | `source.toolkit.fluxcd.io/v1beta2` |
## Features ## Features

View File

@ -6,7 +6,7 @@ require (
github.com/fluxcd/pkg/apis/acl v0.3.0 github.com/fluxcd/pkg/apis/acl v0.3.0
github.com/fluxcd/pkg/apis/meta v1.5.0 github.com/fluxcd/pkg/apis/meta v1.5.0
k8s.io/apimachinery v0.30.0 k8s.io/apimachinery v0.30.0
sigs.k8s.io/controller-runtime v0.18.0 sigs.k8s.io/controller-runtime v0.18.1
) )
// Fix CVE-2022-28948 // Fix CVE-2022-28948

View File

@ -103,8 +103,8 @@ k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI=
k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.18.0 h1:Z7jKuX784TQSUL1TIyeuF7j8KXZ4RtSX0YgtjKcSTME= sigs.k8s.io/controller-runtime v0.18.1 h1:RpWbigmuiylbxOCLy0tGnq1cU1qWPwNIQzoJk+QeJx4=
sigs.k8s.io/controller-runtime v0.18.0/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= sigs.k8s.io/controller-runtime v0.18.1/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=

227
api/v1/helmchart_types.go Normal file
View File

@ -0,0 +1,227 @@
/*
Copyright 2024 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 v1
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/pkg/apis/meta"
)
// HelmChartKind is the string representation of a HelmChart.
const HelmChartKind = "HelmChart"
// HelmChartSpec specifies the desired state of a Helm chart.
type HelmChartSpec struct {
// Chart is the name or path the Helm chart is available at in the
// SourceRef.
// +required
Chart string `json:"chart"`
// Version is 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"`
// SourceRef is the reference to the Source the chart is available at.
// +required
SourceRef LocalHelmChartSourceReference `json:"sourceRef"`
// Interval at which the HelmChart SourceRef is checked for updates.
// This interval is approximate and may be subject to jitter to ensure
// efficient use of resources.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +required
Interval metav1.Duration `json:"interval"`
// ReconcileStrategy 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"`
// ValuesFiles is an 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"`
// IgnoreMissingValuesFiles controls whether to silently ignore missing values
// files rather than failing.
// +optional
IgnoreMissingValuesFiles bool `json:"ignoreMissingValuesFiles,omitempty"`
// Suspend tells the controller to suspend the reconciliation of this
// source.
// +optional
Suspend bool `json:"suspend,omitempty"`
// Verify contains the secret name containing the trusted public keys
// used to verify the signature and specifies which provider to use to check
// whether OCI image is authentic.
// This field is only supported when using HelmRepository source with spec.type 'oci'.
// Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.
// +optional
Verify *OCIRepositoryVerification `json:"verify,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 records the observed state of the HelmChart.
type HelmChartStatus struct {
// ObservedGeneration is the last observed generation of the HelmChart
// object.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// ObservedSourceArtifactRevision is the last observed Artifact.Revision
// of the HelmChartSpec.SourceRef.
// +optional
ObservedSourceArtifactRevision string `json:"observedSourceArtifactRevision,omitempty"`
// ObservedChartName is the last observed chart name as specified by the
// resolved chart reference.
// +optional
ObservedChartName string `json:"observedChartName,omitempty"`
// ObservedValuesFiles are the observed value files of the last successful
// reconciliation.
// It matches the chart in the last successfully reconciled artifact.
// +optional
ObservedValuesFiles []string `json:"observedValuesFiles,omitempty"`
// Conditions holds the conditions for the HelmChart.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// URL is the dynamic fetch link for the latest Artifact.
// It is provided on a "best effort" basis, and using the precise
// BucketStatus.Artifact data is recommended.
// +optional
URL string `json:"url,omitempty"`
// Artifact represents the output of the last successful reconciliation.
// +optional
Artifact *Artifact `json:"artifact,omitempty"`
meta.ReconcileRequestStatus `json:",inline"`
}
const (
// ChartPullSucceededReason signals that the pull of the Helm chart
// succeeded.
ChartPullSucceededReason string = "ChartPullSucceeded"
// ChartPackageSucceededReason signals 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
}
// 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 HelmChartSpec.ValuesFiles.
func (in *HelmChart) GetValuesFiles() []string {
return in.Spec.ValuesFiles
}
// +genclient
// +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="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// +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=""
// 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"`
}
// HelmChartList contains a list of HelmChart objects.
// +kubebuilder:object:root=true
type HelmChartList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []HelmChart `json:"items"`
}
func init() {
SchemeBuilder.Register(&HelmChart{}, &HelmChartList{})
}

View File

@ -0,0 +1,228 @@
/*
Copyright 2024 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 v1
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 used for indexing HelmRepository
// objects by their HelmRepositorySpec.URL.
HelmRepositoryURLIndexKey = ".metadata.helmRepositoryURL"
// HelmRepositoryTypeDefault is the default HelmRepository type.
// It is used when no type is specified and corresponds to a Helm repository.
HelmRepositoryTypeDefault = "default"
// HelmRepositoryTypeOCI is the type for an OCI repository.
HelmRepositoryTypeOCI = "oci"
)
// HelmRepositorySpec specifies the required configuration to produce an
// Artifact for a Helm repository index YAML.
type HelmRepositorySpec struct {
// URL of the Helm repository, a valid URL contains at least a protocol and
// host.
// +kubebuilder:validation:Pattern="^(http|https|oci)://.*$"
// +required
URL string `json:"url"`
// SecretRef specifies the Secret containing authentication credentials
// for the HelmRepository.
// For HTTP/S basic auth the secret must contain 'username' and 'password'
// fields.
// Support for TLS auth using the 'certFile' and 'keyFile', and/or 'caFile'
// keys is deprecated. Please use `.spec.certSecretRef` instead.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
// CertSecretRef can be given the name of a Secret containing
// either or both of
//
// - a PEM-encoded client certificate (`tls.crt`) and private
// key (`tls.key`);
// - a PEM-encoded CA certificate (`ca.crt`)
//
// and whichever are supplied, will be used for connecting to the
// registry. The client cert and key are useful if you are
// authenticating with a certificate; the CA cert is useful if
// you are using a self-signed server certificate. The Secret must
// be of type `Opaque` or `kubernetes.io/tls`.
//
// It takes precedence over the values specified in the Secret referred
// to by `.spec.secretRef`.
// +optional
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,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"`
// Interval at which the HelmRepository URL is checked for updates.
// This interval is approximate and may be subject to jitter to ensure
// efficient use of resources.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +optional
Interval metav1.Duration `json:"interval,omitempty"`
// Insecure allows connecting to a non-TLS HTTP container registry.
// This field is only taken into account if the .spec.type field is set to 'oci'.
// +optional
Insecure bool `json:"insecure,omitempty"`
// Timeout is used for the index fetch operation for an HTTPS helm repository,
// and for remote OCI Repository operations like pulling for an OCI helm
// chart by the associated HelmChart.
// Its default value is 60s.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m))+$"
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
// Suspend tells the controller to suspend the reconciliation of this
// HelmRepository.
// +optional
Suspend bool `json:"suspend,omitempty"`
// AccessFrom specifies an Access Control List for allowing cross-namespace
// references to this object.
// NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092
// +optional
AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"`
// Type of the HelmRepository.
// When this field is set to "oci", the URL field value must be prefixed with "oci://".
// +kubebuilder:validation:Enum=default;oci
// +optional
Type string `json:"type,omitempty"`
// Provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'.
// This field is optional, and only taken into account if the .spec.type field is set to 'oci'.
// When not specified, defaults to 'generic'.
// +kubebuilder:validation:Enum=generic;aws;azure;gcp
// +kubebuilder:default:=generic
// +optional
Provider string `json:"provider,omitempty"`
}
// HelmRepositoryStatus records the observed state of the HelmRepository.
type HelmRepositoryStatus struct {
// ObservedGeneration is the last observed generation of the HelmRepository
// object.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// Conditions holds the conditions for the HelmRepository.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// URL is the dynamic fetch link for the latest Artifact.
// It is provided on a "best effort" basis, and using the precise
// HelmRepositoryStatus.Artifact data is recommended.
// +optional
URL string `json:"url,omitempty"`
// Artifact represents the last successful HelmRepository reconciliation.
// +optional
Artifact *Artifact `json:"artifact,omitempty"`
meta.ReconcileRequestStatus `json:",inline"`
}
const (
// IndexationFailedReason signals that the HelmRepository index fetch
// failed.
IndexationFailedReason string = "IndexationFailed"
)
// 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 {
if in.Spec.Interval.Duration != 0 {
return in.Spec.Interval.Duration
}
return time.Minute
}
// GetTimeout returns the timeout duration used for various operations related
// to this HelmRepository.
func (in HelmRepository) GetTimeout() time.Duration {
if in.Spec.Timeout != nil {
return in.Spec.Timeout.Duration
}
return time.Minute
}
// GetArtifact returns the latest artifact from the source if present in the
// status sub-resource.
func (in *HelmRepository) GetArtifact() *Artifact {
return in.Status.Artifact
}
// +genclient
// +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="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// +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=""
// 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 objects.
// +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{})
}

View File

@ -0,0 +1,56 @@
/*
Copyright 2024 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 v1
import (
"github.com/fluxcd/pkg/apis/meta"
)
// OCIRepositoryVerification verifies the authenticity of an OCI Artifact
type OCIRepositoryVerification struct {
// Provider specifies the technology used to sign the OCI Artifact.
// +kubebuilder:validation:Enum=cosign;notation
// +kubebuilder:default:=cosign
Provider string `json:"provider"`
// SecretRef specifies the Kubernetes Secret containing the
// trusted public keys.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
// MatchOIDCIdentity specifies the identity matching criteria to use
// while verifying an OCI artifact which was signed using Cosign keyless
// signing. The artifact's identity is deemed to be verified if any of the
// specified matchers match against the identity.
// +optional
MatchOIDCIdentity []OIDCIdentityMatch `json:"matchOIDCIdentity,omitempty"`
}
// OIDCIdentityMatch specifies options for verifying the certificate identity,
// i.e. the issuer and the subject of the certificate.
type OIDCIdentityMatch struct {
// Issuer specifies the regex pattern to match against to verify
// the OIDC issuer in the Fulcio certificate. The pattern must be a
// valid Go regular expression.
// +required
Issuer string `json:"issuer"`
// Subject specifies the regex pattern to match against to verify
// the identity subject in the Fulcio certificate. The pattern must
// be a valid Go regular expression.
// +required
Subject string `json:"subject"`
}

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated //go:build !ignore_autogenerated
/* /*
Copyright 2023 The Flux authors Copyright 2024 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ limitations under the License.
package v1 package v1
import ( import (
"github.com/fluxcd/pkg/apis/acl"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -264,3 +265,300 @@ func (in *GitRepositoryVerification) DeepCopy() *GitRepositoryVerification {
in.DeepCopyInto(out) in.DeepCopyInto(out)
return 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.Verify != nil {
in, out := &in.Verify, &out.Verify
*out = new(OCIRepositoryVerification)
(*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.ObservedValuesFiles != nil {
in, out := &in.ObservedValuesFiles, &out.ObservedValuesFiles
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]metav1.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
}
if in.CertSecretRef != nil {
in, out := &in.CertSecretRef, &out.CertSecretRef
*out = new(meta.LocalObjectReference)
**out = **in
}
out.Interval = in.Interval
if in.Timeout != nil {
in, out := &in.Timeout, &out.Timeout
*out = new(metav1.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([]metav1.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
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OCIRepositoryVerification) DeepCopyInto(out *OCIRepositoryVerification) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(meta.LocalObjectReference)
**out = **in
}
if in.MatchOIDCIdentity != nil {
in, out := &in.MatchOIDCIdentity, &out.MatchOIDCIdentity
*out = make([]OIDCIdentityMatch, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIRepositoryVerification.
func (in *OCIRepositoryVerification) DeepCopy() *OCIRepositoryVerification {
if in == nil {
return nil
}
out := new(OCIRepositoryVerification)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OIDCIdentityMatch) DeepCopyInto(out *OIDCIdentityMatch) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCIdentityMatch.
func (in *OIDCIdentityMatch) DeepCopy() *OIDCIdentityMatch {
if in == nil {
return nil
}
out := new(OIDCIdentityMatch)
in.DeepCopyInto(out)
return out
}

View File

@ -195,6 +195,7 @@ func (in *Bucket) GetInterval() metav1.Duration {
// +genclient // +genclient
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// +kubebuilder:subresource:status // +kubebuilder:subresource:status
// +kubebuilder:deprecatedversion:warning="v1beta1 Bucket is deprecated, upgrade to v1beta2"
// +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint` // +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="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="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""

View File

@ -234,6 +234,7 @@ func (in *HelmChart) GetValuesFiles() []string {
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// +kubebuilder:resource:shortName=hc // +kubebuilder:resource:shortName=hc
// +kubebuilder:subresource:status // +kubebuilder:subresource:status
// +kubebuilder:deprecatedversion:warning="v1beta1 HelmChart is deprecated, upgrade to v1"
// +kubebuilder:printcolumn:name="Chart",type=string,JSONPath=`.spec.chart` // +kubebuilder:printcolumn:name="Chart",type=string,JSONPath=`.spec.chart`
// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version` // +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version`
// +kubebuilder:printcolumn:name="Source Kind",type=string,JSONPath=`.spec.sourceRef.kind` // +kubebuilder:printcolumn:name="Source Kind",type=string,JSONPath=`.spec.sourceRef.kind`

View File

@ -184,6 +184,7 @@ func (in *HelmRepository) GetInterval() metav1.Duration {
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// +kubebuilder:resource:shortName=helmrepo // +kubebuilder:resource:shortName=helmrepo
// +kubebuilder:subresource:status // +kubebuilder:subresource:status
// +kubebuilder:deprecatedversion:warning="v1beta1 HelmRepository is deprecated, upgrade to v1"
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url` // +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="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="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated //go:build !ignore_autogenerated
/* /*
Copyright 2023 The Flux authors Copyright 2024 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -23,6 +23,7 @@ import (
"github.com/fluxcd/pkg/apis/acl" "github.com/fluxcd/pkg/apis/acl"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
apiv1 "github.com/fluxcd/source-controller/api/v1" apiv1 "github.com/fluxcd/source-controller/api/v1"
) )
@ -101,7 +102,7 @@ type HelmChartSpec struct {
// This field is only supported when using HelmRepository source with spec.type 'oci'. // This field is only supported when using HelmRepository source with spec.type 'oci'.
// Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. // Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.
// +optional // +optional
Verify *OCIRepositoryVerification `json:"verify,omitempty"` Verify *apiv1.OCIRepositoryVerification `json:"verify,omitempty"`
} }
const ( const (
@ -214,10 +215,10 @@ func (in *HelmChart) GetValuesFiles() []string {
} }
// +genclient // +genclient
// +kubebuilder:storageversion
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// +kubebuilder:resource:shortName=hc // +kubebuilder:resource:shortName=hc
// +kubebuilder:subresource:status // +kubebuilder:subresource:status
// +kubebuilder:deprecatedversion:warning="v1beta2 HelmChart is deprecated, upgrade to v1"
// +kubebuilder:printcolumn:name="Chart",type=string,JSONPath=`.spec.chart` // +kubebuilder:printcolumn:name="Chart",type=string,JSONPath=`.spec.chart`
// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version` // +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version`
// +kubebuilder:printcolumn:name="Source Kind",type=string,JSONPath=`.spec.sourceRef.kind` // +kubebuilder:printcolumn:name="Source Kind",type=string,JSONPath=`.spec.sourceRef.kind`

View File

@ -198,10 +198,10 @@ func (in *HelmRepository) GetArtifact() *apiv1.Artifact {
} }
// +genclient // +genclient
// +kubebuilder:storageversion
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// +kubebuilder:resource:shortName=helmrepo // +kubebuilder:resource:shortName=helmrepo
// +kubebuilder:subresource:status // +kubebuilder:subresource:status
// +kubebuilder:deprecatedversion:warning="v1beta2 HelmRepository is deprecated, upgrade to v1"
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url` // +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""

View File

@ -22,6 +22,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
apiv1 "github.com/fluxcd/source-controller/api/v1" apiv1 "github.com/fluxcd/source-controller/api/v1"
) )
@ -89,7 +90,7 @@ type OCIRepositorySpec struct {
// used to verify the signature and specifies which provider to use to check // used to verify the signature and specifies which provider to use to check
// whether OCI image is authentic. // whether OCI image is authentic.
// +optional // +optional
Verify *OCIRepositoryVerification `json:"verify,omitempty"` Verify *apiv1.OCIRepositoryVerification `json:"verify,omitempty"`
// ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate // ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
// the image pull if the service account has attached pull secrets. For more information: // the image pull if the service account has attached pull secrets. For more information:
@ -183,41 +184,6 @@ type OCILayerSelector struct {
Operation string `json:"operation,omitempty"` Operation string `json:"operation,omitempty"`
} }
// OCIRepositoryVerification verifies the authenticity of an OCI Artifact
type OCIRepositoryVerification struct {
// Provider specifies the technology used to sign the OCI Artifact.
// +kubebuilder:validation:Enum=cosign;notation
// +kubebuilder:default:=cosign
Provider string `json:"provider"`
// SecretRef specifies the Kubernetes Secret containing the
// trusted public keys.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
// MatchOIDCIdentity specifies the identity matching criteria to use
// while verifying an OCI artifact which was signed using Cosign keyless
// signing. The artifact's identity is deemed to be verified if any of the
// specified matchers match against the identity.
// +optional
MatchOIDCIdentity []OIDCIdentityMatch `json:"matchOIDCIdentity,omitempty"`
}
// OIDCIdentityMatch specifies options for verifying the certificate identity,
// i.e. the issuer and the subject of the certificate.
type OIDCIdentityMatch struct {
// Issuer specifies the regex pattern to match against to verify
// the OIDC issuer in the Fulcio certificate. The pattern must be a
// valid Go regular expression.
// +required
Issuer string `json:"issuer"`
// Subject specifies the regex pattern to match against to verify
// the identity subject in the Fulcio certificate. The pattern must
// be a valid Go regular expression.
// +required
Subject string `json:"subject"`
}
// OCIRepositoryStatus defines the observed state of OCIRepository // OCIRepositoryStatus defines the observed state of OCIRepository
type OCIRepositoryStatus struct { type OCIRepositoryStatus struct {
// ObservedGeneration is the last observed generation. // ObservedGeneration is the last observed generation.

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated //go:build !ignore_autogenerated
/* /*
Copyright 2023 The Flux authors Copyright 2024 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -466,7 +466,7 @@ func (in *HelmChartSpec) DeepCopyInto(out *HelmChartSpec) {
} }
if in.Verify != nil { if in.Verify != nil {
in, out := &in.Verify, &out.Verify in, out := &in.Verify, &out.Verify
*out = new(OCIRepositoryVerification) *out = new(apiv1.OCIRepositoryVerification)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
} }
@ -761,7 +761,7 @@ func (in *OCIRepositorySpec) DeepCopyInto(out *OCIRepositorySpec) {
} }
if in.Verify != nil { if in.Verify != nil {
in, out := &in.Verify, &out.Verify in, out := &in.Verify, &out.Verify
*out = new(OCIRepositoryVerification) *out = new(apiv1.OCIRepositoryVerification)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.CertSecretRef != nil { if in.CertSecretRef != nil {
@ -829,43 +829,3 @@ func (in *OCIRepositoryStatus) DeepCopy() *OCIRepositoryStatus {
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OCIRepositoryVerification) DeepCopyInto(out *OCIRepositoryVerification) {
*out = *in
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(meta.LocalObjectReference)
**out = **in
}
if in.MatchOIDCIdentity != nil {
in, out := &in.MatchOIDCIdentity, &out.MatchOIDCIdentity
*out = make([]OIDCIdentityMatch, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OCIRepositoryVerification.
func (in *OCIRepositoryVerification) DeepCopy() *OCIRepositoryVerification {
if in == nil {
return nil
}
out := new(OCIRepositoryVerification)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OIDCIdentityMatch) DeepCopyInto(out *OIDCIdentityMatch) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCIdentityMatch.
func (in *OIDCIdentityMatch) DeepCopy() *OIDCIdentityMatch {
if in == nil {
return nil
}
out := new(OIDCIdentityMatch)
in.DeepCopyInto(out)
return out
}

View File

@ -27,6 +27,8 @@ spec:
- jsonPath: .metadata.creationTimestamp - jsonPath: .metadata.creationTimestamp
name: Age name: Age
type: date type: date
deprecated: true
deprecationWarning: v1beta1 Bucket is deprecated, upgrade to v1beta2
name: v1beta1 name: v1beta1
schema: schema:
openAPIV3Schema: openAPIV3Schema:

View File

@ -16,6 +16,351 @@ spec:
singular: helmchart singular: helmchart
scope: Namespaced scope: Namespaced
versions: versions:
- 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: .metadata.creationTimestamp
name: Age
type: date
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status
type: string
name: v1
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 specifies the desired state of a Helm chart.
properties:
chart:
description: |-
Chart is the name or path the Helm chart is available at in the
SourceRef.
type: string
ignoreMissingValuesFiles:
description: |-
IgnoreMissingValuesFiles controls whether to silently ignore missing values
files rather than failing.
type: boolean
interval:
description: |-
Interval at which the HelmChart SourceRef is checked for updates.
This interval is approximate and may be subject to jitter to ensure
efficient use of resources.
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$
type: string
reconcileStrategy:
default: ChartVersion
description: |-
ReconcileStrategy 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: SourceRef is 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: |-
Suspend tells the controller to suspend the reconciliation of this
source.
type: boolean
valuesFiles:
description: |-
ValuesFiles is an 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
verify:
description: |-
Verify contains the secret name containing the trusted public keys
used to verify the signature and specifies which provider to use to check
whether OCI image is authentic.
This field is only supported when using HelmRepository source with spec.type 'oci'.
Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified.
properties:
matchOIDCIdentity:
description: |-
MatchOIDCIdentity specifies the identity matching criteria to use
while verifying an OCI artifact which was signed using Cosign keyless
signing. The artifact's identity is deemed to be verified if any of the
specified matchers match against the identity.
items:
description: |-
OIDCIdentityMatch specifies options for verifying the certificate identity,
i.e. the issuer and the subject of the certificate.
properties:
issuer:
description: |-
Issuer specifies the regex pattern to match against to verify
the OIDC issuer in the Fulcio certificate. The pattern must be a
valid Go regular expression.
type: string
subject:
description: |-
Subject specifies the regex pattern to match against to verify
the identity subject in the Fulcio certificate. The pattern must
be a valid Go regular expression.
type: string
required:
- issuer
- subject
type: object
type: array
provider:
default: cosign
description: Provider specifies the technology used to sign the
OCI Artifact.
enum:
- cosign
- notation
type: string
secretRef:
description: |-
SecretRef specifies the Kubernetes Secret containing the
trusted public keys.
properties:
name:
description: Name of the referent.
type: string
required:
- name
type: object
required:
- provider
type: object
version:
default: '*'
description: |-
Version is 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 records the observed state of the HelmChart.
properties:
artifact:
description: Artifact represents the output of the last successful
reconciliation.
properties:
digest:
description: Digest is the digest of the file in the form of '<algorithm>:<checksum>'.
pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$
type: string
lastUpdateTime:
description: |-
LastUpdateTime is the timestamp corresponding to the last update of the
Artifact.
format: date-time
type: string
metadata:
additionalProperties:
type: string
description: Metadata holds upstream information such as OCI annotations.
type: object
path:
description: |-
Path is the relative file path of the Artifact. It can be used to locate
the 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 chart version, etc.
type: string
size:
description: Size is the number of bytes in the file.
format: int64
type: integer
url:
description: |-
URL is the HTTP address of the Artifact as exposed by the controller
managing the Source. It can be used to retrieve the Artifact for
consumption, e.g. by another controller applying the Artifact contents.
type: string
required:
- lastUpdateTime
- path
- revision
- 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.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
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 specified by the
resolved chart reference.
type: string
observedGeneration:
description: |-
ObservedGeneration is the last observed generation of the HelmChart
object.
format: int64
type: integer
observedSourceArtifactRevision:
description: |-
ObservedSourceArtifactRevision is the last observed Artifact.Revision
of the HelmChartSpec.SourceRef.
type: string
observedValuesFiles:
description: |-
ObservedValuesFiles are the observed value files of the last successful
reconciliation.
It matches the chart in the last successfully reconciled artifact.
items:
type: string
type: array
url:
description: |-
URL is the dynamic fetch link for the latest Artifact.
It is provided on a "best effort" basis, and using the precise
BucketStatus.Artifact data is recommended.
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
- additionalPrinterColumns: - additionalPrinterColumns:
- jsonPath: .spec.chart - jsonPath: .spec.chart
name: Chart name: Chart
@ -38,6 +383,8 @@ spec:
- jsonPath: .metadata.creationTimestamp - jsonPath: .metadata.creationTimestamp
name: Age name: Age
type: date type: date
deprecated: true
deprecationWarning: v1beta1 HelmChart is deprecated, upgrade to v1
name: v1beta1 name: v1beta1
schema: schema:
openAPIV3Schema: openAPIV3Schema:
@ -305,6 +652,8 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].message - jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status name: Status
type: string type: string
deprecated: true
deprecationWarning: v1beta2 HelmChart is deprecated, upgrade to v1
name: v1beta2 name: v1beta2
schema: schema:
openAPIV3Schema: openAPIV3Schema:
@ -660,6 +1009,6 @@ spec:
type: object type: object
type: object type: object
served: true served: true
storage: true storage: false
subresources: subresources:
status: {} status: {}

View File

@ -16,236 +16,6 @@ spec:
singular: helmrepository singular: helmrepository
scope: Namespaced scope: Namespaced
versions: versions:
- 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: v1beta1
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
caFile 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 downloading, 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.
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.
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.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
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 download link for the last index fetched.
type: string
type: object
type: object
served: true
storage: false
subresources:
status: {}
- additionalPrinterColumns: - additionalPrinterColumns:
- jsonPath: .spec.url - jsonPath: .spec.url
name: URL name: URL
@ -259,7 +29,7 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].message - jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status name: Status
type: string type: string
name: v1beta2 name: v1
schema: schema:
openAPIV3Schema: openAPIV3Schema:
description: HelmRepository is the Schema for the helmrepositories API. description: HelmRepository is the Schema for the helmrepositories API.
@ -564,3 +334,555 @@ spec:
storage: true storage: true
subresources: subresources:
status: {} 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
deprecated: true
deprecationWarning: v1beta1 HelmRepository is deprecated, upgrade to v1
name: v1beta1
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
caFile 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 downloading, 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.
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.
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.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
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 download link for the last index fetched.
type: string
type: object
type: object
served: true
storage: false
subresources:
status: {}
- additionalPrinterColumns:
- jsonPath: .spec.url
name: URL
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status
type: string
deprecated: true
deprecationWarning: v1beta2 HelmRepository is deprecated, upgrade to v1
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 specifies the required configuration to produce an
Artifact for a Helm repository index YAML.
properties:
accessFrom:
description: |-
AccessFrom specifies an Access Control List for allowing cross-namespace
references to this object.
NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092
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
certSecretRef:
description: |-
CertSecretRef can be given the name of a Secret containing
either or both of
- a PEM-encoded client certificate (`tls.crt`) and private
key (`tls.key`);
- a PEM-encoded CA certificate (`ca.crt`)
and whichever are supplied, will be used for connecting to the
registry. The client cert and key are useful if you are
authenticating with a certificate; the CA cert is useful if
you are using a self-signed server certificate. The Secret must
be of type `Opaque` or `kubernetes.io/tls`.
It takes precedence over the values specified in the Secret referred
to by `.spec.secretRef`.
properties:
name:
description: Name of the referent.
type: string
required:
- name
type: object
insecure:
description: |-
Insecure allows connecting to a non-TLS HTTP container registry.
This field is only taken into account if the .spec.type field is set to 'oci'.
type: boolean
interval:
description: |-
Interval at which the HelmRepository URL is checked for updates.
This interval is approximate and may be subject to jitter to ensure
efficient use of resources.
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$
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
provider:
default: generic
description: |-
Provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'.
This field is optional, and only taken into account if the .spec.type field is set to 'oci'.
When not specified, defaults to 'generic'.
enum:
- generic
- aws
- azure
- gcp
type: string
secretRef:
description: |-
SecretRef specifies the Secret containing authentication credentials
for the HelmRepository.
For HTTP/S basic auth the secret must contain 'username' and 'password'
fields.
Support for TLS auth using the 'certFile' and 'keyFile', and/or 'caFile'
keys is deprecated. Please use `.spec.certSecretRef` instead.
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
HelmRepository.
type: boolean
timeout:
description: |-
Timeout is used for the index fetch operation for an HTTPS helm repository,
and for remote OCI Repository operations like pulling for an OCI helm
chart by the associated HelmChart.
Its default value is 60s.
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$
type: string
type:
description: |-
Type of the HelmRepository.
When this field is set to "oci", the URL field value must be prefixed with "oci://".
enum:
- default
- oci
type: string
url:
description: |-
URL of the Helm repository, a valid URL contains at least a protocol and
host.
pattern: ^(http|https|oci)://.*$
type: string
required:
- url
type: object
status:
default:
observedGeneration: -1
description: HelmRepositoryStatus records the observed state of the HelmRepository.
properties:
artifact:
description: Artifact represents the last successful HelmRepository
reconciliation.
properties:
digest:
description: Digest is the digest of the file in the form of '<algorithm>:<checksum>'.
pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$
type: string
lastUpdateTime:
description: |-
LastUpdateTime is the timestamp corresponding to the last update of the
Artifact.
format: date-time
type: string
metadata:
additionalProperties:
type: string
description: Metadata holds upstream information such as OCI annotations.
type: object
path:
description: |-
Path is the relative file path of the Artifact. It can be used to locate
the 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 chart version, etc.
type: string
size:
description: Size is the number of bytes in the file.
format: int64
type: integer
url:
description: |-
URL is the HTTP address of the Artifact as exposed by the controller
managing the Source. It can be used to retrieve the Artifact for
consumption, e.g. by another controller applying the Artifact contents.
type: string
required:
- lastUpdateTime
- path
- revision
- 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.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
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 of the HelmRepository
object.
format: int64
type: integer
url:
description: |-
URL is the dynamic fetch link for the latest Artifact.
It is provided on a "best effort" basis, and using the precise
HelmRepositoryStatus.Artifact data is recommended.
type: string
type: object
type: object
served: true
storage: false
subresources:
status: {}

View File

@ -1,4 +1,4 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2 apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart kind: HelmChart
metadata: metadata:
name: helmchart-git-sample name: helmchart-git-sample

View File

@ -1,4 +1,4 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2 apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart kind: HelmChart
metadata: metadata:
name: helmchart-sample-oci name: helmchart-sample-oci

View File

@ -1,11 +1,12 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2 apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart kind: HelmChart
metadata: metadata:
name: helmchart-sample name: helmchart-sample
spec: spec:
chart: podinfo chart: podinfo
version: '>=2.0.0 <3.0.0' version: '6.x'
sourceRef: sourceRef:
kind: HelmRepository kind: HelmRepository
name: helmrepository-sample name: helmrepository-sample
interval: 1m interval: 1m
ignoreMissingValuesFiles: true

View File

@ -1,4 +1,4 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2 apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository kind: HelmRepository
metadata: metadata:
name: helmrepository-sample-oci name: helmrepository-sample-oci

View File

@ -1,4 +1,4 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2 apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository kind: HelmRepository
metadata: metadata:
name: helmrepository-sample name: helmrepository-sample

View File

@ -1,5 +1,5 @@
--- ---
apiVersion: source.toolkit.fluxcd.io/v1beta2 apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository kind: HelmRepository
metadata: metadata:
name: podinfo-notation name: podinfo-notation
@ -8,7 +8,7 @@ spec:
type: "oci" type: "oci"
interval: 1m interval: 1m
--- ---
apiVersion: source.toolkit.fluxcd.io/v1beta2 apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart kind: HelmChart
metadata: metadata:
name: podinfo-notation name: podinfo-notation

File diff suppressed because it is too large Load Diff

View File

@ -704,8 +704,8 @@ NOTE: Not implemented, provisional as of <a href="https://github.com/fluxcd/flux
<td> <td>
<code>verify</code><br> <code>verify</code><br>
<em> <em>
<a href="#source.toolkit.fluxcd.io/v1beta2.OCIRepositoryVerification"> <a href="https://pkg.go.dev/github.com/fluxcd/source-controller/api/v1#OCIRepositoryVerification">
OCIRepositoryVerification github.com/fluxcd/source-controller/api/v1.OCIRepositoryVerification
</a> </a>
</em> </em>
</td> </td>
@ -1122,8 +1122,8 @@ The secret must be of type kubernetes.io/dockerconfigjson.</p>
<td> <td>
<code>verify</code><br> <code>verify</code><br>
<em> <em>
<a href="#source.toolkit.fluxcd.io/v1beta2.OCIRepositoryVerification"> <a href="https://pkg.go.dev/github.com/fluxcd/source-controller/api/v1#OCIRepositoryVerification">
OCIRepositoryVerification github.com/fluxcd/source-controller/api/v1.OCIRepositoryVerification
</a> </a>
</em> </em>
</td> </td>
@ -2386,8 +2386,8 @@ NOTE: Not implemented, provisional as of <a href="https://github.com/fluxcd/flux
<td> <td>
<code>verify</code><br> <code>verify</code><br>
<em> <em>
<a href="#source.toolkit.fluxcd.io/v1beta2.OCIRepositoryVerification"> <a href="https://pkg.go.dev/github.com/fluxcd/source-controller/api/v1#OCIRepositoryVerification">
OCIRepositoryVerification github.com/fluxcd/source-controller/api/v1.OCIRepositoryVerification
</a> </a>
</em> </em>
</td> </td>
@ -3096,8 +3096,8 @@ The secret must be of type kubernetes.io/dockerconfigjson.</p>
<td> <td>
<code>verify</code><br> <code>verify</code><br>
<em> <em>
<a href="#source.toolkit.fluxcd.io/v1beta2.OCIRepositoryVerification"> <a href="https://pkg.go.dev/github.com/fluxcd/source-controller/api/v1#OCIRepositoryVerification">
OCIRepositoryVerification github.com/fluxcd/source-controller/api/v1.OCIRepositoryVerification
</a> </a>
</em> </em>
</td> </td>
@ -3357,119 +3357,6 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
</table> </table>
</div> </div>
</div> </div>
<h3 id="source.toolkit.fluxcd.io/v1beta2.OCIRepositoryVerification">OCIRepositoryVerification
</h3>
<p>
(<em>Appears on:</em>
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmChartSpec">HelmChartSpec</a>,
<a href="#source.toolkit.fluxcd.io/v1beta2.OCIRepositorySpec">OCIRepositorySpec</a>)
</p>
<p>OCIRepositoryVerification verifies the authenticity of an OCI Artifact</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>provider</code><br>
<em>
string
</em>
</td>
<td>
<p>Provider specifies the technology used to sign the OCI Artifact.</p>
</td>
</tr>
<tr>
<td>
<code>secretRef</code><br>
<em>
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>SecretRef specifies the Kubernetes Secret containing the
trusted public keys.</p>
</td>
</tr>
<tr>
<td>
<code>matchOIDCIdentity</code><br>
<em>
<a href="#source.toolkit.fluxcd.io/v1beta2.OIDCIdentityMatch">
[]OIDCIdentityMatch
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>MatchOIDCIdentity specifies the identity matching criteria to use
while verifying an OCI artifact which was signed using Cosign keyless
signing. The artifact&rsquo;s identity is deemed to be verified if any of the
specified matchers match against the identity.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 id="source.toolkit.fluxcd.io/v1beta2.OIDCIdentityMatch">OIDCIdentityMatch
</h3>
<p>
(<em>Appears on:</em>
<a href="#source.toolkit.fluxcd.io/v1beta2.OCIRepositoryVerification">OCIRepositoryVerification</a>)
</p>
<p>OIDCIdentityMatch specifies options for verifying the certificate identity,
i.e. the issuer and the subject of the certificate.</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>issuer</code><br>
<em>
string
</em>
</td>
<td>
<p>Issuer specifies the regex pattern to match against to verify
the OIDC issuer in the Fulcio certificate. The pattern must be a
valid Go regular expression.</p>
</td>
</tr>
<tr>
<td>
<code>subject</code><br>
<em>
string
</em>
</td>
<td>
<p>Subject specifies the regex pattern to match against to verify
the identity subject in the Fulcio certificate. The pattern must
be a valid Go regular expression.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 id="source.toolkit.fluxcd.io/v1beta2.Source">Source <h3 id="source.toolkit.fluxcd.io/v1beta2.Source">Source
</h3> </h3>
<p>Source interface must be supported by all API types. <p>Source interface must be supported by all API types.

View File

@ -6,6 +6,8 @@ This is the v1 API specification for defining the desired state sources of Kuber
* Source kinds: * Source kinds:
+ [GitRepository](gitrepositories.md) + [GitRepository](gitrepositories.md)
+ [HelmRepository](helmrepositories.md)
+ [HelmChart](helmcharts.md)
## Implementation ## Implementation

865
docs/spec/v1/helmcharts.md Normal file
View File

@ -0,0 +1,865 @@
# Helm Charts
<!-- menuweight:50 -->
The `HelmChart` API defines a Source to produce an Artifact for a Helm chart
archive with a set of specific configurations.
## Example
The following is an example of a HelmChart. It fetches and/or packages a Helm
chart and exposes it as a tarball (`.tgz`) Artifact for the specified
configuration:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: podinfo
namespace: default
spec:
interval: 5m0s
chart: podinfo
reconcileStrategy: ChartVersion
sourceRef:
kind: HelmRepository
name: podinfo
version: '5.*'
```
In the above example:
- A HelmChart named `podinfo` is created, indicated by the `.metadata.name`
field.
- The source-controller fetches the Helm chart every five minutes from the
`podinfo` HelmRepository source reference, indicated by the
`.spec.sourceRef.kind` and `.spec.sourceRef.name` fields.
- The fetched Helm chart version is the latest available chart
version in the range specified in `spec.version`. This version is also used as
Artifact revision, reported in-cluster in the `.status.artifact.revision`
field.
- When the current Helm Chart version differs from the latest available chart
in the version range, it is fetched and/or packaged as a new Artifact.
- The new Artifact is reported in the `.status.artifact` field.
You can run this example by saving the manifest into `helmchart.yaml`.
**Note:** HelmChart is usually used by the helm-controller. Based on the
HelmRelease configuration, an associated HelmChart is created by the
helm-controller.
1. Apply the resource on the cluster:
```sh
kubectl apply -f helmchart.yaml
```
2. Run `kubectl get helmchart` to see the HelmChart:
```console
NAME CHART VERSION SOURCE KIND SOURCE NAME AGE READY STATUS
podinfo podinfo 5.* HelmRepository podinfo 53s True pulled 'podinfo' chart with version '5.2.1'
```
3. Run `kubectl describe helmchart podinfo` to see the [Artifact](#artifact) and
[Conditions](#conditions) in the HelmChart's Status:
```console
Status:
Observed Source Artifact Revision: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
Artifact:
Digest: sha256:6c3cc3b955bce1686036ae6822ee2ca0ef6ecb994e3f2d19eaf3ec03dcba84b3
Last Update Time: 2022-02-13T11:24:10Z
Path: helmchart/default/podinfo/podinfo-5.2.1.tgz
Revision: 5.2.1
Size: 14166
URL: http://source-controller.flux-system.svc.cluster.local./helmchart/default/podinfo/podinfo-5.2.1.tgz
Conditions:
Last Transition Time: 2022-02-13T11:24:10Z
Message: pulled 'podinfo' chart with version '5.2.1'
Observed Generation: 1
Reason: ChartPullSucceeded
Status: True
Type: Ready
Last Transition Time: 2022-02-13T11:24:10Z
Message: pulled 'podinfo' chart with version '5.2.1'
Observed Generation: 1
Reason: ChartPullSucceeded
Status: True
Type: ArtifactInStorage
Observed Chart Name: podinfo
Observed Generation: 1
URL: http://source-controller.flux-system.svc.cluster.local./helmchart/default/podinfo/latest.tar.gz
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ChartPullSucceeded 2m51s source-controller pulled 'podinfo' chart with version '5.2.1'
```
## Writing a HelmChart spec
As with all other Kubernetes config, a HelmChart needs `apiVersion`, `kind`, and
`metadata` fields. The name of a HelmChart object must be a valid
[DNS subdomain name](https://kubernetes.io/docs/concepts/overview/working-with-objects/names#dns-subdomain-names).
A HelmChart also needs a
[`.spec` section](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status).
### Source reference
`.spec.sourceRef` is a required field that specifies a reference to the Source
the chart is available at.
Supported references are:
- [`HelmRepository`](helmrepositories.md)
- [`GitRepository`](gitrepositories.md)
- [`Bucket`](buckets.md)
Although there are three kinds of source references, there are only two
underlying implementations. The artifact building process for `GitRepository`
and `Bucket` are the same as they are already built source artifacts. In case
of `HelmRepository`, a chart is fetched and/or packaged based on the
configuration of the Helm chart.
For a `HelmChart` to be reconciled, the associated artifact in the source
reference must be ready. If the source artifact is not ready, the `HelmChart`
reconciliation is retried.
When the `metadata.generation` of the `HelmChart` don't match with the
`status.observedGeneration`, the chart is fetched from source and/or packaged.
If there's no `.spec.valuesFiles` specified, the chart is only fetched from the
source, and not packaged. If `.spec.valuesFiles` are specified, the chart is
fetched and packaged with the values files. When the `metadata.generation`
matches the `status.observedGeneration`, the chart is only fetched from source
or from the cache if available, and not packaged.
When using a `HelmRepository` source reference, the secret reference defined in
the Helm repository is used to fetch the chart.
The HelmChart reconciliation behavior varies depending on the source reference
kind, see [reconcile strategy](#reconcile-strategy).
The attributes of the generated artifact also varies depending on the source
reference kind, see [artifact](#artifact).
### Chart
`.spec.chart` is a required field that specifies the name or path the Helm chart
is available at in the [Source reference](#source-reference).
For `HelmRepository` Source reference, it'll be just the name of the chart.
```yaml
spec:
chart: podinfo
sourceRef:
name: podinfo
kind: HelmRepository
```
For `GitRepository` and `Bucket` Source reference, it'll be the path to the
Helm chart directory.
```yaml
spec:
chart: ./charts/podinfo
sourceRef:
name: podinfo
kind: <GitRepository|Bucket>
```
### Version
`.spec.version` is an optional field to specify the version of the chart in
semver. It is applicable only when the Source reference is a `HelmRepository`.
It is ignored for `GitRepository` and `Bucket` Source reference. It defaults to
the latest version of the chart with value `*`.
Version can be a fixed semver, minor or patch semver range of a specific
version (i.e. `4.0.x`) or any semver range (i.e. `>=4.0.0 <5.0.0`).
### Values files
`.spec.valuesFiles` is an optional field to specify an alternative list of
values files to use as the chart values (values.yaml). The file paths are
expected to be relative to the Source reference. Values files are merged in the
order of the list with the last file overriding the first. It is ignored when
omitted. When values files are specified, the chart is fetched and packaged
with the provided values.
```yaml
spec:
chart:
spec:
chart: podinfo
...
valuesFiles:
- values.yaml
- values-production.yaml
```
Values files also affect the generated artifact revision, see
[artifact](#artifact).
### Ignore missing values files
`.spec.ignoreMissingValuesFiles` is an optional field to specify whether missing
values files should be ignored rather than be considered errors. It defaults to
`false`.
When `.spec.valuesFiles` and `.spec.ignoreMissingValuesFiles` are specified,
the `.status.observedValuesFiles` field is populated with the list of values
files that were found and actually contributed to the packaged chart.
### Reconcile strategy
`.spec.reconcileStrategy` is an optional field to specify what enables the
creation of a new Artifact. Valid values are `ChartVersion` and `Revision`.
`ChartVersion` is used for creating a new artifact when the chart version
changes in a `HelmRepository`. `Revision` is used for creating a new artifact
when the source revision changes in a `GitRepository` or a `Bucket` Source. It
defaults to `ChartVersion`.
**Note:** If the reconcile strategy is `ChartVersion` and the source reference
is a `GitRepository` or a `Bucket`, no new chart artifact is produced on updates
to the source unless the `version` in `Chart.yaml` is incremented. To produce
new chart artifact on change in source revision, set the reconcile strategy to
`Revision`.
Reconcile strategy also affects the artifact version, see [artifact](#artifact)
for more details.
### Interval
`.spec.interval` is a required field that specifies the interval at which the
Helm Chart source must be checked for updates.
After successfully reconciling a HelmChart object, the source-controller
requeues the object for inspection after the specified interval. The value must
be in a [Go recognized duration string format](https://pkg.go.dev/time#ParseDuration),
e.g. `10m0s` to look at the source for updates every 10 minutes.
If the `.metadata.generation` of a resource changes (due to e.g. applying a
change to the spec), this is handled instantly outside the interval window.
**Note:** The controller can be configured to apply a jitter to the interval in
order to distribute the load more evenly when multiple HelmChart objects are set
up with the same interval. For more information, please refer to the
[source-controller configuration options](https://fluxcd.io/flux/components/source/options/).
### Suspend
`.spec.suspend` is an optional field to suspend the reconciliation of a
HelmChart. When set to `true`, the controller will stop reconciling the
HelmChart, and changes to the resource or the Helm chart Source will not result
in a new Artifact. When the field is set to `false` or removed, it will resume.
For practical information, see
[suspending and resuming](#suspending-and-resuming).
### Verification
**Note:** This feature is available only for Helm charts fetched from an OCI Registry.
`.spec.verify` is an optional field to enable the verification of [Cosign](https://github.com/sigstore/cosign) or [Notation](https://github.com/notaryproject/notation)
signatures. The field offers three subfields:
- `.provider`, to specify the verification provider. The supported options are `cosign` and `notation` at present.
- `.secretRef.name`, to specify a reference to a Secret in the same namespace as
the HelmChart, containing the public keys of trusted authors. For Notation this Secret should also include the [trust policy](https://github.com/notaryproject/specifications/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-policy) in
addition to the CA certificate.
- `.matchOIDCIdentity`, to specify a list of OIDC identity matchers (only supported when using `cosign` as the verification provider). Please see
[Keyless verification](#keyless-verification) for more details.
#### Cosign
The `cosign` provider can be used to verify the signature of an OCI artifact using either a known public key or via the [Cosign Keyless](https://github.com/sigstore/cosign/blob/main/KEYLESS.md) procedure.
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: podinfo
spec:
verify:
provider: cosign
secretRef:
name: cosign-public-keys
```
When the verification succeeds, the controller adds a Condition with the
following attributes to the HelmChart's `.status.conditions`:
- `type: SourceVerified`
- `status: "True"`
- `reason: Succeeded`
##### Public keys verification
To verify the authenticity of HelmChart hosted in an OCI Registry, create a Kubernetes
secret with the Cosign public keys:
```yaml
---
apiVersion: v1
kind: Secret
metadata:
name: cosign-public-keys
type: Opaque
data:
key1.pub: <BASE64>
key2.pub: <BASE64>
```
Note that the keys must have the `.pub` extension for Flux to make use of them.
Flux will loop over the public keys and use them to verify a HelmChart's signature.
This allows for older HelmCharts to be valid as long as the right key is in the secret.
##### Keyless verification
For publicly available HelmCharts, which are signed using the
[Cosign Keyless](https://github.com/sigstore/cosign/blob/main/KEYLESS.md) procedure,
you can enable the verification by omitting the `.verify.secretRef` field.
To verify the identity's subject and the OIDC issuer present in the Fulcio
certificate, you can specify a list of OIDC identity matchers using
`.spec.verify.matchOIDCIdentity`. The matcher provides two required fields:
- `.issuer`, to specify a regexp that matches against the OIDC issuer.
- `.subject`, to specify a regexp that matches against the subject identity in
the certificate.
Both values should follow the [Go regular expression syntax](https://golang.org/s/re2syntax).
The matchers are evaluated in an OR fashion, i.e. the identity is deemed to be
verified if any one matcher successfully matches against the identity.
Example of verifying HelmCharts signed by the
[Cosign GitHub Action](https://github.com/sigstore/cosign-installer) with GitHub OIDC Token:
```yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: podinfo
spec:
interval: 5m
chart: podinfo
reconcileStrategy: ChartVersion
sourceRef:
kind: HelmRepository
name: podinfo
version: ">=6.1.6"
verify:
provider: cosign
matchOIDCIdentity:
- issuer: "^https://token.actions.githubusercontent.com$"
subject: "^https://github.com/stefanprodan/podinfo.*$"
```
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: podinfo
spec:
interval: 1m0s
url: oci://ghcr.io/stefanprodan/charts
type: "oci"
```
The controller verifies the signatures using the Fulcio root CA and the Rekor
instance hosted at [rekor.sigstore.dev](https://rekor.sigstore.dev/).
Note that keyless verification is an **experimental feature**, using
custom root CAs or self-hosted Rekor instances are not currently supported.
#### Notation
The `notation` provider can be used to verify the signature of an OCI artifact using known
trust policy and CA certificate.
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: podinfo
spec:
verify:
provider: notation
secretRef:
name: notation-config
```
When the verification succeeds, the controller adds a Condition with the
following attributes to the HelmChart's `.status.conditions`:
- `type: SourceVerified`
- `status: "True"`
- `reason: Succeeded`
To verify the authenticity of an OCI artifact, create a Kubernetes secret
containing Certificate Authority (CA) root certificates and the a `trust policy`
```yaml
---
apiVersion: v1
kind: Secret
metadata:
name: notation-config
type: Opaque
data:
certificate1.pem: <BASE64>
certificate2.crt: <BASE64>
trustpolicy.json: <BASE64>
```
Note that the CA certificates must have either `.pem` or `.crt` extension and your trust policy must
be named `trustpolicy.json` for Flux to make use of them.
For more information on the signing and verification process see [Signing and Verification Workflow](https://github.com/notaryproject/specifications/blob/v1.0.0/specs/signing-and-verification-workflow.md).
Flux will loop over the certificates and use them to verify an artifact's signature.
This allows for older artifacts to be valid as long as the right certificate is in the secret.
## Working with HelmCharts
### Triggering a reconcile
To manually tell the source-controller to reconcile a HelmChart outside the
[specified interval window](#interval), a HelmCHart can be annotated with
`reconcile.fluxcd.io/requestedAt: <arbitrary value>`. Annotating the resource
queues the object for reconciliation if the `<arbitrary-value>` differs from
the last value the controller acted on, as reported in
[`.status.lastHandledReconcileAt`](#last-handled-reconcile-at).
Using `kubectl`:
```sh
kubectl annotate --field-manager=flux-client-side-apply --overwrite helmchart/<chart-name> reconcile.fluxcd.io/requestedAt="$(date +%s)"
```
### Waiting for `Ready`
When a change is applied, it is possible to wait for the HelmChart to reach a
[ready state](#ready-helmchart) using `kubectl`:
```sh
kubectl wait helmchart/<chart-name> --for=condition=ready --timeout=1m
```
### Suspending and resuming
When you find yourself in a situation where you temporarily want to pause the
reconciliation of a HelmChart, you can suspend it using the
[`.spec.suspend` field](#suspend).
#### Suspend a HelmChart
In your YAML declaration:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: <chart-name>
spec:
suspend: true
```
Using `kubectl`:
```sh
kubectl patch helmchart <chart-name> --field-manager=flux-client-side-apply -p '{\"spec\": {\"suspend\" : true }}'
```
**Note:** When a HelmChart has an Artifact and is suspended, and this
Artifact later disappears from the storage due to e.g. the source-controller
Pod being evicted from a Node, this will not be reflected in the
HelmChart's Status until it is resumed.
#### Resume a HelmChart
In your YAML declaration, comment out (or remove) the field:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: <chart-name>
spec:
# suspend: true
```
**Note:** Setting the field value to `false` has the same effect as removing
it, but does not allow for "hot patching" using e.g. `kubectl` while practicing
GitOps; as the manually applied patch would be overwritten by the declared
state in Git.
Using `kubectl`:
```sh
kubectl patch helmchart <chart-name> --field-manager=flux-client-side-apply -p '{\"spec\" : {\"suspend\" : false }}'
```
### Debugging a HelmChart
There are several ways to gather information about a HelmChart for debugging
purposes.
#### Describe the HelmChart
Describing a HelmChart using `kubectl describe helmchart <chart-name>` displays
the latest recorded information for the resource in the `Status` and `Events`
sections:
```console
...
Status:
...
Conditions:
Last Transition Time: 2022-02-13T14:06:27Z
Message: invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with version matching '9.*' found
Observed Generation: 3
Reason: InvalidChartReference
Status: True
Type: Stalled
Last Transition Time: 2022-02-13T14:06:27Z
Message: invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with version matching '9.*' found
Observed Generation: 3
Reason: InvalidChartReference
Status: False
Type: Ready
Last Transition Time: 2022-02-13T14:06:27Z
Message: invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with version matching '9.*' found
Observed Generation: 3
Reason: InvalidChartReference
Status: True
Type: FetchFailed
Last Handled Reconcile At: 1644759954
Observed Chart Name: podinfo
Observed Generation: 3
URL: http://source-controller.flux-system.svc.cluster.local./helmchart/default/podinfo/latest.tar.gz
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning InvalidChartReference 11s source-controller invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with ver
sion matching '9.*' found
```
#### Trace emitted Events
To view events for specific HelmChart(s), `kubectl events` can be used in
combination with `--for` to list the Events for specific objects. For example,
running
```sh
kubectl events --for HelmChart/<chart-name>
```
lists
```console
LAST SEEN TYPE REASON OBJECT MESSAGE
22s Warning InvalidChartReference helmchart/<chart-name> invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with version matching '9.*' found
2s Normal ChartPullSucceeded helmchart/<chart-name> pulled 'podinfo' chart with version '6.0.3'
2s Normal ArtifactUpToDate helmchart/<chart-name> artifact up-to-date with remote revision: '6.0.3'
```
Besides being reported in Events, the reconciliation errors are also logged by
the controller. The Flux CLI offer commands for filtering the logs for a
specific HelmChart, e.g. `flux logs --level=error --kind=HelmChart --name=<chart-name>`.
### Improving resource consumption by enabling the cache
When using a `HelmRepository` as Source for a `HelmChart`, the controller loads
the repository index in memory to find the latest version of the chart.
The controller can be configured to cache Helm repository indexes in memory.
The cache is used to avoid loading repository indexes for every `HelmChart`
reconciliation.
The following flags are provided to enable and configure the cache:
- `helm-cache-max-size`: The maximum size of the cache in number of indexes.
If `0`, then the cache is disabled.
- `helm-cache-ttl`: The TTL of an index in the cache.
- `helm-cache-purge-interval`: The interval at which the cache is purged of
expired items.
The caching strategy is to pull a repository index from the cache if it is
available, otherwise to load the index, retrieve and build the chart,
then cache the index. The cached index TTL is refreshed every time the
Helm repository index is loaded with the `helm-cache-ttl` value.
The cache is purged of expired items every `helm-cache-purge-interval`.
When the cache is full, no more items can be added to the cache, and the
source-controller will report a warning event instead.
In order to use the cache, set the related flags in the source-controller
Deployment config:
```yaml
spec:
containers:
- args:
- --watch-all-namespaces
- --log-level=info
- --log-encoding=json
- --enable-leader-election
- --storage-path=/data
- --storage-adv-addr=source-controller.$(RUNTIME_NAMESPACE).svc.cluster.local.
## Helm cache with up to 10 items, i.e. 10 indexes.
- --helm-cache-max-size=10
## TTL of an index is 1 hour.
- --helm-cache-ttl=1h
## Purge expired index every 10 minutes.
- --helm-cache-purge-interval=10m
```
## HelmChart Status
### Artifact
The HelmChart reports the last built chart as an Artifact object in the
`.status.artifact` of the resource.
The Artifact file is a gzip compressed TAR archive (`<chart-name>-<chart-version>.tgz`),
and can be retrieved in-cluster from the `.status.artifact.url` HTTP address.
#### Artifact example
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: <chart-name>
status:
artifact:
digest: sha256:e30b95a08787de69ffdad3c232d65cfb131b5b50c6fd44295f48a078fceaa44e
lastUpdateTime: "2022-02-10T18:53:47Z"
path: helmchart/<source-namespace>/<chart-name>/<chart-name>-<chart-version>.tgz
revision: 6.0.3
size: 14166
url: http://source-controller.flux-system.svc.cluster.local./helmchart/<source-namespace>/<chart-name>/<chart-name>-<chart-version>.tgz
```
When using a `HelmRepository` as the source reference and values files are
provided, the value of `status.artifact.revision` is the chart version combined
with the `HelmChart` object generation. For example, if the chart version is
`6.0.3` and the `HelmChart` object generation is `1`, the
`status.artifact.revision` value will be `6.0.3+1`.
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: <chart-name>
status:
artifact:
digest: sha256:ee68224ded207ebb18a8e9730cf3313fa6bc1f31e6d8d3943ab541113559bb52
lastUpdateTime: "2022-02-28T08:07:12Z"
path: helmchart/<source-namespace>/<chart-name>/<chart-name>-6.0.3+1.tgz
revision: 6.0.3+1
size: 14166
url: http://source-controller.flux-system.svc.cluster.local./helmchart/<source-namespace>/<chart-name>/<chart-name>-6.0.3+1.tgz
observedGeneration: 1
...
```
When using a `GitRepository` or a `Bucket` as the source reference and
`Revision` as the reconcile strategy, the value of `status.artifact.revision` is
the chart version combined with the first 12 characters of the revision of the
`GitRepository` or `Bucket`. For example if the chart version is `6.0.3` and the
revision of the `Bucket` is `4e5cbb7b97d00a8039b8810b90b922f4256fd3bd8f78b934b4892dae13f7ca87`,
the `status.artifact.revision` value will be `6.0.3+4e5cbb7b97d0`.
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmChart
metadata:
name: <chart-name>
status:
artifact:
digest: sha256:8d1f0ac3f4b0e8759a32180086f17ac87ca04e5d46c356e67f97e97616ef4718
lastUpdateTime: "2022-02-28T08:07:12Z"
path: helmchart/<source-namespace>/<chart-name>/<chart-name>-6.0.3+4e5cbb7b97d0.tgz
revision: 6.0.3+4e5cbb7b97d0
size: 14166
url: http://source-controller.flux-system.svc.cluster.local./helmchart/<source-namespace>/<chart-name>/<chart-name>-6.0.3+4e5cbb7b97d0.tgz
```
### Conditions
A HelmChart enters various states during its lifecycle, reflected as [Kubernetes
Conditions][typical-status-properties].
It can be [reconciling](#reconciling-helmchart) while fetching or building the
chart, it can be [ready](#ready-helmchart), it can
[fail during reconciliation](#failed-helmchart), or it can
[stall](#stalled-helmchart).
The HelmChart API is compatible with the [kstatus
specification][kstatus-spec],
and reports `Reconciling` and `Stalled` conditions where applicable to
provide better (timeout) support to solutions polling the HelmChart to become
`Ready`.
#### Reconciling HelmChart
The source-controller marks a HelmChart as _reconciling_ when one of the
following is true:
- There is no current Artifact for the HelmChart, or the reported Artifact is
determined to have disappeared from the storage.
- The generation of the HelmChart is newer than the [Observed
Generation](#observed-generation).
- The newly fetched Artifact revision differs from the current Artifact.
When the HelmChart is "reconciling", the `Ready` Condition status becomes
`Unknown` when the controller detects drift, and the controller adds a Condition
with the following attributes to the HelmChart's `.status.conditions`:
- `type: Reconciling`
- `status: "True"`
- `reason: Progressing` | `reason: ProgressingWithRetry`
If the reconciling state is due to a new version, it adds an additional
Condition with the following attributes:
- `type: ArtifactOutdated`
- `status: "True"`
- `reason: NewChart`
Both Conditions have a ["negative polarity"][typical-status-properties],
and are only present on the HelmChart while their status value is `"True"`.
#### Ready HelmChart
The source-controller marks a HelmChart as _ready_ when it has the following
characteristics:
- The HelmChart reports an [Artifact](#artifact).
- The reported Artifact exists in the controller's Artifact storage.
- The controller was able to fetch and build the Helm chart using the current
spec.
- The version/revision of the reported Artifact is up-to-date with the
latest version/revision of the Helm chart.
When the HelmChart is "ready", the controller sets a Condition with the
following attributes in the HelmChart's `.status.conditions`:
- `type: Ready`
- `status: "True"`
- `reason: Succeeded`
This `Ready` Condition will retain a status value of `"True"` until the
HelmChart is marked as [reconciling](#reconciling-helmchart), or e.g.
a [transient error](#failed-helmchart) occurs due to a temporary network issue.
When the HelmChart Artifact is archived in the controller's Artifact
storage, the controller sets a Condition with the following attributes in the
HelmChart's `.status.conditions`:
- `type: ArtifactInStorage`
- `status: "True"`
- `reason: Succeeded`
This `ArtifactInStorage` Condition will retain a status value of `"True"` until
the Artifact in the storage no longer exists.
#### Failed HelmChart
The source-controller may get stuck trying to produce an Artifact for a
HelmChart without completing. This can occur due to some of the following
factors:
- The Helm chart Source is temporarily unavailable.
- The credentials in the [Source reference](#source-reference) Secret are
invalid.
- The HelmChart spec contains a generic misconfiguration.
- A storage related failure when storing the artifact.
When this happens, the controller sets the `Ready` Condition status to `False`,
and adds a Condition with the following attributes to the HelmChart's
`.status.conditions`:
- `type: FetchFailed` | `type: StorageOperationFailed`
- `status: "True"`
- `reason: AuthenticationFailed` | `reason: StorageOperationFailed` | `reason: URLInvalid` | `reason: IllegalPath` | `reason: Failed`
This condition has a ["negative polarity"][typical-status-properties],
and is only present on the HelmChart while the status value is `"True"`.
There may be more arbitrary values for the `reason` field to provide accurate
reason for a condition.
While the HelmChart has this Condition, the controller will continue to
attempt to produce an Artifact for the resource with an exponential backoff,
until it succeeds and the HelmChart is marked as [ready](#ready-helmchart).
Note that a HelmChart can be [reconciling](#reconciling-helmchart)
while failing at the same time, for example due to a newly introduced
configuration issue in the HelmChart spec. When a reconciliation fails, the
`Reconciling` Condition reason would be `ProgressingWithRetry`. When the
reconciliation is performed again after the failure, the reason is updated to
`Progressing`.
#### Stalled HelmChart
The source-controller can mark a HelmChart as _stalled_ when it determines that
without changes to the spec, the reconciliation can not succeed.
For example because a HelmChart Version is set to a non-existing version.
When this happens, the controller sets the same Conditions as when it
[fails](#failed-helmchart), but adds another Condition with the following
attributes to the HelmChart's `.status.conditions`:
- `type: Stalled`
- `status: "True"`
- `reason: InvalidChartReference`
While the HelmChart has this Condition, the controller will not requeue the
resource any further, and will stop reconciling the resource until a change to
the spec is made.
### Observed Source Artifact Revision
The source-controller reports the revision of the last
[Source reference's](#source-reference) Artifact the current chart was fetched
from in the HelmChart's `.status.observedSourceArtifactRevision`. It is used to
keep track of the source artifact revision and detect when a new source
artifact is available.
### Observed Chart Name
The source-controller reports the last resolved chart name of the Artifact
for the [`.spec.chart` field](#chart) in the HelmChart's
`.status.observedChartName`. It is used to keep track of the chart and detect
when a new chart is found.
### Observed Generation
The source-controller reports an [observed generation][typical-status-properties]
in the HelmChart's `.status.observedGeneration`. The observed generation is the
latest `.metadata.generation` which resulted in either a [ready state](#ready-helmchart),
or stalled due to error it can not recover from without human
intervention.
### Last Handled Reconcile At
The source-controller reports the last `reconcile.fluxcd.io/requestedAt`
annotation value it acted on in the `.status.lastHandledReconcileAt` field.
For practical information about this field, see [triggering a
reconcile](#triggering-a-reconcile).
[typical-status-properties]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
[kstatus-spec]: https://github.com/kubernetes-sigs/cli-utils/tree/master/pkg/kstatus

View File

@ -0,0 +1,878 @@
# Helm Repositories
<!-- menuweight:40 -->
There are 2 [Helm repository types](#type) defined by the `HelmRepository` API:
- Helm HTTP/S repository, which defines a Source to produce an Artifact for a Helm
repository index YAML (`index.yaml`).
- OCI Helm repository, which defines a source that does not produce an Artifact.
It's a data container to store the information about the OCI repository that
can be used by [HelmChart](helmcharts.md) to access OCI Helm charts.
## Examples
### Helm HTTP/S repository
The following is an example of a HelmRepository. It creates a YAML (`.yaml`)
Artifact from the fetched Helm repository index (in this example the [podinfo
repository](https://github.com/stefanprodan/podinfo)):
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: podinfo
namespace: default
spec:
interval: 5m0s
url: https://stefanprodan.github.io/podinfo
```
In the above example:
- A HelmRepository named `podinfo` is created, indicated by the
`.metadata.name` field.
- The source-controller fetches the Helm repository index YAML every five
minutes from `https://stefanprodan.github.io/podinfo`, indicated by the
`.spec.interval` and `.spec.url` fields.
- The digest (algorithm defaults to SHA256) of the Helm repository index after
stable sorting the entries is used as Artifact revision, reported in-cluster
in the `.status.artifact.revision` field.
- When the current HelmRepository revision differs from the latest fetched
revision, it is stored as a new Artifact.
- The new Artifact is reported in the `.status.artifact` field.
You can run this example by saving the manifest into `helmrepository.yaml`.
1. Apply the resource on the cluster:
```sh
kubectl apply -f helmrepository.yaml
```
2. Run `kubectl get helmrepository` to see the HelmRepository:
```console
NAME URL AGE READY STATUS
podinfo https://stefanprodan.github.io/podinfo 4s True stored artifact for revision 'sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
```
3. Run `kubectl describe helmrepository podinfo` to see the [Artifact](#artifact)
and [Conditions](#conditions) in the HelmRepository's Status:
```console
...
Status:
Artifact:
Digest: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
Last Update Time: 2022-02-04T09:55:58Z
Path: helmrepository/default/podinfo/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
Revision: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
Size: 40898
URL: http://source-controller.flux-system.svc.cluster.local./helmrepository/default/podinfo/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
Conditions:
Last Transition Time: 2022-02-04T09:55:58Z
Message: stored artifact for revision 'sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
Observed Generation: 1
Reason: Succeeded
Status: True
Type: Ready
Last Transition Time: 2022-02-04T09:55:58Z
Message: stored artifact for revision 'sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
Observed Generation: 1
Reason: Succeeded
Status: True
Type: ArtifactInStorage
Observed Generation: 1
URL: http://source-controller.flux-system.svc.cluster.local./helmrepository/default/podinfo/index.yaml
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal NewArtifact 1m source-controller fetched index of size 30.88kB from 'https://stefanprodan.github.io/podinfo'
```
### Helm OCI repository
The following is an example of an OCI HelmRepository.
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: podinfo
namespace: default
spec:
type: "oci"
interval: 5m0s
url: oci://ghcr.io/stefanprodan/charts
```
In the above example:
- A HelmRepository named `podinfo` is created, indicated by the
`.metadata.name` field.
- A HelmChart that refers to this HelmRepository uses the URL in the `.spec.url`
field to access the OCI Helm chart.
**NOTE:** The `.spec.interval` field is only used by the `default` Helm
repository and is ignored for any value in `oci` Helm repository.
You can run this example by saving the manifest into `helmrepository.yaml`.
1. Apply the resource on the cluster:
```sh
kubectl apply -f helmrepository.yaml
```
2. Run `kubectl get helmrepository` to see the HelmRepository:
```console
NAME URL AGE READY STATUS
podinfo oci://ghcr.io/stefanprodan/charts 3m22s
```
Because the OCI Helm repository is a data container, there's nothing to report
for `READY` and `STATUS` columns above. The existence of the object can be
considered to be ready for use.
## Writing a HelmRepository spec
As with all other Kubernetes config, a HelmRepository needs `apiVersion`,
`kind`, and `metadata` fields. The name of a HelmRepository object must be a
valid [DNS subdomain name](https://kubernetes.io/docs/concepts/overview/working-with-objects/names#dns-subdomain-names).
A HelmRepository also needs a
[`.spec` section](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status).
### Type
`.spec.type` is an optional field that specifies the Helm repository type.
Possible values are `default` for a Helm HTTP/S repository, or `oci` for an OCI Helm repository.
### Provider
`.spec.provider` is an optional field that allows specifying an OIDC provider used
for authentication purposes.
Supported options are:
- `generic`
- `aws`
- `azure`
- `gcp`
The `generic` provider can be used for public repositories or when static credentials
are used for authentication. If you do not specify `.spec.provider`, it defaults
to `generic`.
**Note**: The provider field is supported only for Helm OCI repositories. The `spec.type`
field must be set to `oci`.
#### AWS
The `aws` provider can be used to authenticate automatically using the EKS worker
node IAM role or IAM Role for Service Accounts (IRSA), and by extension gain access
to ECR.
##### EKS Worker Node IAM Role
When the worker node IAM role has access to ECR, source-controller running on it
will also have access to ECR.
##### IAM Role for Service Accounts (IRSA)
When using IRSA to enable access to ECR, add the following patch to your bootstrap
repository, in the `flux-system/kustomization.yaml` file:
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- patch: |
apiVersion: v1
kind: ServiceAccount
metadata:
name: source-controller
annotations:
eks.amazonaws.com/role-arn: <role arn>
target:
kind: ServiceAccount
name: source-controller
```
Note that you can attach the AWS managed policy `arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly`
to the IAM role when using IRSA.
#### Azure
The `azure` provider can be used to authenticate automatically using Workload Identity and Kubelet Managed
Identity to gain access to ACR.
##### Kubelet Managed Identity
When the kubelet managed identity has access to ACR, source-controller running on
it will also have access to ACR.
**Note:** If you have more than one identity configured on the cluster, you have to specify which one to use
by setting the `AZURE_CLIENT_ID` environment variable in the source-controller deployment.
If you are running into further issues, please look at the
[troubleshooting guide](https://github.com/Azure/azure-sdk-for-go/blob/main/sdk/azidentity/TROUBLESHOOTING.md#azure-virtual-machine-managed-identity).
##### Azure Workload Identity
When using Workload Identity to enable access to ACR, add the following patch to
your bootstrap repository, in the `flux-system/kustomization.yaml` file:
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- patch: |-
apiVersion: v1
kind: ServiceAccount
metadata:
name: source-controller
namespace: flux-system
annotations:
azure.workload.identity/client-id: <AZURE_CLIENT_ID>
labels:
azure.workload.identity/use: "true"
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: source-controller
namespace: flux-system
labels:
azure.workload.identity/use: "true"
spec:
template:
metadata:
labels:
azure.workload.identity/use: "true"
```
Ensure Workload Identity is properly set up on your cluster and the mutating webhook is installed.
Create an identity that has access to ACR. Next, establish
a federated identity between the source-controller ServiceAccount and the
identity. Patch the source-controller Deployment and ServiceAccount as shown in the patch
above. Please take a look at this [guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#6-establish-federated-identity-credential-between-the-identity-and-the-service-account-issuer--subject).
#### GCP
The `gcp` provider can be used to authenticate automatically using OAuth scopes or
Workload Identity, and by extension gain access to GCR or Artifact Registry.
##### Access Scopes
When the GKE nodes have the appropriate OAuth scope for accessing GCR and Artifact Registry,
source-controller running on it will also have access to them.
##### GKE Workload Identity
When using Workload Identity to enable access to GCR or Artifact Registry, add the
following patch to your bootstrap repository, in the `flux-system/kustomization.yaml`
file:
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- patch: |
apiVersion: v1
kind: ServiceAccount
metadata:
name: source-controller
annotations:
iam.gke.io/gcp-service-account: <identity-name>
target:
kind: ServiceAccount
name: source-controller
```
The Artifact Registry service uses the permission `artifactregistry.repositories.downloadArtifacts`
that is located under the Artifact Registry Reader role. If you are using Google Container Registry service,
the needed permission is instead `storage.objects.list` which can be bound as part
of the Container Registry Service Agent role. Take a look at [this guide](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
for more information about setting up GKE Workload Identity.
### Insecure
`.spec.insecure` is an optional field to allow connecting to an insecure (HTTP)
container registry server, if set to `true`. The default value is `false`,
denying insecure non-TLS connections when fetching Helm chart OCI artifacts.
**Note**: The insecure field is supported only for Helm OCI repositories.
The `spec.type` field must be set to `oci`.
### Interval
**Note:** This field is ineffectual for [OCI Helm
Repositories](#helm-oci-repository).
`.spec.interval` is a an optional field that specifies the interval which the
Helm repository index must be consulted at. When not set, the default value is
`1m`.
After successfully reconciling a HelmRepository object, the source-controller
requeues the object for inspection after the specified interval. The value
must be in a [Go recognized duration string format](https://pkg.go.dev/time#ParseDuration),
e.g. `10m0s` to fetch the HelmRepository index YAML every 10 minutes.
If the `.metadata.generation` of a resource changes (due to e.g. applying a
change to the spec), this is handled instantly outside the interval window.
**Note:** The controller can be configured to apply a jitter to the interval in
order to distribute the load more evenly when multiple HelmRepository objects
are set up with the same interval. For more information, please refer to the
[source-controller configuration options](https://fluxcd.io/flux/components/source/options/).
### URL
`.spec.url` is a required field that depending on the [type of the HelmRepository object](#type)
specifies the HTTP/S or OCI address of a Helm repository.
For OCI, the URL is expected to point to a registry repository, e.g. `oci://ghcr.io/fluxcd/source-controller`.
For Helm repositories which require authentication, see [Secret reference](#secret-reference).
### Timeout
**Note:** This field is not applicable to [OCI Helm
Repositories](#helm-oci-repository).
`.spec.timeout` is an optional field to specify a timeout for the fetch
operation. The value must be in a
[Go recognized duration string format](https://pkg.go.dev/time#ParseDuration),
e.g. `1m30s` for a timeout of one minute and thirty seconds. When not set, the
default value is `1m`.
### Secret reference
`.spec.secretRef.name` is an optional field to specify a name reference to a
Secret in the same namespace as the HelmRepository, containing authentication
credentials for the repository.
#### Basic access authentication
To authenticate towards a Helm repository using basic access authentication
(in other words: using a username and password), the referenced Secret is
expected to contain `.data.username` and `.data.password` values.
For example:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: example
namespace: default
spec:
interval: 5m0s
url: https://example.com
secretRef:
name: example-user
---
apiVersion: v1
kind: Secret
metadata:
name: example-user
namespace: default
stringData:
username: "user-123456"
password: "pass-123456"
```
OCI Helm repository example:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: podinfo
namespace: default
spec:
interval: 5m0s
url: oci://ghcr.io/my-user/my-private-repo
type: "oci"
secretRef:
name: oci-creds
---
apiVersion: v1
kind: Secret
metadata:
name: oci-creds
namespace: default
stringData:
username: "user-123456"
password: "pass-123456"
```
For OCI Helm repositories, Kubernetes secrets of type [kubernetes.io/dockerconfigjson](https://kubernetes.io/docs/concepts/configuration/secret/#secret-types) are also supported.
It is possible to create one such secret with `kubectl create secret docker-registry`
or using the Flux CLI:
```yaml
flux create secret oci ghcr-auth \
--url=ghcr.io \
--username=flux \
--password=${GITHUB_PAT}
```
**Warning:** Support for specifying TLS authentication data using this API has been
deprecated. Please use [`.spec.certSecretRef`](#cert-secret-reference) instead.
If the controller uses the secret specified by this field to configure TLS, then
a deprecation warning will be logged.
### Cert secret reference
`.spec.certSecretRef.name` is an optional field to specify a secret containing
TLS certificate data. The secret can contain the following keys:
* `tls.crt` and `tls.key`, to specify the client certificate and private key used
for TLS client authentication. These must be used in conjunction, i.e.
specifying one without the other will lead to an error.
* `ca.crt`, to specify the CA certificate used to verify the server, which is
required if the server is using a self-signed certificate.
If the server is using a self-signed certificate and has TLS client
authentication enabled, all three values are required.
The Secret should be of type `Opaque` or `kubernetes.io/tls`. All the files in
the Secret are expected to be [PEM-encoded][pem-encoding]. Assuming you have
three files; `client.key`, `client.crt` and `ca.crt` for the client private key,
client certificate and the CA certificate respectively, you can generate the
required Secret using the `flux create secret tls` command:
```sh
flux create secret tls --tls-key-file=client.key --tls-crt-file=client.crt --ca-crt-file=ca.crt
```
Example usage:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: example
namespace: default
spec:
interval: 5m0s
url: https://example.com
certSecretRef:
name: example-tls
---
apiVersion: v1
kind: Secret
metadata:
name: example-tls
namespace: default
type: kubernetes.io/tls # or Opaque
data:
tls.crt: <BASE64>
tls.key: <BASE64>
# NOTE: Can be supplied without the above values
ca.crt: <BASE64>
```
### Pass credentials
`.spec.passCredentials` is an optional field to allow the credentials from the
[Secret reference](#secret-reference) to be passed on to a host that does not
match the host as defined in URL. This may for example be required if the host
advertised chart URLs in the index differ from the specified URL.
Enabling this should be done with caution, as it can potentially result in
credentials getting stolen in a man-in-the-middle attack. This feature only applies
to HTTP/S Helm repositories.
### Suspend
**Note:** This field is not applicable to [OCI Helm
Repositories](#helm-oci-repository).
`.spec.suspend` is an optional field to suspend the reconciliation of a
HelmRepository. When set to `true`, the controller will stop reconciling the
HelmRepository, and changes to the resource or the Helm repository index will
not result in a new Artifact. When the field is set to `false` or removed, it
will resume.
For practical information, see
[suspending and resuming](#suspending-and-resuming).
## Working with HelmRepositories
**Note:** This section does not apply to [OCI Helm
Repositories](#helm-oci-repository), being a data container, once created, they
are ready to used by [HelmCharts](helmcharts.md).
### Triggering a reconcile
To manually tell the source-controller to reconcile a HelmRepository outside the
[specified interval window](#interval), a HelmRepository can be annotated with
`reconcile.fluxcd.io/requestedAt: <arbitrary value>`. Annotating the resource
queues the object for reconciliation if the `<arbitrary-value>` differs from
the last value the controller acted on, as reported in
[`.status.lastHandledReconcileAt`](#last-handled-reconcile-at).
Using `kubectl`:
```sh
kubectl annotate --field-manager=flux-client-side-apply --overwrite helmrepository/<repository-name> reconcile.fluxcd.io/requestedAt="$(date +%s)"
```
Using `flux`:
```sh
flux reconcile source helm <repository-name>
```
### Waiting for `Ready`
When a change is applied, it is possible to wait for the HelmRepository to
reach a [ready state](#ready-helmrepository) using `kubectl`:
```sh
kubectl wait helmrepository/<repository-name> --for=condition=ready --timeout=1m
```
### Suspending and resuming
When you find yourself in a situation where you temporarily want to pause the
reconciliation of a HelmRepository, you can suspend it using the
[`.spec.suspend` field](#suspend).
#### Suspend a HelmRepository
In your YAML declaration:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: <repository-name>
spec:
suspend: true
```
Using `kubectl`:
```sh
kubectl patch helmrepository <repository-name> --field-manager=flux-client-side-apply -p '{\"spec\": {\"suspend\" : true }}'
```
Using `flux`:
```sh
flux suspend source helm <repository-name>
```
**Note:** When a HelmRepository has an Artifact and is suspended, and this
Artifact later disappears from the storage due to e.g. the source-controller
Pod being evicted from a Node, this will not be reflected in the
HelmRepository's Status until it is resumed.
#### Resume a HelmRepository
In your YAML declaration, comment out (or remove) the field:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: <repository-name>
spec:
# suspend: true
```
**Note:** Setting the field value to `false` has the same effect as removing
it, but does not allow for "hot patching" using e.g. `kubectl` while practicing
GitOps; as the manually applied patch would be overwritten by the declared
state in Git.
Using `kubectl`:
```sh
kubectl patch helmrepository <repository-name> --field-manager=flux-client-side-apply -p '{\"spec\" : {\"suspend\" : false }}'
```
Using `flux`:
```sh
flux resume source helm <repository-name>
```
### Debugging a HelmRepository
**Note:** This section does not apply to [OCI Helm
Repositories](#helm-oci-repository), being a data container, they are static
objects that don't require debugging if valid.
There are several ways to gather information about a HelmRepository for debugging
purposes.
#### Describe the HelmRepository
Describing a HelmRepository using `kubectl describe helmrepository <repository-name>`
displays the latest recorded information for the resource in the `Status` and
`Events` sections:
```console
...
Status:
...
Conditions:
Last Transition Time: 2022-02-04T13:41:56Z
Message: failed to construct Helm client: scheme "invalid" not supported
Observed Generation: 2
Reason: Failed
Status: True
Type: Stalled
Last Transition Time: 2022-02-04T13:41:56Z
Message: failed to construct Helm client: scheme "invalid" not supported
Observed Generation: 2
Reason: Failed
Status: False
Type: Ready
Last Transition Time: 2022-02-04T13:41:56Z
Message: failed to construct Helm client: scheme "invalid" not supported
Observed Generation: 2
Reason: Failed
Status: True
Type: FetchFailed
Observed Generation: 2
URL: http://source-controller.source-system.svc.cluster.local./helmrepository/default/podinfo/index.yaml
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Failed 6s source-controller failed to construct Helm client: scheme "invalid" not supported
```
#### Trace emitted Events
To view events for specific HelmRepository(s), `kubectl events` can be used in
combination with `--for` to list the Events for specific objects. For example,
running
```sh
kubectl events --for HelmRepository/<repository-name>
```
lists
```console
LAST SEEN TYPE REASON OBJECT MESSAGE
107s Warning Failed helmrepository/<repository-name> failed to construct Helm client: scheme "invalid" not supported
7s Normal NewArtifact helmrepository/<repository-name> fetched index of size 30.88kB from 'https://stefanprodan.github.io/podinfo'
3s Normal ArtifactUpToDate helmrepository/<repository-name> artifact up-to-date with remote revision: 'sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
```
Besides being reported in Events, the reconciliation errors are also logged by
the controller. The Flux CLI offer commands for filtering the logs for a
specific HelmRepository, e.g. `flux logs --level=error --kind=HelmRepository --name=<chart-name>`.
## HelmRepository Status
**Note:** This section does not apply to [OCI Helm
Repositories](#helm-oci-repository), they do not contain any information in the
status.
### Artifact
The HelmRepository reports the last fetched repository index as an Artifact
object in the `.status.artifact` of the resource.
The Artifact file is an exact copy of the Helm repository index YAML
(`index-<revision>.yaml`) as fetched, and can be retrieved in-cluster from the
`.status.artifact.url` HTTP address.
#### Artifact example
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: <repository-name>
status:
artifact:
digest: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
lastUpdateTime: "2022-02-04T09:55:58Z"
path: helmrepository/<namespace>/<repository-name>/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
revision: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
size: 40898
url: http://source-controller.flux-system.svc.cluster.local./helmrepository/<namespace>/<repository-name>/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
```
### Conditions
A HelmRepository enters various states during its lifecycle, reflected as [Kubernetes
Conditions][typical-status-properties].
It can be [reconciling](#reconciling-helmrepository) while fetching the
repository index, it can be [ready](#ready-helmrepository), it can
[fail during reconciliation](#failed-helmrepository), or it can
[stall](#stalled-helmrepository).
The HelmRepository API is compatible with the [kstatus
specification][kstatus-spec],
and reports `Reconciling` and `Stalled` conditions where applicable to
provide better (timeout) support to solutions polling the HelmRepository to become
`Ready`.
#### Reconciling HelmRepository
The source-controller marks a HelmRepository as _reconciling_ when one of the following
is true:
- There is no current Artifact for the HelmRepository, or the reported Artifact
is determined to have disappeared from the storage.
- The generation of the HelmRepository is newer than the [Observed
Generation](#observed-generation).
- The newly fetched Artifact revision differs from the current Artifact.
When the HelmRepository is "reconciling", the `Ready` Condition status becomes
`Unknown` when the controller detects drift, and the controller adds a Condition
with the following attributes to the HelmRepository's `.status.conditions`:
- `type: Reconciling`
- `status: "True"`
- `reason: Progressing` | `reason: ProgressingWithRetry`
If the reconciling state is due to a new revision, it adds an additional
Condition with the following attributes:
- `type: ArtifactOutdated`
- `status: "True"`
- `reason: NewRevision`
Both Conditions have a ["negative polarity"][typical-status-properties],
and are only present on the HelmRepository while their status value is `"True"`.
#### Ready HelmRepository
The source-controller marks a HelmRepository as _ready_ when it has the following
characteristics:
- The HelmRepository reports an [Artifact](#artifact).
- The reported Artifact exists in the controller's Artifact storage.
- The controller was able to fetch the Helm repository index using the current
spec.
- The revision of the reported Artifact is up-to-date with the latest
revision of the Helm repository.
When the HelmRepository is "ready", the controller sets a Condition with the following
attributes in the HelmRepository's `.status.conditions`:
- `type: Ready`
- `status: "True"`
- `reason: Succeeded`
This `Ready` Condition will retain a status value of `"True"` until the
HelmRepository is marked as [reconciling](#reconciling-helmrepository), or e.g.
a [transient error](#failed-helmrepository) occurs due to a temporary network
issue.
When the HelmRepository Artifact is archived in the controller's Artifact
storage, the controller sets a Condition with the following attributes in the
HelmRepository's `.status.conditions`:
- `type: ArtifactInStorage`
- `status: "True"`
- `reason: Succeeded`
This `ArtifactInStorage` Condition will retain a status value of `"True"` until
the Artifact in the storage no longer exists.
#### Failed HelmRepository
The source-controller may get stuck trying to produce an Artifact for a
HelmRepository without completing. This can occur due to some of the following
factors:
- The Helm repository [URL](#url) is temporarily unavailable.
- The [Secret reference](#secret-reference) contains a reference to a
non-existing Secret.
- The credentials in the referenced Secret are invalid.
- The HelmRepository spec contains a generic misconfiguration.
- A storage related failure when storing the artifact.
When this happens, the controller sets the `Ready` Condition status to `False`,
and adds a Condition with the following attributes to the HelmRepository's
`.status.conditions`:
- `type: FetchFailed` | `type: StorageOperationFailed`
- `status: "True"`
- `reason: AuthenticationFailed` | `reason: IndexationFailed` | `reason: Failed`
This condition has a ["negative polarity"][typical-status-properties],
and is only present on the HelmRepository while the status value is `"True"`.
There may be more arbitrary values for the `reason` field to provide accurate
reason for a condition.
While the HelmRepository has this Condition, the controller will continue to
attempt to produce an Artifact for the resource with an exponential backoff,
until it succeeds and the HelmRepository is marked as [ready](#ready-helmrepository).
Note that a HelmRepository can be [reconciling](#reconciling-helmrepository)
while failing at the same time, for example due to a newly introduced
configuration issue in the HelmRepository spec. When a reconciliation fails, the
`Reconciling` Condition reason would be `ProgressingWithRetry`. When the
reconciliation is performed again after the failure, the reason is updated to
`Progressing`.
#### Stalled HelmRepository
The source-controller can mark a HelmRepository as _stalled_ when it determines
that without changes to the spec, the reconciliation can not succeed.
For example because a Helm repository URL with an unsupported protocol is
specified.
When this happens, the controller sets the same Conditions as when it
[fails](#failed-helmrepository), but adds another Condition with the following
attributes to the HelmRepository's
`.status.conditions`:
- `type: Stalled`
- `status: "True"`
- `reason: URLInvalid`
While the HelmRepository has this Condition, the controller will not requeue
the resource any further, and will stop reconciling the resource until a change
to the spec is made.
### Observed Generation
The source-controller reports an [observed generation][typical-status-properties]
in the HelmRepository's `.status.observedGeneration`. The observed generation is
the latest `.metadata.generation` which resulted in either a [ready state](#ready-helmrepository),
or stalled due to error it can not recover from without human intervention.
### Last Handled Reconcile At
The source-controller reports the last `reconcile.fluxcd.io/requestedAt`
annotation value it acted on in the `.status.lastHandledReconcileAt` field.
For practical information about this field, see [triggering a
reconcile](#triggering-a-reconcile).
[pem-encoding]: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail
[typical-status-properties]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
[kstatus-spec]: https://github.com/kubernetes-sigs/cli-utils/tree/master/pkg/kstatus

View File

@ -157,9 +157,8 @@ to the IAM role when using IRSA.
#### Azure #### Azure
The `azure` provider can be used to authenticate automatically using Workload Identity, Kubelet Managed The `azure` provider can be used to authenticate automatically using Workload Identity and Kubelet Managed
Identity or Azure Active Directory pod-managed identity (aad-pod-identity), Identity to gain access to ACR.
and by extension gain access to ACR.
##### Kubelet Managed Identity ##### Kubelet Managed Identity
@ -215,41 +214,6 @@ a federated identity between the source-controller ServiceAccount and the
identity. Patch the source-controller Deployment and ServiceAccount as shown in the patch identity. Patch the source-controller Deployment and ServiceAccount as shown in the patch
above. Please take a look at this [guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#6-establish-federated-identity-credential-between-the-identity-and-the-service-account-issuer--subject). above. Please take a look at this [guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#6-establish-federated-identity-credential-between-the-identity-and-the-service-account-issuer--subject).
##### Deprecated: AAD Pod Identity
**Note:** The AAD Pod Identity project will be archived in [September 2023](https://github.com/Azure/aad-pod-identity#-announcement),
and you are advised to use Workload Identity instead.
When using aad-pod-identity to enable access to ACR, add the following patch to
your bootstrap repository, in the `flux-system/kustomization.yaml` file:
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- patch: |
- op: add
path: /spec/template/metadata/labels/aadpodidbinding
value: <identity-name>
target:
kind: Deployment
name: source-controller
```
When using pod-managed identity on an AKS cluster, AAD Pod Identity
has to be used to give the `source-controller` pod access to the ACR.
To do this, you have to install `aad-pod-identity` on your cluster, create a managed identity
that has access to the container registry (this can also be the Kubelet identity
if it has `AcrPull` role assignment on the ACR), create an `AzureIdentity` and `AzureIdentityBinding`
that describe the managed identity and then label the `source-controller` deployment
with the name of the AzureIdentity as shown in the patch above. Please take a look
at [this guide](https://azure.github.io/aad-pod-identity/docs/) or
[this one](https://docs.microsoft.com/en-us/azure/aks/use-azure-ad-pod-identity)
if you want to use AKS pod-managed identities add-on that is in preview.
#### GCP #### GCP
The `gcp` provider can be used to authenticate automatically using OAuth scopes The `gcp` provider can be used to authenticate automatically using OAuth scopes

14
go.mod
View File

@ -28,8 +28,8 @@ require (
github.com/fluxcd/pkg/helmtestserver v0.18.0 github.com/fluxcd/pkg/helmtestserver v0.18.0
github.com/fluxcd/pkg/lockedfile v0.3.0 github.com/fluxcd/pkg/lockedfile v0.3.0
github.com/fluxcd/pkg/masktoken v0.4.0 github.com/fluxcd/pkg/masktoken v0.4.0
github.com/fluxcd/pkg/oci v0.37.0 github.com/fluxcd/pkg/oci v0.37.1
github.com/fluxcd/pkg/runtime v0.47.0 github.com/fluxcd/pkg/runtime v0.47.1
github.com/fluxcd/pkg/sourceignore v0.7.0 github.com/fluxcd/pkg/sourceignore v0.7.0
github.com/fluxcd/pkg/ssh v0.13.0 github.com/fluxcd/pkg/ssh v0.13.0
github.com/fluxcd/pkg/tar v0.7.0 github.com/fluxcd/pkg/tar v0.7.0
@ -46,7 +46,7 @@ require (
github.com/minio/minio-go/v7 v7.0.70 github.com/minio/minio-go/v7 v7.0.70
github.com/notaryproject/notation-core-go v1.0.2 github.com/notaryproject/notation-core-go v1.0.2
github.com/notaryproject/notation-go v1.1.0 github.com/notaryproject/notation-go v1.1.0
github.com/onsi/gomega v1.32.0 github.com/onsi/gomega v1.33.1
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/go-digest/blake3 v0.0.0-20231212064514-429d0316a3dd github.com/opencontainers/go-digest/blake3 v0.0.0-20231212064514-429d0316a3dd
github.com/opencontainers/image-spec v1.1.0 github.com/opencontainers/image-spec v1.1.0
@ -68,7 +68,7 @@ require (
k8s.io/client-go v0.30.0 k8s.io/client-go v0.30.0
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 k8s.io/utils v0.0.0-20240310230437-4693a0247e57
oras.land/oras-go/v2 v2.5.0 oras.land/oras-go/v2 v2.5.0
sigs.k8s.io/controller-runtime v0.18.0 sigs.k8s.io/controller-runtime v0.18.1
sigs.k8s.io/yaml v1.4.0 sigs.k8s.io/yaml v1.4.0
) )
@ -116,8 +116,8 @@ require (
github.com/aliyun/credentials-go v1.3.1 // indirect github.com/aliyun/credentials-go v1.3.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.10 // indirect github.com/aws/aws-sdk-go-v2/config v1.27.11 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.10 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
@ -126,7 +126,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 // indirect github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.4 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
github.com/aws/smithy-go v1.20.2 // indirect github.com/aws/smithy-go v1.20.2 // indirect

39
go.sum
View File

@ -159,10 +159,10 @@ github.com/aws/aws-sdk-go v1.51.6 h1:Ld36dn9r7P9IjU8WZSaswQ8Y/XUCRpewim5980DwYiU
github.com/aws/aws-sdk-go v1.51.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go v1.51.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/config v1.27.10 h1:PS+65jThT0T/snC5WjyfHHyUgG+eBoupSDV+f838cro= github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA=
github.com/aws/aws-sdk-go-v2/config v1.27.10/go.mod h1:BePM7Vo4OBpHreKRUMuDXX+/+JWP38FLkzl5m27/Jjs= github.com/aws/aws-sdk-go-v2/config v1.27.11/go.mod h1:SMsV78RIOYdve1vf36z8LmnszlRWkwMQtomCAI0/mIE=
github.com/aws/aws-sdk-go-v2/credentials v1.17.10 h1:qDZ3EA2lv1KangvQB6y258OssCHD0xvaGiEDkG4X/10= github.com/aws/aws-sdk-go-v2/credentials v1.17.11 h1:YuIB1dJNf1Re822rriUOTxopaHHvIq0l/pX3fwO+Tzs=
github.com/aws/aws-sdk-go-v2/credentials v1.17.10/go.mod h1:6t3sucOaYDwDssHQa0ojH1RpmVmF5/jArkye1b2FKMI= github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
@ -181,8 +181,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/g
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 h1:yS0JkEdV6h9JOo8sy2JSpjX+i7vsKifU8SIeHrqiDhU= github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 h1:yS0JkEdV6h9JOo8sy2JSpjX+i7vsKifU8SIeHrqiDhU=
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0/go.mod h1:+I8VUUSVD4p5ISQtzpgSva4I8cJ4SQ4b1dcBcof7O+g= github.com/aws/aws-sdk-go-v2/service/kms v1.30.0/go.mod h1:+I8VUUSVD4p5ISQtzpgSva4I8cJ4SQ4b1dcBcof7O+g=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.4 h1:WzFol5Cd+yDxPAdnzTA5LmpHYSWinhmSj4rQChV0ee8= github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 h1:vN8hEbpRnL7+Hopy9dzmRle1xmDc7o8tmY0klsr175w=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.4/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM= github.com/aws/aws-sdk-go-v2/service/sso v1.20.5/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU= github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU=
@ -352,10 +352,10 @@ github.com/fluxcd/pkg/lockedfile v0.3.0 h1:tZkBAffcxyt4zMigHIKc54cKgN5I/kFF005gy
github.com/fluxcd/pkg/lockedfile v0.3.0/go.mod h1:5iCYXAs953LlXZq7nTId9ZSGnHVvTfZ0mDmrDE49upk= github.com/fluxcd/pkg/lockedfile v0.3.0/go.mod h1:5iCYXAs953LlXZq7nTId9ZSGnHVvTfZ0mDmrDE49upk=
github.com/fluxcd/pkg/masktoken v0.4.0 h1:pRItymXzW8dhT9Fd4XfnbrgKeySPeeLCrr6W1pgrUbM= github.com/fluxcd/pkg/masktoken v0.4.0 h1:pRItymXzW8dhT9Fd4XfnbrgKeySPeeLCrr6W1pgrUbM=
github.com/fluxcd/pkg/masktoken v0.4.0/go.mod h1:MP1nCsr2tJbH8hnhZP4+7TfTR0ggrKOJgi9Bo7Mj/6M= github.com/fluxcd/pkg/masktoken v0.4.0/go.mod h1:MP1nCsr2tJbH8hnhZP4+7TfTR0ggrKOJgi9Bo7Mj/6M=
github.com/fluxcd/pkg/oci v0.37.0 h1:hiRNMIGN1jKC2SrC4uWkSEQkGJnrZmSFYOYRL8/vVro= github.com/fluxcd/pkg/oci v0.37.1 h1:p4rfCHZlBWL+Q5Xey51iiBRmoje0IevCBT0/r8iae3M=
github.com/fluxcd/pkg/oci v0.37.0/go.mod h1:shriYSpwJFwORG3djxg5V6mifC6jXyRYbcXqdLd2kmY= github.com/fluxcd/pkg/oci v0.37.1/go.mod h1:LrVuX6VACenJ5ycQJxec+I7YJegCsE4nzRUV+6RuxcY=
github.com/fluxcd/pkg/runtime v0.47.0 h1:m3BEgwTYJslIF0lqhZMw6ZcKD6bD+4Ut+Xd/8X86SZA= github.com/fluxcd/pkg/runtime v0.47.1 h1:Q1tAFsp92uurWyoEe52AmMC4k+6DYTPBrUQDs+nz/9c=
github.com/fluxcd/pkg/runtime v0.47.0/go.mod h1:UgHy8DTkU2MFHDe2q3b+OP4mBYTsopGhSzWb8rHJa9Q= github.com/fluxcd/pkg/runtime v0.47.1/go.mod h1:97a+PqpWMgQsoqh91uH3EQz+/DC7Uxc8xcu/rDHFC5c=
github.com/fluxcd/pkg/sourceignore v0.7.0 h1:qQrB2o543wA1o4vgR62ufwkAaDp8+f8Wdj1HKDlmDrU= github.com/fluxcd/pkg/sourceignore v0.7.0 h1:qQrB2o543wA1o4vgR62ufwkAaDp8+f8Wdj1HKDlmDrU=
github.com/fluxcd/pkg/sourceignore v0.7.0/go.mod h1:A4GuZt2seJJkBm3kMiIx9nheoYZs98KTMr/A6/2fIro= github.com/fluxcd/pkg/sourceignore v0.7.0/go.mod h1:A4GuZt2seJJkBm3kMiIx9nheoYZs98KTMr/A6/2fIro=
github.com/fluxcd/pkg/ssh v0.13.0 h1:lPU1Gst8XIz7AU2dhdqVFaaOWd54/O1LZu62vH4JB/s= github.com/fluxcd/pkg/ssh v0.13.0 h1:lPU1Gst8XIz7AU2dhdqVFaaOWd54/O1LZu62vH4JB/s=
@ -443,7 +443,8 @@ github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqw
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU=
@ -526,8 +527,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
@ -748,14 +749,14 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/open-policy-agent/opa v0.63.0 h1:ztNNste1v8kH0/vJMJNquE45lRvqwrM5mY9Ctr9xIXw= github.com/open-policy-agent/opa v0.63.0 h1:ztNNste1v8kH0/vJMJNquE45lRvqwrM5mY9Ctr9xIXw=
github.com/open-policy-agent/opa v0.63.0/go.mod h1:9VQPqEfoB2N//AToTxzZ1pVTVPUoF2Mhd64szzjWPpU= github.com/open-policy-agent/opa v0.63.0/go.mod h1:9VQPqEfoB2N//AToTxzZ1pVTVPUoF2Mhd64szzjWPpU=
github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be h1:f2PlhC9pm5sqpBZFvnAoKj+KzXRzbjFMA+TqXfJdgho= github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be h1:f2PlhC9pm5sqpBZFvnAoKj+KzXRzbjFMA+TqXfJdgho=
@ -1311,8 +1312,8 @@ oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY=
oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324= oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324=
oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c=
oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg= oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=
sigs.k8s.io/controller-runtime v0.18.0 h1:Z7jKuX784TQSUL1TIyeuF7j8KXZ4RtSX0YgtjKcSTME= sigs.k8s.io/controller-runtime v0.18.1 h1:RpWbigmuiylbxOCLy0tGnq1cU1qWPwNIQzoJk+QeJx4=
sigs.k8s.io/controller-runtime v0.18.0/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= sigs.k8s.io/controller-runtime v0.18.1/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.17.1 h1:MYJBOP/yQ3/5tp4/sf6HiiMfNNyO97LmtnirH9SLNr4= sigs.k8s.io/kustomize/api v0.17.1 h1:MYJBOP/yQ3/5tp4/sf6HiiMfNNyO97LmtnirH9SLNr4=

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2023 The Flux authors Copyright 2024 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -65,7 +65,7 @@ import (
"github.com/fluxcd/pkg/tar" "github.com/fluxcd/pkg/tar"
sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
helmv1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/fluxcd/source-controller/internal/cache" "github.com/fluxcd/source-controller/internal/cache"
serror "github.com/fluxcd/source-controller/internal/error" serror "github.com/fluxcd/source-controller/internal/error"
"github.com/fluxcd/source-controller/internal/helm/chart" "github.com/fluxcd/source-controller/internal/helm/chart"
@ -159,29 +159,29 @@ type HelmChartReconcilerOptions struct {
RateLimiter ratelimiter.RateLimiter RateLimiter ratelimiter.RateLimiter
} }
// helmChartReconcileFunc is the function type for all the v1beta2.HelmChart // helmChartReconcileFunc is the function type for all the v1.HelmChart
// (sub)reconcile functions. The type implementations are grouped and // (sub)reconcile functions. The type implementations are grouped and
// executed serially to perform the complete reconcile of the object. // executed serially to perform the complete reconcile of the object.
type helmChartReconcileFunc func(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmChart, build *chart.Build) (sreconcile.Result, error) type helmChartReconcileFunc func(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmChart, build *chart.Build) (sreconcile.Result, error)
func (r *HelmChartReconciler) SetupWithManagerAndOptions(ctx context.Context, mgr ctrl.Manager, opts HelmChartReconcilerOptions) error { func (r *HelmChartReconciler) SetupWithManagerAndOptions(ctx context.Context, mgr ctrl.Manager, opts HelmChartReconcilerOptions) error {
r.patchOptions = getPatchOptions(helmChartReadyCondition.Owned, r.ControllerName) r.patchOptions = getPatchOptions(helmChartReadyCondition.Owned, r.ControllerName)
if err := mgr.GetCache().IndexField(ctx, &helmv1.HelmRepository{}, helmv1.HelmRepositoryURLIndexKey, if err := mgr.GetCache().IndexField(ctx, &sourcev1.HelmRepository{}, sourcev1.HelmRepositoryURLIndexKey,
r.indexHelmRepositoryByURL); err != nil { r.indexHelmRepositoryByURL); err != nil {
return fmt.Errorf("failed setting index fields: %w", err) return fmt.Errorf("failed setting index fields: %w", err)
} }
if err := mgr.GetCache().IndexField(ctx, &helmv1.HelmChart{}, sourcev1.SourceIndexKey, if err := mgr.GetCache().IndexField(ctx, &sourcev1.HelmChart{}, sourcev1.SourceIndexKey,
r.indexHelmChartBySource); err != nil { r.indexHelmChartBySource); err != nil {
return fmt.Errorf("failed setting index fields: %w", err) return fmt.Errorf("failed setting index fields: %w", err)
} }
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).
For(&helmv1.HelmChart{}, builder.WithPredicates( For(&sourcev1.HelmChart{}, builder.WithPredicates(
predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}), predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}),
)). )).
Watches( Watches(
&helmv1.HelmRepository{}, &sourcev1.HelmRepository{},
handler.EnqueueRequestsFromMapFunc(r.requestsForHelmRepositoryChange), handler.EnqueueRequestsFromMapFunc(r.requestsForHelmRepositoryChange),
builder.WithPredicates(SourceRevisionChangePredicate{}), builder.WithPredicates(SourceRevisionChangePredicate{}),
). ).
@ -191,7 +191,7 @@ func (r *HelmChartReconciler) SetupWithManagerAndOptions(ctx context.Context, mg
builder.WithPredicates(SourceRevisionChangePredicate{}), builder.WithPredicates(SourceRevisionChangePredicate{}),
). ).
Watches( Watches(
&helmv1.Bucket{}, &sourcev1beta2.Bucket{},
handler.EnqueueRequestsFromMapFunc(r.requestsForBucketChange), handler.EnqueueRequestsFromMapFunc(r.requestsForBucketChange),
builder.WithPredicates(SourceRevisionChangePredicate{}), builder.WithPredicates(SourceRevisionChangePredicate{}),
). ).
@ -206,7 +206,7 @@ func (r *HelmChartReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
log := ctrl.LoggerFrom(ctx) log := ctrl.LoggerFrom(ctx)
// Fetch the HelmChart // Fetch the HelmChart
obj := &helmv1.HelmChart{} obj := &sourcev1.HelmChart{}
if err := r.Get(ctx, req.NamespacedName, obj); err != nil { if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err) return ctrl.Result{}, client.IgnoreNotFound(err)
} }
@ -280,7 +280,7 @@ func (r *HelmChartReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
// reconcile iterates through the helmChartReconcileFunc tasks for the // reconcile iterates through the helmChartReconcileFunc tasks for the
// object. It returns early on the first call that returns // object. It returns early on the first call that returns
// reconcile.ResultRequeue, or produces an error. // reconcile.ResultRequeue, or produces an error.
func (r *HelmChartReconciler) reconcile(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmChart, reconcilers []helmChartReconcileFunc) (sreconcile.Result, error) { func (r *HelmChartReconciler) reconcile(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmChart, reconcilers []helmChartReconcileFunc) (sreconcile.Result, error) {
oldObj := obj.DeepCopy() oldObj := obj.DeepCopy()
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconciliation in progress") rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconciliation in progress")
@ -333,7 +333,7 @@ func (r *HelmChartReconciler) reconcile(ctx context.Context, sp *patch.SerialPat
} }
// notify emits notification related to the reconciliation. // notify emits notification related to the reconciliation.
func (r *HelmChartReconciler) notify(ctx context.Context, oldObj, newObj *helmv1.HelmChart, build *chart.Build, res sreconcile.Result, resErr error) { func (r *HelmChartReconciler) notify(ctx context.Context, oldObj, newObj *sourcev1.HelmChart, build *chart.Build, res sreconcile.Result, resErr error) {
// Notify successful reconciliation for new artifact and recovery from any // Notify successful reconciliation for new artifact and recovery from any
// failure. // failure.
if resErr == nil && res == sreconcile.ResultSuccess && newObj.Status.Artifact != nil { if resErr == nil && res == sreconcile.ResultSuccess && newObj.Status.Artifact != nil {
@ -369,7 +369,7 @@ func (r *HelmChartReconciler) notify(ctx context.Context, oldObj, newObj *helmv1
// condition is added. // condition is added.
// The hostname of any URL in the Status of the object are updated, to ensure // The hostname of any URL in the Status of the object are updated, to ensure
// they match the Storage server hostname of current runtime. // they match the Storage server hostname of current runtime.
func (r *HelmChartReconciler) reconcileStorage(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmChart, _ *chart.Build) (sreconcile.Result, error) { func (r *HelmChartReconciler) reconcileStorage(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmChart, _ *chart.Build) (sreconcile.Result, error) {
// Garbage collect previous advertised artifact(s) from storage // Garbage collect previous advertised artifact(s) from storage
_ = r.garbageCollect(ctx, obj) _ = r.garbageCollect(ctx, obj)
@ -423,7 +423,7 @@ func (r *HelmChartReconciler) reconcileStorage(ctx context.Context, sp *patch.Se
return sreconcile.ResultSuccess, nil return sreconcile.ResultSuccess, nil
} }
func (r *HelmChartReconciler) reconcileSource(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmChart, build *chart.Build) (_ sreconcile.Result, retErr error) { func (r *HelmChartReconciler) reconcileSource(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmChart, build *chart.Build) (_ sreconcile.Result, retErr error) {
// Remove any failed verification condition. // Remove any failed verification condition.
// The reason is that a failing verification should be recalculated. // The reason is that a failing verification should be recalculated.
if conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) { if conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) {
@ -453,7 +453,7 @@ func (r *HelmChartReconciler) reconcileSource(ctx context.Context, sp *patch.Ser
// Assert source has an artifact // Assert source has an artifact
if s.GetArtifact() == nil || !r.Storage.ArtifactExist(*s.GetArtifact()) { if s.GetArtifact() == nil || !r.Storage.ArtifactExist(*s.GetArtifact()) {
// Set the condition to indicate that the source has no artifact for all types except OCI HelmRepository // Set the condition to indicate that the source has no artifact for all types except OCI HelmRepository
if helmRepo, ok := s.(*helmv1.HelmRepository); !ok || helmRepo.Spec.Type != helmv1.HelmRepositoryTypeOCI { if helmRepo, ok := s.(*sourcev1.HelmRepository); !ok || helmRepo.Spec.Type != sourcev1.HelmRepositoryTypeOCI {
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, "NoSourceArtifact", conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, "NoSourceArtifact",
"no artifact available for %s source '%s'", obj.Spec.SourceRef.Kind, obj.Spec.SourceRef.Name) "no artifact available for %s source '%s'", obj.Spec.SourceRef.Kind, obj.Spec.SourceRef.Name)
r.eventLogf(ctx, obj, eventv1.EventTypeTrace, "NoSourceArtifact", r.eventLogf(ctx, obj, eventv1.EventTypeTrace, "NoSourceArtifact",
@ -500,9 +500,9 @@ func (r *HelmChartReconciler) reconcileSource(ctx context.Context, sp *patch.Ser
// Perform the build for the chart source type // Perform the build for the chart source type
switch typedSource := s.(type) { switch typedSource := s.(type) {
case *helmv1.HelmRepository: case *sourcev1.HelmRepository:
return r.buildFromHelmRepository(ctx, obj, typedSource, build) return r.buildFromHelmRepository(ctx, obj, typedSource, build)
case *sourcev1.GitRepository, *helmv1.Bucket: case *sourcev1.GitRepository, *sourcev1beta2.Bucket:
return r.buildFromTarballArtifact(ctx, obj, *typedSource.GetArtifact(), build) return r.buildFromTarballArtifact(ctx, obj, *typedSource.GetArtifact(), build)
default: default:
// Ending up here should generally not be possible // Ending up here should generally not be possible
@ -512,12 +512,12 @@ func (r *HelmChartReconciler) reconcileSource(ctx context.Context, sp *patch.Ser
} }
// buildFromHelmRepository attempts to pull and/or package a Helm chart with // buildFromHelmRepository attempts to pull and/or package a Helm chart with
// the specified data from the v1beta2.HelmRepository and v1beta2.HelmChart // the specified data from the v1.HelmRepository and v1.HelmChart
// objects. // objects.
// In case of a failure it records v1beta2.FetchFailedCondition on the chart // In case of a failure it records v1.FetchFailedCondition on the chart
// object, and returns early. // object, and returns early.
func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *helmv1.HelmChart, func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *sourcev1.HelmChart,
repo *helmv1.HelmRepository, b *chart.Build) (sreconcile.Result, error) { repo *sourcev1.HelmRepository, b *chart.Build) (sreconcile.Result, error) {
// Used to login with the repository declared provider // Used to login with the repository declared provider
ctxTimeout, cancel := context.WithTimeout(ctx, repo.GetTimeout()) ctxTimeout, cancel := context.WithTimeout(ctx, repo.GetTimeout())
defer cancel() defer cancel()
@ -550,7 +550,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
// Initialize the chart repository // Initialize the chart repository
var chartRepo repository.Downloader var chartRepo repository.Downloader
switch repo.Spec.Type { switch repo.Spec.Type {
case helmv1.HelmRepositoryTypeOCI: case sourcev1.HelmRepositoryTypeOCI:
if !helmreg.IsOCI(normalizedURL) { if !helmreg.IsOCI(normalizedURL) {
err := fmt.Errorf("invalid OCI registry URL: %s", normalizedURL) err := fmt.Errorf("invalid OCI registry URL: %s", normalizedURL)
return chartRepoConfigErrorReturn(err, obj) return chartRepoConfigErrorReturn(err, obj)
@ -696,11 +696,11 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
} }
// buildFromTarballArtifact attempts to pull and/or package a Helm chart with // buildFromTarballArtifact attempts to pull and/or package a Helm chart with
// the specified data from the v1beta2.HelmChart object and the given // the specified data from the v1.HelmChart object and the given
// v1beta2.Artifact. // v1.Artifact.
// In case of a failure it records v1beta2.FetchFailedCondition on the chart // In case of a failure it records v1.FetchFailedCondition on the chart
// object, and returns early. // object, and returns early.
func (r *HelmChartReconciler) buildFromTarballArtifact(ctx context.Context, obj *helmv1.HelmChart, source sourcev1.Artifact, b *chart.Build) (sreconcile.Result, error) { func (r *HelmChartReconciler) buildFromTarballArtifact(ctx context.Context, obj *sourcev1.HelmChart, source sourcev1.Artifact, b *chart.Build) (sreconcile.Result, error) {
// Create temporary working directory // Create temporary working directory
tmpDir, err := util.TempDirForObj("", obj) tmpDir, err := util.TempDirForObj("", obj)
if err != nil { if err != nil {
@ -772,17 +772,17 @@ func (r *HelmChartReconciler) buildFromTarballArtifact(ctx context.Context, obj
} }
// Configure revision metadata for chart build if we should react to revision changes // Configure revision metadata for chart build if we should react to revision changes
if obj.Spec.ReconcileStrategy == helmv1.ReconcileStrategyRevision { if obj.Spec.ReconcileStrategy == sourcev1.ReconcileStrategyRevision {
rev := source.Revision rev := source.Revision
if obj.Spec.SourceRef.Kind == sourcev1.GitRepositoryKind { if obj.Spec.SourceRef.Kind == sourcev1.GitRepositoryKind {
rev = git.ExtractHashFromRevision(rev).String() rev = git.ExtractHashFromRevision(rev).String()
} }
if obj.Spec.SourceRef.Kind == helmv1.BucketKind { if obj.Spec.SourceRef.Kind == sourcev1beta2.BucketKind {
if dig := digest.Digest(rev); dig.Validate() == nil { if dig := digest.Digest(rev); dig.Validate() == nil {
rev = dig.Encoded() rev = dig.Encoded()
} }
} }
if kind := obj.Spec.SourceRef.Kind; kind == sourcev1.GitRepositoryKind || kind == helmv1.BucketKind { if kind := obj.Spec.SourceRef.Kind; kind == sourcev1.GitRepositoryKind || kind == sourcev1beta2.BucketKind {
// The SemVer from the metadata is at times used in e.g. the label metadata for a resource // The SemVer from the metadata is at times used in e.g. the label metadata for a resource
// in a chart, which has a limited length of 63 characters. // in a chart, which has a limited length of 63 characters.
// To not fill most of this space with a full length SHA hex (40 characters for SHA-1, and // To not fill most of this space with a full length SHA hex (40 characters for SHA-1, and
@ -824,12 +824,12 @@ func (r *HelmChartReconciler) buildFromTarballArtifact(ctx context.Context, obj
// (Status) data on the object does not match the given. // (Status) data on the object does not match the given.
// //
// The inspection of the given data to the object is differed, ensuring any // The inspection of the given data to the object is differed, ensuring any
// stale observations like v1beta2.ArtifactOutdatedCondition are removed. // stale observations like v1.ArtifactOutdatedCondition are removed.
// If the given Artifact does not differ from the object's current, it returns // If the given Artifact does not differ from the object's current, it returns
// early. // early.
// On a successful archive, the Artifact in the Status of the object is set, // On a successful archive, the Artifact in the Status of the object is set,
// and the symlink in the Storage is updated to its path. // and the symlink in the Storage is updated to its path.
func (r *HelmChartReconciler) reconcileArtifact(ctx context.Context, _ *patch.SerialPatcher, obj *helmv1.HelmChart, b *chart.Build) (sreconcile.Result, error) { func (r *HelmChartReconciler) reconcileArtifact(ctx context.Context, _ *patch.SerialPatcher, obj *sourcev1.HelmChart, b *chart.Build) (sreconcile.Result, error) {
// Without a complete chart build, there is little to reconcile // Without a complete chart build, there is little to reconcile
if !b.Complete() { if !b.Complete() {
return sreconcile.ResultRequeue, nil return sreconcile.ResultRequeue, nil
@ -909,15 +909,15 @@ func (r *HelmChartReconciler) reconcileArtifact(ctx context.Context, _ *patch.Se
// getSource returns the v1beta1.Source for the given object, or an error describing why the source could not be // getSource returns the v1beta1.Source for the given object, or an error describing why the source could not be
// returned. // returned.
func (r *HelmChartReconciler) getSource(ctx context.Context, obj *helmv1.HelmChart) (sourcev1.Source, error) { func (r *HelmChartReconciler) getSource(ctx context.Context, obj *sourcev1.HelmChart) (sourcev1.Source, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: obj.GetNamespace(), Namespace: obj.GetNamespace(),
Name: obj.Spec.SourceRef.Name, Name: obj.Spec.SourceRef.Name,
} }
var s sourcev1.Source var s sourcev1.Source
switch obj.Spec.SourceRef.Kind { switch obj.Spec.SourceRef.Kind {
case helmv1.HelmRepositoryKind: case sourcev1.HelmRepositoryKind:
var repo helmv1.HelmRepository var repo sourcev1.HelmRepository
if err := r.Client.Get(ctx, namespacedName, &repo); err != nil { if err := r.Client.Get(ctx, namespacedName, &repo); err != nil {
return nil, err return nil, err
} }
@ -928,15 +928,15 @@ func (r *HelmChartReconciler) getSource(ctx context.Context, obj *helmv1.HelmCha
return nil, err return nil, err
} }
s = &repo s = &repo
case helmv1.BucketKind: case sourcev1beta2.BucketKind:
var bucket helmv1.Bucket var bucket sourcev1beta2.Bucket
if err := r.Client.Get(ctx, namespacedName, &bucket); err != nil { if err := r.Client.Get(ctx, namespacedName, &bucket); err != nil {
return nil, err return nil, err
} }
s = &bucket s = &bucket
default: default:
return nil, fmt.Errorf("unsupported source kind '%s', must be one of: %v", obj.Spec.SourceRef.Kind, []string{ return nil, fmt.Errorf("unsupported source kind '%s', must be one of: %v", obj.Spec.SourceRef.Kind, []string{
helmv1.HelmRepositoryKind, sourcev1.GitRepositoryKind, helmv1.BucketKind}) sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind, sourcev1beta2.BucketKind})
} }
return s, nil return s, nil
} }
@ -944,7 +944,7 @@ func (r *HelmChartReconciler) getSource(ctx context.Context, obj *helmv1.HelmCha
// reconcileDelete handles the deletion of the object. // reconcileDelete handles the deletion of the object.
// It first garbage collects all Artifacts for the object from the Storage. // It first garbage collects all Artifacts for the object from the Storage.
// Removing the finalizer from the object if successful. // Removing the finalizer from the object if successful.
func (r *HelmChartReconciler) reconcileDelete(ctx context.Context, obj *helmv1.HelmChart) (sreconcile.Result, error) { func (r *HelmChartReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.HelmChart) (sreconcile.Result, error) {
// Garbage collect the resource's artifacts // Garbage collect the resource's artifacts
if err := r.garbageCollect(ctx, obj); err != nil { if err := r.garbageCollect(ctx, obj); err != nil {
// Return the error so we retry the failed garbage collection // Return the error so we retry the failed garbage collection
@ -963,7 +963,7 @@ func (r *HelmChartReconciler) reconcileDelete(ctx context.Context, obj *helmv1.H
// It removes all but the current Artifact from the Storage, unless the // It removes all but the current Artifact from the Storage, unless the
// deletion timestamp on the object is set. Which will result in the // deletion timestamp on the object is set. Which will result in the
// removal of all Artifacts for the objects. // removal of all Artifacts for the objects.
func (r *HelmChartReconciler) garbageCollect(ctx context.Context, obj *helmv1.HelmChart) error { func (r *HelmChartReconciler) garbageCollect(ctx context.Context, obj *sourcev1.HelmChart) error {
if !obj.DeletionTimestamp.IsZero() { if !obj.DeletionTimestamp.IsZero() {
if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil { if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
return serror.NewGeneric( return serror.NewGeneric(
@ -1010,8 +1010,8 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
if apierrs.ReasonForError(err) != metav1.StatusReasonUnknown { if apierrs.ReasonForError(err) != metav1.StatusReasonUnknown {
return nil, err return nil, err
} }
obj = &helmv1.HelmRepository{ obj = &sourcev1.HelmRepository{
Spec: helmv1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
URL: url, URL: url,
Timeout: &metav1.Duration{Duration: 60 * time.Second}, Timeout: &metav1.Duration{Duration: 60 * time.Second},
}, },
@ -1099,13 +1099,13 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
} }
} }
func (r *HelmChartReconciler) resolveDependencyRepository(ctx context.Context, url string, namespace string) (*helmv1.HelmRepository, error) { func (r *HelmChartReconciler) resolveDependencyRepository(ctx context.Context, url string, namespace string) (*sourcev1.HelmRepository, error) {
listOpts := []client.ListOption{ listOpts := []client.ListOption{
client.InNamespace(namespace), client.InNamespace(namespace),
client.MatchingFields{helmv1.HelmRepositoryURLIndexKey: url}, client.MatchingFields{sourcev1.HelmRepositoryURLIndexKey: url},
client.Limit(1), client.Limit(1),
} }
var list helmv1.HelmRepositoryList var list sourcev1.HelmRepositoryList
err := r.Client.List(ctx, &list, listOpts...) err := r.Client.List(ctx, &list, listOpts...)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to retrieve HelmRepositoryList: %w", err) return nil, fmt.Errorf("unable to retrieve HelmRepositoryList: %w", err)
@ -1117,7 +1117,7 @@ func (r *HelmChartReconciler) resolveDependencyRepository(ctx context.Context, u
} }
func (r *HelmChartReconciler) indexHelmRepositoryByURL(o client.Object) []string { func (r *HelmChartReconciler) indexHelmRepositoryByURL(o client.Object) []string {
repo, ok := o.(*helmv1.HelmRepository) repo, ok := o.(*sourcev1.HelmRepository)
if !ok { if !ok {
panic(fmt.Sprintf("Expected a HelmRepository, got %T", o)) panic(fmt.Sprintf("Expected a HelmRepository, got %T", o))
} }
@ -1129,7 +1129,7 @@ func (r *HelmChartReconciler) indexHelmRepositoryByURL(o client.Object) []string
} }
func (r *HelmChartReconciler) indexHelmChartBySource(o client.Object) []string { func (r *HelmChartReconciler) indexHelmChartBySource(o client.Object) []string {
hc, ok := o.(*helmv1.HelmChart) hc, ok := o.(*sourcev1.HelmChart)
if !ok { if !ok {
panic(fmt.Sprintf("Expected a HelmChart, got %T", o)) panic(fmt.Sprintf("Expected a HelmChart, got %T", o))
} }
@ -1137,7 +1137,7 @@ func (r *HelmChartReconciler) indexHelmChartBySource(o client.Object) []string {
} }
func (r *HelmChartReconciler) requestsForHelmRepositoryChange(ctx context.Context, o client.Object) []reconcile.Request { func (r *HelmChartReconciler) requestsForHelmRepositoryChange(ctx context.Context, o client.Object) []reconcile.Request {
repo, ok := o.(*helmv1.HelmRepository) repo, ok := o.(*sourcev1.HelmRepository)
if !ok { if !ok {
ctrl.LoggerFrom(ctx).Error(fmt.Errorf("expected a HelmRepository, got %T", o), "failed to get requests for HelmRepository change") ctrl.LoggerFrom(ctx).Error(fmt.Errorf("expected a HelmRepository, got %T", o), "failed to get requests for HelmRepository change")
return nil return nil
@ -1148,9 +1148,9 @@ func (r *HelmChartReconciler) requestsForHelmRepositoryChange(ctx context.Contex
return nil return nil
} }
var list helmv1.HelmChartList var list sourcev1.HelmChartList
if err := r.List(ctx, &list, client.MatchingFields{ if err := r.List(ctx, &list, client.MatchingFields{
sourcev1.SourceIndexKey: fmt.Sprintf("%s/%s", helmv1.HelmRepositoryKind, repo.Name), sourcev1.SourceIndexKey: fmt.Sprintf("%s/%s", sourcev1.HelmRepositoryKind, repo.Name),
}); err != nil { }); err != nil {
ctrl.LoggerFrom(ctx).Error(err, "failed to list HelmCharts for HelmRepository change") ctrl.LoggerFrom(ctx).Error(err, "failed to list HelmCharts for HelmRepository change")
return nil return nil
@ -1178,7 +1178,7 @@ func (r *HelmChartReconciler) requestsForGitRepositoryChange(ctx context.Context
return nil return nil
} }
var list helmv1.HelmChartList var list sourcev1.HelmChartList
if err := r.List(ctx, &list, client.MatchingFields{ if err := r.List(ctx, &list, client.MatchingFields{
sourcev1.SourceIndexKey: fmt.Sprintf("%s/%s", sourcev1.GitRepositoryKind, repo.Name), sourcev1.SourceIndexKey: fmt.Sprintf("%s/%s", sourcev1.GitRepositoryKind, repo.Name),
}); err != nil { }); err != nil {
@ -1196,7 +1196,7 @@ func (r *HelmChartReconciler) requestsForGitRepositoryChange(ctx context.Context
} }
func (r *HelmChartReconciler) requestsForBucketChange(ctx context.Context, o client.Object) []reconcile.Request { func (r *HelmChartReconciler) requestsForBucketChange(ctx context.Context, o client.Object) []reconcile.Request {
bucket, ok := o.(*helmv1.Bucket) bucket, ok := o.(*sourcev1beta2.Bucket)
if !ok { if !ok {
ctrl.LoggerFrom(ctx).Error(fmt.Errorf("expected a Bucket, got %T", o), ctrl.LoggerFrom(ctx).Error(fmt.Errorf("expected a Bucket, got %T", o),
"failed to get reconcile requests for Bucket change") "failed to get reconcile requests for Bucket change")
@ -1208,9 +1208,9 @@ func (r *HelmChartReconciler) requestsForBucketChange(ctx context.Context, o cli
return nil return nil
} }
var list helmv1.HelmChartList var list sourcev1.HelmChartList
if err := r.List(ctx, &list, client.MatchingFields{ if err := r.List(ctx, &list, client.MatchingFields{
sourcev1.SourceIndexKey: fmt.Sprintf("%s/%s", helmv1.BucketKind, bucket.Name), sourcev1.SourceIndexKey: fmt.Sprintf("%s/%s", sourcev1beta2.BucketKind, bucket.Name),
}); err != nil { }); err != nil {
ctrl.LoggerFrom(ctx).Error(err, "failed to list HelmCharts for Bucket change") ctrl.LoggerFrom(ctx).Error(err, "failed to list HelmCharts for Bucket change")
return nil return nil
@ -1242,7 +1242,7 @@ func (r *HelmChartReconciler) eventLogf(ctx context.Context, obj runtime.Object,
} }
// observeChartBuild records the observation on the given given build and error on the object. // observeChartBuild records the observation on the given given build and error on the object.
func observeChartBuild(ctx context.Context, sp *patch.SerialPatcher, pOpts []patch.Option, obj *helmv1.HelmChart, build *chart.Build, err error) { func observeChartBuild(ctx context.Context, sp *patch.SerialPatcher, pOpts []patch.Option, obj *sourcev1.HelmChart, build *chart.Build, err error) {
if build.HasMetadata() { if build.HasMetadata() {
if build.Name != obj.Status.ObservedChartName || !obj.GetArtifact().HasRevision(build.Version) { if build.Name != obj.Status.ObservedChartName || !obj.GetArtifact().HasRevision(build.Version) {
if obj.GetArtifact() != nil { if obj.GetArtifact() != nil {
@ -1297,12 +1297,12 @@ func reasonForBuild(build *chart.Build) string {
return "" return ""
} }
if build.Packaged { if build.Packaged {
return helmv1.ChartPackageSucceededReason return sourcev1.ChartPackageSucceededReason
} }
return helmv1.ChartPullSucceededReason return sourcev1.ChartPullSucceededReason
} }
func chartRepoConfigErrorReturn(err error, obj *helmv1.HelmChart) (sreconcile.Result, error) { func chartRepoConfigErrorReturn(err error, obj *sourcev1.HelmChart) (sreconcile.Result, error) {
switch err.(type) { switch err.(type) {
case *url.Error: case *url.Error:
e := serror.NewStalling( e := serror.NewStalling(
@ -1322,7 +1322,7 @@ func chartRepoConfigErrorReturn(err error, obj *helmv1.HelmChart) (sreconcile.Re
} }
// makeVerifiers returns a list of verifiers for the given chart. // makeVerifiers returns a list of verifiers for the given chart.
func (r *HelmChartReconciler) makeVerifiers(ctx context.Context, obj *helmv1.HelmChart, clientOpts getter.ClientOpts) ([]soci.Verifier, error) { func (r *HelmChartReconciler) makeVerifiers(ctx context.Context, obj *sourcev1.HelmChart, clientOpts getter.ClientOpts) ([]soci.Verifier, error) {
var verifiers []soci.Verifier var verifiers []soci.Verifier
verifyOpts := []remote.Option{} verifyOpts := []remote.Option{}

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,6 @@ import (
rreconcile "github.com/fluxcd/pkg/runtime/reconcile" rreconcile "github.com/fluxcd/pkg/runtime/reconcile"
sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
helmv1 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/fluxcd/source-controller/internal/cache" "github.com/fluxcd/source-controller/internal/cache"
intdigest "github.com/fluxcd/source-controller/internal/digest" intdigest "github.com/fluxcd/source-controller/internal/digest"
serror "github.com/fluxcd/source-controller/internal/error" serror "github.com/fluxcd/source-controller/internal/error"
@ -61,7 +60,7 @@ import (
) )
// helmRepositoryReadyCondition contains the information required to summarize a // helmRepositoryReadyCondition contains the information required to summarize a
// v1beta2.HelmRepository Ready Condition. // v1.HelmRepository Ready Condition.
var helmRepositoryReadyCondition = summarize.Conditions{ var helmRepositoryReadyCondition = summarize.Conditions{
Target: meta.ReadyCondition, Target: meta.ReadyCondition,
Owned: []string{ Owned: []string{
@ -102,7 +101,7 @@ var helmRepositoryFailConditions = []string{
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmrepositories/finalizers,verbs=get;create;update;patch;delete // +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmrepositories/finalizers,verbs=get;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
// HelmRepositoryReconciler reconciles a v1beta2.HelmRepository object. // HelmRepositoryReconciler reconciles a v1.HelmRepository object.
type HelmRepositoryReconciler struct { type HelmRepositoryReconciler struct {
client.Client client.Client
kuberecorder.EventRecorder kuberecorder.EventRecorder
@ -124,10 +123,10 @@ type HelmRepositoryReconcilerOptions struct {
} }
// helmRepositoryReconcileFunc is the function type for all the // helmRepositoryReconcileFunc is the function type for all the
// v1beta2.HelmRepository (sub)reconcile functions. The type implementations // v1.HelmRepository (sub)reconcile functions. The type implementations
// are grouped and executed serially to perform the complete reconcile of the // are grouped and executed serially to perform the complete reconcile of the
// object. // object.
type helmRepositoryReconcileFunc func(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) type helmRepositoryReconcileFunc func(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error)
func (r *HelmRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *HelmRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
return r.SetupWithManagerAndOptions(mgr, HelmRepositoryReconcilerOptions{}) return r.SetupWithManagerAndOptions(mgr, HelmRepositoryReconcilerOptions{})
@ -137,7 +136,7 @@ func (r *HelmRepositoryReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager,
r.patchOptions = getPatchOptions(helmRepositoryReadyCondition.Owned, r.ControllerName) r.patchOptions = getPatchOptions(helmRepositoryReadyCondition.Owned, r.ControllerName)
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).
For(&helmv1.HelmRepository{}). For(&sourcev1.HelmRepository{}).
WithEventFilter( WithEventFilter(
predicate.And( predicate.And(
intpredicates.HelmRepositoryOCIMigrationPredicate{}, intpredicates.HelmRepositoryOCIMigrationPredicate{},
@ -155,7 +154,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
log := ctrl.LoggerFrom(ctx) log := ctrl.LoggerFrom(ctx)
// Fetch the HelmRepository // Fetch the HelmRepository
obj := &helmv1.HelmRepository{} obj := &sourcev1.HelmRepository{}
if err := r.Get(ctx, req.NamespacedName, obj); err != nil { if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err) return ctrl.Result{}, client.IgnoreNotFound(err)
} }
@ -164,7 +163,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
serialPatcher := patch.NewSerialPatcher(obj, r.Client) serialPatcher := patch.NewSerialPatcher(obj, r.Client)
// If it's of type OCI, migrate the object to static. // If it's of type OCI, migrate the object to static.
if obj.Spec.Type == helmv1.HelmRepositoryTypeOCI { if obj.Spec.Type == sourcev1.HelmRepositoryTypeOCI {
return r.migrationToStatic(ctx, serialPatcher, obj) return r.migrationToStatic(ctx, serialPatcher, obj)
} }
@ -234,7 +233,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
// object. It returns early on the first call that returns // object. It returns early on the first call that returns
// reconcile.ResultRequeue, or produces an error. // reconcile.ResultRequeue, or produces an error.
func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, sp *patch.SerialPatcher, func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, sp *patch.SerialPatcher,
obj *helmv1.HelmRepository, reconcilers []helmRepositoryReconcileFunc) (sreconcile.Result, error) { obj *sourcev1.HelmRepository, reconcilers []helmRepositoryReconcileFunc) (sreconcile.Result, error) {
oldObj := obj.DeepCopy() oldObj := obj.DeepCopy()
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconciliation in progress") rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconciliation in progress")
@ -287,7 +286,7 @@ func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, sp *patch.Seri
} }
// notify emits notification related to the reconciliation. // notify emits notification related to the reconciliation.
func (r *HelmRepositoryReconciler) notify(ctx context.Context, oldObj, newObj *helmv1.HelmRepository, chartRepo *repository.ChartRepository, res sreconcile.Result, resErr error) { func (r *HelmRepositoryReconciler) notify(ctx context.Context, oldObj, newObj *sourcev1.HelmRepository, chartRepo *repository.ChartRepository, res sreconcile.Result, resErr error) {
// Notify successful reconciliation for new artifact and recovery from any // Notify successful reconciliation for new artifact and recovery from any
// failure. // failure.
if resErr == nil && res == sreconcile.ResultSuccess && newObj.Status.Artifact != nil { if resErr == nil && res == sreconcile.ResultSuccess && newObj.Status.Artifact != nil {
@ -331,7 +330,7 @@ func (r *HelmRepositoryReconciler) notify(ctx context.Context, oldObj, newObj *h
// The hostname of any URL in the Status of the object are updated, to ensure // The hostname of any URL in the Status of the object are updated, to ensure
// they match the Storage server hostname of current runtime. // they match the Storage server hostname of current runtime.
func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, sp *patch.SerialPatcher, func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, sp *patch.SerialPatcher,
obj *helmv1.HelmRepository, _ *sourcev1.Artifact, _ *repository.ChartRepository) (sreconcile.Result, error) { obj *sourcev1.HelmRepository, _ *sourcev1.Artifact, _ *repository.ChartRepository) (sreconcile.Result, error) {
// Garbage collect previous advertised artifact(s) from storage // Garbage collect previous advertised artifact(s) from storage
_ = r.garbageCollect(ctx, obj) _ = r.garbageCollect(ctx, obj)
@ -386,15 +385,15 @@ func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, sp *pat
} }
// reconcileSource attempts to fetch the Helm repository index using the // reconcileSource attempts to fetch the Helm repository index using the
// specified configuration on the v1beta2.HelmRepository object. // specified configuration on the v1.HelmRepository object.
// //
// When the fetch fails, it records v1beta2.FetchFailedCondition=True and // When the fetch fails, it records v1.FetchFailedCondition=True and
// returns early. // returns early.
// If successful and the index is valid, any previous // If successful and the index is valid, any previous
// v1beta2.FetchFailedCondition is removed, and the repository.ChartRepository // v1.FetchFailedCondition is removed, and the repository.ChartRepository
// pointer is set to the newly fetched index. // pointer is set to the newly fetched index.
func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch.SerialPatcher, func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch.SerialPatcher,
obj *helmv1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) { obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
// Ensure it's not an OCI URL. API validation ensures that only // Ensure it's not an OCI URL. API validation ensures that only
// http/https/oci scheme are allowed. // http/https/oci scheme are allowed.
if strings.HasPrefix(obj.Spec.URL, helmreg.OCIScheme) { if strings.HasPrefix(obj.Spec.URL, helmreg.OCIScheme) {
@ -483,7 +482,7 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patc
if err := chartRepo.LoadFromPath(); err != nil { if err := chartRepo.LoadFromPath(); err != nil {
e := serror.NewGeneric( e := serror.NewGeneric(
fmt.Errorf("failed to load Helm repository from index YAML: %w", err), fmt.Errorf("failed to load Helm repository from index YAML: %w", err),
helmv1.IndexationFailedReason, sourcev1.IndexationFailedReason,
) )
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error()) conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
return sreconcile.ResultEmpty, e return sreconcile.ResultEmpty, e
@ -496,7 +495,7 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patc
if revision.Validate() != nil { if revision.Validate() != nil {
e := serror.NewGeneric( e := serror.NewGeneric(
fmt.Errorf("failed to calculate revision: %w", err), fmt.Errorf("failed to calculate revision: %w", err),
helmv1.IndexationFailedReason, sourcev1.IndexationFailedReason,
) )
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error()) conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
return sreconcile.ResultEmpty, e return sreconcile.ResultEmpty, e
@ -526,12 +525,12 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patc
// (Status) data on the object does not match the given. // (Status) data on the object does not match the given.
// //
// The inspection of the given data to the object is differed, ensuring any // The inspection of the given data to the object is differed, ensuring any
// stale observations like v1beta2.ArtifactOutdatedCondition are removed. // stale observations like v1.ArtifactOutdatedCondition are removed.
// If the given Artifact does not differ from the object's current, it returns // If the given Artifact does not differ from the object's current, it returns
// early. // early.
// On a successful archive, the Artifact in the Status of the object is set, // On a successful archive, the Artifact in the Status of the object is set,
// and the symlink in the Storage is updated to its path. // and the symlink in the Storage is updated to its path.
func (r *HelmRepositoryReconciler) reconcileArtifact(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) { func (r *HelmRepositoryReconciler) reconcileArtifact(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
// Set the ArtifactInStorageCondition if there's no drift. // Set the ArtifactInStorageCondition if there's no drift.
defer func() { defer func() {
if obj.GetArtifact().HasRevision(artifact.Revision) { if obj.GetArtifact().HasRevision(artifact.Revision) {
@ -623,7 +622,7 @@ func (r *HelmRepositoryReconciler) reconcileArtifact(ctx context.Context, sp *pa
// reconcileDelete handles the deletion of the object. // reconcileDelete handles the deletion of the object.
// It first garbage collects all Artifacts for the object from the Storage. // It first garbage collects all Artifacts for the object from the Storage.
// Removing the finalizer from the object if successful. // Removing the finalizer from the object if successful.
func (r *HelmRepositoryReconciler) reconcileDelete(ctx context.Context, obj *helmv1.HelmRepository) (sreconcile.Result, error) { func (r *HelmRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.HelmRepository) (sreconcile.Result, error) {
// Garbage collect the resource's artifacts // Garbage collect the resource's artifacts
if err := r.garbageCollect(ctx, obj); err != nil { if err := r.garbageCollect(ctx, obj); err != nil {
// Return the error so we retry the failed garbage collection // Return the error so we retry the failed garbage collection
@ -651,8 +650,8 @@ func (r *HelmRepositoryReconciler) reconcileDelete(ctx context.Context, obj *hel
// - the deletion timestamp on the object is set // - the deletion timestamp on the object is set
// - the obj.Spec.Type has changed and artifacts are not supported by the new type // - the obj.Spec.Type has changed and artifacts are not supported by the new type
// Which will result in the removal of all Artifacts for the objects. // Which will result in the removal of all Artifacts for the objects.
func (r *HelmRepositoryReconciler) garbageCollect(ctx context.Context, obj *helmv1.HelmRepository) error { func (r *HelmRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.HelmRepository) error {
if !obj.DeletionTimestamp.IsZero() || (obj.Spec.Type != "" && obj.Spec.Type != helmv1.HelmRepositoryTypeDefault) { if !obj.DeletionTimestamp.IsZero() || (obj.Spec.Type != "" && obj.Spec.Type != sourcev1.HelmRepositoryTypeDefault) {
if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil { if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
return serror.NewGeneric( return serror.NewGeneric(
fmt.Errorf("garbage collection for deleted resource failed: %w", err), fmt.Errorf("garbage collection for deleted resource failed: %w", err),
@ -703,7 +702,7 @@ func (r *HelmRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Ob
} }
// migrateToStatic is HelmRepository OCI migration to static object. // migrateToStatic is HelmRepository OCI migration to static object.
func (r *HelmRepositoryReconciler) migrationToStatic(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmRepository) (result ctrl.Result, err error) { func (r *HelmRepositoryReconciler) migrationToStatic(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmRepository) (result ctrl.Result, err error) {
// Skip migration if suspended and not being deleted. // Skip migration if suspended and not being deleted.
if obj.Spec.Suspend && obj.DeletionTimestamp.IsZero() { if obj.Spec.Suspend && obj.DeletionTimestamp.IsZero() {
return ctrl.Result{}, nil return ctrl.Result{}, nil
@ -721,7 +720,7 @@ func (r *HelmRepositoryReconciler) migrationToStatic(ctx context.Context, sp *pa
} }
// Delete finalizer and reset the status. // Delete finalizer and reset the status.
controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer) controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer)
obj.Status = helmv1.HelmRepositoryStatus{} obj.Status = sourcev1.HelmRepositoryStatus{}
if err := sp.Patch(ctx, obj); err != nil { if err := sp.Patch(ctx, obj); err != nil {
return ctrl.Result{}, err return ctrl.Result{}, err

View File

@ -50,7 +50,6 @@ import (
"github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/runtime/patch"
sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
helmv1 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/fluxcd/source-controller/internal/cache" "github.com/fluxcd/source-controller/internal/cache"
intdigest "github.com/fluxcd/source-controller/internal/digest" intdigest "github.com/fluxcd/source-controller/internal/digest"
"github.com/fluxcd/source-controller/internal/helm/getter" "github.com/fluxcd/source-controller/internal/helm/getter"
@ -73,10 +72,10 @@ func TestHelmRepositoryReconciler_deleteBeforeFinalizer(t *testing.T) {
g.Expect(k8sClient.Delete(ctx, namespace)).NotTo(HaveOccurred()) g.Expect(k8sClient.Delete(ctx, namespace)).NotTo(HaveOccurred())
}) })
helmrepo := &helmv1.HelmRepository{} helmrepo := &sourcev1.HelmRepository{}
helmrepo.Name = "test-helmrepo" helmrepo.Name = "test-helmrepo"
helmrepo.Namespace = namespaceName helmrepo.Namespace = namespaceName
helmrepo.Spec = helmv1.HelmRepositorySpec{ helmrepo.Spec = sourcev1.HelmRepositorySpec{
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
URL: "https://example.com", URL: "https://example.com",
} }
@ -109,12 +108,12 @@ func TestHelmRepositoryReconciler_Reconcile(t *testing.T) {
testServer.Start() testServer.Start()
defer testServer.Stop() defer testServer.Stop()
origObj := &helmv1.HelmRepository{ origObj := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: "helmrepository-reconcile-", GenerateName: "helmrepository-reconcile-",
Namespace: "default", Namespace: "default",
}, },
Spec: helmv1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
URL: testServer.URL(), URL: testServer.URL(),
}, },
@ -175,7 +174,7 @@ func TestHelmRepositoryReconciler_Reconcile(t *testing.T) {
func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) { func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
beforeFunc func(obj *helmv1.HelmRepository, storage *Storage) error beforeFunc func(obj *sourcev1.HelmRepository, storage *Storage) error
want sreconcile.Result want sreconcile.Result
wantErr bool wantErr bool
assertArtifact *sourcev1.Artifact assertArtifact *sourcev1.Artifact
@ -184,7 +183,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
}{ }{
{ {
name: "garbage collects", name: "garbage collects",
beforeFunc: func(obj *helmv1.HelmRepository, storage *Storage) error { beforeFunc: func(obj *sourcev1.HelmRepository, storage *Storage) error {
revisions := []string{"a", "b", "c", "d"} revisions := []string{"a", "b", "c", "d"}
for n := range revisions { for n := range revisions {
v := revisions[n] v := revisions[n]
@ -234,7 +233,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
}, },
{ {
name: "notices missing artifact in storage", name: "notices missing artifact in storage",
beforeFunc: func(obj *helmv1.HelmRepository, storage *Storage) error { beforeFunc: func(obj *sourcev1.HelmRepository, storage *Storage) error {
obj.Status.Artifact = &sourcev1.Artifact{ obj.Status.Artifact = &sourcev1.Artifact{
Path: "/reconcile-storage/invalid.txt", Path: "/reconcile-storage/invalid.txt",
Revision: "d", Revision: "d",
@ -253,7 +252,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
}, },
{ {
name: "notices empty artifact digest", name: "notices empty artifact digest",
beforeFunc: func(obj *helmv1.HelmRepository, storage *Storage) error { beforeFunc: func(obj *sourcev1.HelmRepository, storage *Storage) error {
f := "empty-digest.txt" f := "empty-digest.txt"
obj.Status.Artifact = &sourcev1.Artifact{ obj.Status.Artifact = &sourcev1.Artifact{
@ -284,7 +283,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
}, },
{ {
name: "notices artifact digest mismatch", name: "notices artifact digest mismatch",
beforeFunc: func(obj *helmv1.HelmRepository, storage *Storage) error { beforeFunc: func(obj *sourcev1.HelmRepository, storage *Storage) error {
f := "digest-mismatch.txt" f := "digest-mismatch.txt"
obj.Status.Artifact = &sourcev1.Artifact{ obj.Status.Artifact = &sourcev1.Artifact{
@ -315,7 +314,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
}, },
{ {
name: "updates hostname on diff from current", name: "updates hostname on diff from current",
beforeFunc: func(obj *helmv1.HelmRepository, storage *Storage) error { beforeFunc: func(obj *sourcev1.HelmRepository, storage *Storage) error {
obj.Status.Artifact = &sourcev1.Artifact{ obj.Status.Artifact = &sourcev1.Artifact{
Path: "/reconcile-storage/hostname.txt", Path: "/reconcile-storage/hostname.txt",
Revision: "f", Revision: "f",
@ -354,14 +353,14 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
r := &HelmRepositoryReconciler{ r := &HelmRepositoryReconciler{
Client: fakeclient.NewClientBuilder(). Client: fakeclient.NewClientBuilder().
WithScheme(testEnv.GetScheme()). WithScheme(testEnv.GetScheme()).
WithStatusSubresource(&helmv1.HelmRepository{}). WithStatusSubresource(&sourcev1.HelmRepository{}).
Build(), Build(),
EventRecorder: record.NewFakeRecorder(32), EventRecorder: record.NewFakeRecorder(32),
Storage: testStorage, Storage: testStorage,
patchOptions: getPatchOptions(helmRepositoryReadyCondition.Owned, "sc"), patchOptions: getPatchOptions(helmRepositoryReadyCondition.Owned, "sc"),
} }
obj := &helmv1.HelmRepository{ obj := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-", GenerateName: "test-",
Generation: 1, Generation: 1,
@ -421,8 +420,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
server options server options
url string url string
secret *corev1.Secret secret *corev1.Secret
beforeFunc func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) beforeFunc func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest)
afterFunc func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) afterFunc func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository)
want sreconcile.Result want sreconcile.Result
wantErr bool wantErr bool
assertConditions []metav1.Condition assertConditions []metav1.Condition
@ -440,7 +439,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
"ca.crt": tlsCA, "ca.crt": tlsCA,
}, },
}, },
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.CertSecretRef = &meta.LocalObjectReference{Name: "ca-file"} obj.Spec.CertSecretRef = &meta.LocalObjectReference{Name: "ca-file"}
}, },
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
@ -464,7 +463,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
"ca.crt": tlsCA, "ca.crt": tlsCA,
}, },
}, },
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.CertSecretRef = &meta.LocalObjectReference{Name: "ca-file"} obj.Spec.CertSecretRef = &meta.LocalObjectReference{Name: "ca-file"}
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
@ -472,7 +471,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
t.Expect(chartRepo.Path).ToNot(BeEmpty()) t.Expect(chartRepo.Path).ToNot(BeEmpty())
t.Expect(chartRepo.Index).ToNot(BeNil()) t.Expect(chartRepo.Index).ToNot(BeNil())
t.Expect(artifact.Revision).ToNot(BeEmpty()) t.Expect(artifact.Revision).ToNot(BeEmpty())
@ -494,7 +493,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
"caFile": tlsCA, "caFile": tlsCA,
}, },
}, },
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "ca-file"} obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "ca-file"}
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
@ -502,7 +501,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
t.Expect(chartRepo.Path).ToNot(BeEmpty()) t.Expect(chartRepo.Path).ToNot(BeEmpty())
t.Expect(chartRepo.Index).ToNot(BeNil()) t.Expect(chartRepo.Index).ToNot(BeNil())
t.Expect(artifact.Revision).ToNot(BeEmpty()) t.Expect(artifact.Revision).ToNot(BeEmpty())
@ -526,7 +525,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
}, },
Type: corev1.SecretTypeDockerConfigJson, Type: corev1.SecretTypeDockerConfigJson,
}, },
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "ca-file"} obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "ca-file"}
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
@ -534,7 +533,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
t.Expect(chartRepo.Path).ToNot(BeEmpty()) t.Expect(chartRepo.Path).ToNot(BeEmpty())
t.Expect(chartRepo.Index).ToNot(BeNil()) t.Expect(chartRepo.Index).ToNot(BeNil())
t.Expect(artifact.Revision).ToNot(BeEmpty()) t.Expect(artifact.Revision).ToNot(BeEmpty())
@ -548,7 +547,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
t.Expect(chartRepo.Path).ToNot(BeEmpty()) t.Expect(chartRepo.Path).ToNot(BeEmpty())
t.Expect(chartRepo.Index).ToNot(BeNil()) t.Expect(chartRepo.Index).ToNot(BeNil())
t.Expect(artifact.Revision).ToNot(BeEmpty()) t.Expect(artifact.Revision).ToNot(BeEmpty())
@ -570,7 +569,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
"password": []byte("1234"), "password": []byte("1234"),
}, },
}, },
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "basic-auth"} obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "basic-auth"}
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
@ -578,7 +577,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
t.Expect(chartRepo.Path).ToNot(BeEmpty()) t.Expect(chartRepo.Path).ToNot(BeEmpty())
t.Expect(chartRepo.Index).ToNot(BeNil()) t.Expect(chartRepo.Index).ToNot(BeNil())
t.Expect(artifact.Revision).ToNot(BeEmpty()) t.Expect(artifact.Revision).ToNot(BeEmpty())
@ -602,7 +601,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
}, },
Type: corev1.SecretTypeDockerConfigJson, Type: corev1.SecretTypeDockerConfigJson,
}, },
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "basic-auth"} obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "basic-auth"}
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
@ -610,7 +609,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
t.Expect(chartRepo.Path).ToNot(BeEmpty()) t.Expect(chartRepo.Path).ToNot(BeEmpty())
t.Expect(chartRepo.Index).ToNot(BeNil()) t.Expect(chartRepo.Index).ToNot(BeNil())
t.Expect(artifact.Revision).ToNot(BeEmpty()) t.Expect(artifact.Revision).ToNot(BeEmpty())
@ -632,7 +631,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
"ca.crt": []byte("invalid"), "ca.crt": []byte("invalid"),
}, },
}, },
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.CertSecretRef = &meta.LocalObjectReference{Name: "invalid-ca"} obj.Spec.CertSecretRef = &meta.LocalObjectReference{Name: "invalid-ca"}
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo") conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar") conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
@ -643,7 +642,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"), *conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
// No repo index due to fetch fail. // No repo index due to fetch fail.
t.Expect(chartRepo.Path).To(BeEmpty()) t.Expect(chartRepo.Path).To(BeEmpty())
t.Expect(chartRepo.Index).To(BeNil()) t.Expect(chartRepo.Index).To(BeNil())
@ -653,7 +652,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
{ {
name: "Invalid URL makes FetchFailed=True and returns stalling error", name: "Invalid URL makes FetchFailed=True and returns stalling error",
protocol: "http", protocol: "http",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.URL = strings.ReplaceAll(obj.Spec.URL, "http://", "") obj.Spec.URL = strings.ReplaceAll(obj.Spec.URL, "http://", "")
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo") conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar") conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
@ -665,7 +664,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"), *conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
// No repo index due to fetch fail. // No repo index due to fetch fail.
t.Expect(chartRepo.Path).To(BeEmpty()) t.Expect(chartRepo.Path).To(BeEmpty())
t.Expect(chartRepo.Index).To(BeNil()) t.Expect(chartRepo.Index).To(BeNil())
@ -675,7 +674,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
{ {
name: "Unsupported scheme makes FetchFailed=True and returns stalling error", name: "Unsupported scheme makes FetchFailed=True and returns stalling error",
protocol: "http", protocol: "http",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.URL = strings.ReplaceAll(obj.Spec.URL, "http://", "ftp://") obj.Spec.URL = strings.ReplaceAll(obj.Spec.URL, "http://", "ftp://")
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo") conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar") conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
@ -687,7 +686,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"), *conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
// No repo index due to fetch fail. // No repo index due to fetch fail.
t.Expect(chartRepo.Path).To(BeEmpty()) t.Expect(chartRepo.Path).To(BeEmpty())
t.Expect(chartRepo.Index).To(BeNil()) t.Expect(chartRepo.Index).To(BeNil())
@ -697,7 +696,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
{ {
name: "Missing secret returns FetchFailed=True and returns error", name: "Missing secret returns FetchFailed=True and returns error",
protocol: "http", protocol: "http",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "non-existing"} obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "non-existing"}
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo") conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar") conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
@ -708,7 +707,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"), *conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
// No repo index due to fetch fail. // No repo index due to fetch fail.
t.Expect(chartRepo.Path).To(BeEmpty()) t.Expect(chartRepo.Path).To(BeEmpty())
t.Expect(chartRepo.Index).To(BeNil()) t.Expect(chartRepo.Index).To(BeNil())
@ -726,7 +725,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
"username": []byte("git"), "username": []byte("git"),
}, },
}, },
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "malformed-basic-auth"} obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "malformed-basic-auth"}
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo") conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar") conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
@ -737,7 +736,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"), *conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
// No repo index due to fetch fail. // No repo index due to fetch fail.
t.Expect(chartRepo.Path).To(BeEmpty()) t.Expect(chartRepo.Path).To(BeEmpty())
t.Expect(chartRepo.Index).To(BeNil()) t.Expect(chartRepo.Index).To(BeNil())
@ -747,7 +746,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
{ {
name: "Stored index with same revision", name: "Stored index with same revision",
protocol: "http", protocol: "http",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Status.Artifact = &sourcev1.Artifact{ obj.Status.Artifact = &sourcev1.Artifact{
Revision: rev.String(), Revision: rev.String(),
} }
@ -760,7 +759,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"), *conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
t.Expect(chartRepo.Path).ToNot(BeEmpty()) t.Expect(chartRepo.Path).ToNot(BeEmpty())
t.Expect(chartRepo.Index).To(BeNil()) t.Expect(chartRepo.Index).To(BeNil())
@ -771,7 +770,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
{ {
name: "Stored index with different revision", name: "Stored index with different revision",
protocol: "http", protocol: "http",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Status.Artifact = &sourcev1.Artifact{ obj.Status.Artifact = &sourcev1.Artifact{
Revision: "80bb3dd67c63095d985850459834ea727603727a370079de90d221191d375a86", Revision: "80bb3dd67c63095d985850459834ea727603727a370079de90d221191d375a86",
} }
@ -784,7 +783,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"),
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"),
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
t.Expect(chartRepo.Path).ToNot(BeEmpty()) t.Expect(chartRepo.Path).ToNot(BeEmpty())
t.Expect(chartRepo.Index).ToNot(BeNil()) t.Expect(chartRepo.Index).ToNot(BeNil())
@ -796,7 +795,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
{ {
name: "Existing artifact makes ArtifactOutdated=True", name: "Existing artifact makes ArtifactOutdated=True",
protocol: "http", protocol: "http",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, rev digest.Digest) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, rev digest.Digest) {
obj.Status.Artifact = &sourcev1.Artifact{ obj.Status.Artifact = &sourcev1.Artifact{
Path: "some-path", Path: "some-path",
Revision: "some-rev", Revision: "some-rev",
@ -812,12 +811,12 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
obj := &helmv1.HelmRepository{ obj := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: "auth-strategy-", GenerateName: "auth-strategy-",
Generation: 1, Generation: 1,
}, },
Spec: helmv1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
Timeout: &metav1.Duration{Duration: timeout}, Timeout: &metav1.Duration{Duration: timeout},
}, },
@ -868,7 +867,7 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
clientBuilder := fakeclient.NewClientBuilder(). clientBuilder := fakeclient.NewClientBuilder().
WithScheme(testEnv.GetScheme()). WithScheme(testEnv.GetScheme()).
WithStatusSubresource(&helmv1.HelmRepository{}) WithStatusSubresource(&sourcev1.HelmRepository{})
if secret != nil { if secret != nil {
clientBuilder.WithObjects(secret.DeepCopy()) clientBuilder.WithObjects(secret.DeepCopy())
@ -960,19 +959,19 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
cache *cache.Cache cache *cache.Cache
beforeFunc func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) beforeFunc func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository)
afterFunc func(t *WithT, obj *helmv1.HelmRepository, cache *cache.Cache) afterFunc func(t *WithT, obj *sourcev1.HelmRepository, cache *cache.Cache)
want sreconcile.Result want sreconcile.Result
wantErr bool wantErr bool
assertConditions []metav1.Condition assertConditions []metav1.Condition
}{ }{
{ {
name: "Archiving artifact to storage makes ArtifactInStorage=True and artifact is stored as JSON", name: "Archiving artifact to storage makes ArtifactInStorage=True and artifact is stored as JSON",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) {
obj.Spec.Interval = metav1.Duration{Duration: interval} obj.Spec.Interval = metav1.Duration{Duration: interval}
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, cache *cache.Cache) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, cache *cache.Cache) {
localPath := testStorage.LocalPath(*obj.GetArtifact()) localPath := testStorage.LocalPath(*obj.GetArtifact())
b, err := os.ReadFile(localPath) b, err := os.ReadFile(localPath)
t.Expect(err).To(Not(HaveOccurred())) t.Expect(err).To(Not(HaveOccurred()))
@ -985,7 +984,7 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
{ {
name: "Archiving (loaded) artifact to storage adds to cache", name: "Archiving (loaded) artifact to storage adds to cache",
cache: cache.New(10, time.Minute), cache: cache.New(10, time.Minute),
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) {
index.Index = &repo.IndexFile{ index.Index = &repo.IndexFile{
APIVersion: "v1", APIVersion: "v1",
Generated: time.Now(), Generated: time.Now(),
@ -993,7 +992,7 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
obj.Spec.Interval = metav1.Duration{Duration: interval} obj.Spec.Interval = metav1.Duration{Duration: interval}
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, cache *cache.Cache) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, cache *cache.Cache) {
i, ok := cache.Get(obj.GetArtifact().Path) i, ok := cache.Get(obj.GetArtifact().Path)
t.Expect(ok).To(BeTrue()) t.Expect(ok).To(BeTrue())
t.Expect(i).To(BeAssignableToTypeOf(&repo.IndexFile{})) t.Expect(i).To(BeAssignableToTypeOf(&repo.IndexFile{}))
@ -1004,11 +1003,11 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
}, },
{ {
name: "Up-to-date artifact should not update status", name: "Up-to-date artifact should not update status",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) {
obj.Spec.Interval = metav1.Duration{Duration: interval} obj.Spec.Interval = metav1.Duration{Duration: interval}
obj.Status.Artifact = artifact.DeepCopy() obj.Status.Artifact = artifact.DeepCopy()
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, _ *cache.Cache) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, _ *cache.Cache) {
t.Expect(obj.Status.URL).To(BeEmpty()) t.Expect(obj.Status.URL).To(BeEmpty())
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
@ -1018,7 +1017,7 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
}, },
{ {
name: "Removes ArtifactOutdatedCondition after creating a new artifact", name: "Removes ArtifactOutdatedCondition after creating a new artifact",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) {
obj.Spec.Interval = metav1.Duration{Duration: interval} obj.Spec.Interval = metav1.Duration{Duration: interval}
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "Foo", "") conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "Foo", "")
}, },
@ -1029,10 +1028,10 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
}, },
{ {
name: "Creates latest symlink to the created artifact", name: "Creates latest symlink to the created artifact",
beforeFunc: func(t *WithT, obj *helmv1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) { beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, index *repository.ChartRepository) {
obj.Spec.Interval = metav1.Duration{Duration: interval} obj.Spec.Interval = metav1.Duration{Duration: interval}
}, },
afterFunc: func(t *WithT, obj *helmv1.HelmRepository, _ *cache.Cache) { afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, _ *cache.Cache) {
localPath := testStorage.LocalPath(*obj.GetArtifact()) localPath := testStorage.LocalPath(*obj.GetArtifact())
symlinkPath := filepath.Join(filepath.Dir(localPath), "index.yaml") symlinkPath := filepath.Join(filepath.Dir(localPath), "index.yaml")
targetFile, err := os.Readlink(symlinkPath) targetFile, err := os.Readlink(symlinkPath)
@ -1053,7 +1052,7 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
r := &HelmRepositoryReconciler{ r := &HelmRepositoryReconciler{
Client: fakeclient.NewClientBuilder(). Client: fakeclient.NewClientBuilder().
WithScheme(testEnv.GetScheme()). WithScheme(testEnv.GetScheme()).
WithStatusSubresource(&helmv1.HelmRepository{}). WithStatusSubresource(&sourcev1.HelmRepository{}).
Build(), Build(),
EventRecorder: record.NewFakeRecorder(32), EventRecorder: record.NewFakeRecorder(32),
Storage: testStorage, Storage: testStorage,
@ -1062,16 +1061,16 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
patchOptions: getPatchOptions(helmRepositoryReadyCondition.Owned, "sc"), patchOptions: getPatchOptions(helmRepositoryReadyCondition.Owned, "sc"),
} }
obj := &helmv1.HelmRepository{ obj := &sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: helmv1.HelmRepositoryKind, Kind: sourcev1.HelmRepositoryKind,
}, },
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-bucket-", GenerateName: "test-bucket-",
Generation: 1, Generation: 1,
Namespace: "default", Namespace: "default",
}, },
Spec: helmv1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
Timeout: &metav1.Duration{Duration: timeout}, Timeout: &metav1.Duration{Duration: timeout},
URL: "https://example.com/index.yaml", URL: "https://example.com/index.yaml",
}, },
@ -1111,7 +1110,7 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) { func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
// Helper to build simple helmRepositoryReconcileFunc with result and error. // Helper to build simple helmRepositoryReconcileFunc with result and error.
buildReconcileFuncs := func(r sreconcile.Result, e error) helmRepositoryReconcileFunc { buildReconcileFuncs := func(r sreconcile.Result, e error) helmRepositoryReconcileFunc {
return func(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) { return func(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) {
return r, e return r, e
} }
} }
@ -1166,11 +1165,11 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
{ {
name: "multiple object status conditions mutations", name: "multiple object status conditions mutations",
reconcileFuncs: []helmRepositoryReconcileFunc{ reconcileFuncs: []helmRepositoryReconcileFunc{
func(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) { func(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) {
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "new index revision") conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "new index revision")
return sreconcile.ResultSuccess, nil return sreconcile.ResultSuccess, nil
}, },
func(ctx context.Context, sp *patch.SerialPatcher, obj *helmv1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) { func(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) {
conditions.MarkTrue(obj, meta.ReconcilingCondition, meta.ProgressingReason, "creating artifact") conditions.MarkTrue(obj, meta.ReconcilingCondition, meta.ProgressingReason, "creating artifact")
return sreconcile.ResultSuccess, nil return sreconcile.ResultSuccess, nil
}, },
@ -1220,16 +1219,16 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
r := &HelmRepositoryReconciler{ r := &HelmRepositoryReconciler{
Client: fakeclient.NewClientBuilder(). Client: fakeclient.NewClientBuilder().
WithScheme(testEnv.GetScheme()). WithScheme(testEnv.GetScheme()).
WithStatusSubresource(&helmv1.HelmRepository{}). WithStatusSubresource(&sourcev1.HelmRepository{}).
Build(), Build(),
patchOptions: getPatchOptions(helmRepositoryReadyCondition.Owned, "sc"), patchOptions: getPatchOptions(helmRepositoryReadyCondition.Owned, "sc"),
} }
obj := &helmv1.HelmRepository{ obj := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-", GenerateName: "test-",
Generation: tt.generation, Generation: tt.generation,
}, },
Status: helmv1.HelmRepositoryStatus{ Status: sourcev1.HelmRepositoryStatus{
ObservedGeneration: tt.observedGeneration, ObservedGeneration: tt.observedGeneration,
}, },
} }
@ -1254,13 +1253,13 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
func TestHelmRepositoryReconciler_statusConditions(t *testing.T) { func TestHelmRepositoryReconciler_statusConditions(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
beforeFunc func(obj *helmv1.HelmRepository) beforeFunc func(obj *sourcev1.HelmRepository)
assertConditions []metav1.Condition assertConditions []metav1.Condition
wantErr bool wantErr bool
}{ }{
{ {
name: "positive conditions only", name: "positive conditions only",
beforeFunc: func(obj *helmv1.HelmRepository) { beforeFunc: func(obj *sourcev1.HelmRepository) {
conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason, "stored artifact for revision") conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason, "stored artifact for revision")
}, },
assertConditions: []metav1.Condition{ assertConditions: []metav1.Condition{
@ -1271,7 +1270,7 @@ func TestHelmRepositoryReconciler_statusConditions(t *testing.T) {
}, },
{ {
name: "multiple failures", name: "multiple failures",
beforeFunc: func(obj *helmv1.HelmRepository) { beforeFunc: func(obj *sourcev1.HelmRepository) {
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret") conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret")
conditions.MarkTrue(obj, sourcev1.StorageOperationFailedCondition, sourcev1.DirCreationFailedReason, "failed to create directory") conditions.MarkTrue(obj, sourcev1.StorageOperationFailedCondition, sourcev1.DirCreationFailedReason, "failed to create directory")
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "some error") conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "some error")
@ -1286,7 +1285,7 @@ func TestHelmRepositoryReconciler_statusConditions(t *testing.T) {
}, },
{ {
name: "mixed positive and negative conditions", name: "mixed positive and negative conditions",
beforeFunc: func(obj *helmv1.HelmRepository) { beforeFunc: func(obj *sourcev1.HelmRepository) {
conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason, "stored artifact for revision") conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason, "stored artifact for revision")
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret") conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret")
}, },
@ -1303,10 +1302,10 @@ func TestHelmRepositoryReconciler_statusConditions(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
obj := &helmv1.HelmRepository{ obj := &sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: helmv1.HelmRepositoryKind, Kind: sourcev1.HelmRepositoryKind,
APIVersion: helmv1.GroupVersion.String(), APIVersion: sourcev1.GroupVersion.String(),
}, },
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "helmrepo", Name: "helmrepo",
@ -1316,7 +1315,7 @@ func TestHelmRepositoryReconciler_statusConditions(t *testing.T) {
clientBuilder := fakeclient.NewClientBuilder(). clientBuilder := fakeclient.NewClientBuilder().
WithObjects(obj). WithObjects(obj).
WithStatusSubresource(&helmv1.HelmRepository{}) WithStatusSubresource(&sourcev1.HelmRepository{})
c := clientBuilder.Build() c := clientBuilder.Build()
@ -1351,8 +1350,8 @@ func TestHelmRepositoryReconciler_notify(t *testing.T) {
name string name string
res sreconcile.Result res sreconcile.Result
resErr error resErr error
oldObjBeforeFunc func(obj *helmv1.HelmRepository) oldObjBeforeFunc func(obj *sourcev1.HelmRepository)
newObjBeforeFunc func(obj *helmv1.HelmRepository) newObjBeforeFunc func(obj *sourcev1.HelmRepository)
wantEvent string wantEvent string
}{ }{
{ {
@ -1364,7 +1363,7 @@ func TestHelmRepositoryReconciler_notify(t *testing.T) {
name: "new artifact with nil size", name: "new artifact with nil size",
res: sreconcile.ResultSuccess, res: sreconcile.ResultSuccess,
resErr: nil, resErr: nil,
newObjBeforeFunc: func(obj *helmv1.HelmRepository) { newObjBeforeFunc: func(obj *sourcev1.HelmRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: nil} obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: nil}
}, },
wantEvent: "Normal NewArtifact stored fetched index of unknown size", wantEvent: "Normal NewArtifact stored fetched index of unknown size",
@ -1373,7 +1372,7 @@ func TestHelmRepositoryReconciler_notify(t *testing.T) {
name: "new artifact", name: "new artifact",
res: sreconcile.ResultSuccess, res: sreconcile.ResultSuccess,
resErr: nil, resErr: nil,
newObjBeforeFunc: func(obj *helmv1.HelmRepository) { newObjBeforeFunc: func(obj *sourcev1.HelmRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize} obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize}
}, },
wantEvent: "Normal NewArtifact stored fetched index of size", wantEvent: "Normal NewArtifact stored fetched index of size",
@ -1382,12 +1381,12 @@ func TestHelmRepositoryReconciler_notify(t *testing.T) {
name: "recovery from failure", name: "recovery from failure",
res: sreconcile.ResultSuccess, res: sreconcile.ResultSuccess,
resErr: nil, resErr: nil,
oldObjBeforeFunc: func(obj *helmv1.HelmRepository) { oldObjBeforeFunc: func(obj *sourcev1.HelmRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize} obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize}
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, "fail") conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, "fail")
conditions.MarkFalse(obj, meta.ReadyCondition, meta.FailedReason, "foo") conditions.MarkFalse(obj, meta.ReadyCondition, meta.FailedReason, "foo")
}, },
newObjBeforeFunc: func(obj *helmv1.HelmRepository) { newObjBeforeFunc: func(obj *sourcev1.HelmRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize} obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize}
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready") conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready")
}, },
@ -1397,12 +1396,12 @@ func TestHelmRepositoryReconciler_notify(t *testing.T) {
name: "recovery and new artifact", name: "recovery and new artifact",
res: sreconcile.ResultSuccess, res: sreconcile.ResultSuccess,
resErr: nil, resErr: nil,
oldObjBeforeFunc: func(obj *helmv1.HelmRepository) { oldObjBeforeFunc: func(obj *sourcev1.HelmRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize} obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize}
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, "fail") conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, "fail")
conditions.MarkFalse(obj, meta.ReadyCondition, meta.FailedReason, "foo") conditions.MarkFalse(obj, meta.ReadyCondition, meta.FailedReason, "foo")
}, },
newObjBeforeFunc: func(obj *helmv1.HelmRepository) { newObjBeforeFunc: func(obj *sourcev1.HelmRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: "aaa", Digest: "bbb", Size: &aSize} obj.Status.Artifact = &sourcev1.Artifact{Revision: "aaa", Digest: "bbb", Size: &aSize}
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready") conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready")
}, },
@ -1412,11 +1411,11 @@ func TestHelmRepositoryReconciler_notify(t *testing.T) {
name: "no updates", name: "no updates",
res: sreconcile.ResultSuccess, res: sreconcile.ResultSuccess,
resErr: nil, resErr: nil,
oldObjBeforeFunc: func(obj *helmv1.HelmRepository) { oldObjBeforeFunc: func(obj *sourcev1.HelmRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize} obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize}
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready") conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready")
}, },
newObjBeforeFunc: func(obj *helmv1.HelmRepository) { newObjBeforeFunc: func(obj *sourcev1.HelmRepository) {
obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize} obj.Status.Artifact = &sourcev1.Artifact{Revision: "xxx", Digest: "yyy", Size: &aSize}
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready") conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready")
}, },
@ -1428,7 +1427,7 @@ func TestHelmRepositoryReconciler_notify(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
recorder := record.NewFakeRecorder(32) recorder := record.NewFakeRecorder(32)
oldObj := &helmv1.HelmRepository{} oldObj := &sourcev1.HelmRepository{}
newObj := oldObj.DeepCopy() newObj := oldObj.DeepCopy()
if tt.oldObjBeforeFunc != nil { if tt.oldObjBeforeFunc != nil {
@ -1475,12 +1474,12 @@ func TestHelmRepositoryReconciler_ReconcileTypeUpdatePredicateFilter(t *testing.
testServer.Start() testServer.Start()
defer testServer.Stop() defer testServer.Stop()
obj := &helmv1.HelmRepository{ obj := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: "helmrepository-reconcile-", GenerateName: "helmrepository-reconcile-",
Namespace: "default", Namespace: "default",
}, },
Spec: helmv1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
URL: testServer.URL(), URL: testServer.URL(),
}, },
@ -1524,7 +1523,7 @@ func TestHelmRepositoryReconciler_ReconcileTypeUpdatePredicateFilter(t *testing.
g.Expect(res.Status).To(Equal(kstatus.CurrentStatus)) g.Expect(res.Status).To(Equal(kstatus.CurrentStatus))
// Switch to a OCI helm repository type // Switch to a OCI helm repository type
obj.Spec.Type = helmv1.HelmRepositoryTypeOCI obj.Spec.Type = sourcev1.HelmRepositoryTypeOCI
obj.Spec.URL = fmt.Sprintf("oci://%s", testRegistryServer.registryHost) obj.Spec.URL = fmt.Sprintf("oci://%s", testRegistryServer.registryHost)
oldGen := obj.GetGeneration() oldGen := obj.GetGeneration()
@ -1564,12 +1563,12 @@ func TestHelmRepositoryReconciler_ReconcileSpecUpdatePredicateFilter(t *testing.
testServer.Start() testServer.Start()
defer testServer.Stop() defer testServer.Stop()
obj := &helmv1.HelmRepository{ obj := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: "helmrepository-reconcile-", GenerateName: "helmrepository-reconcile-",
Namespace: "default", Namespace: "default",
}, },
Spec: helmv1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
URL: testServer.URL(), URL: testServer.URL(),
}, },
@ -1666,12 +1665,12 @@ func TestHelmRepositoryReconciler_InMemoryCaching(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }()
helmRepo := &helmv1.HelmRepository{ helmRepo := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: "helmrepository-", GenerateName: "helmrepository-",
Namespace: ns.Name, Namespace: ns.Name,
}, },
Spec: helmv1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
URL: testServer.URL(), URL: testServer.URL(),
}, },
} }
@ -1725,7 +1724,7 @@ func TestHelmRepositoryReconciler_ociMigration(t *testing.T) {
g.Expect(testEnv.Cleanup(ctx, testns)).ToNot(HaveOccurred()) g.Expect(testEnv.Cleanup(ctx, testns)).ToNot(HaveOccurred())
}) })
hr := &helmv1.HelmRepository{ hr := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("hr-%s", randStringRunes(5)), Name: fmt.Sprintf("hr-%s", randStringRunes(5)),
Namespace: testns.Name, Namespace: testns.Name,
@ -1736,8 +1735,8 @@ func TestHelmRepositoryReconciler_ociMigration(t *testing.T) {
// Migrates newly created object with finalizer. // Migrates newly created object with finalizer.
hr.ObjectMeta.Finalizers = append(hr.ObjectMeta.Finalizers, "foo.bar", sourcev1.SourceFinalizer) hr.ObjectMeta.Finalizers = append(hr.ObjectMeta.Finalizers, "foo.bar", sourcev1.SourceFinalizer)
hr.Spec = helmv1.HelmRepositorySpec{ hr.Spec = sourcev1.HelmRepositorySpec{
Type: helmv1.HelmRepositoryTypeOCI, Type: sourcev1.HelmRepositoryTypeOCI,
URL: "oci://foo/bar", URL: "oci://foo/bar",
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
} }

View File

@ -1393,7 +1393,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureNotation(t *testi
}, },
Spec: ociv1.OCIRepositorySpec{ Spec: ociv1.OCIRepositorySpec{
URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost), URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost),
Verify: &ociv1.OCIRepositoryVerification{ Verify: &sourcev1.OCIRepositoryVerification{
Provider: "notation", Provider: "notation",
}, },
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
@ -1713,7 +1713,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceTrustPolicyNotation(t *tes
}, },
Spec: ociv1.OCIRepositorySpec{ Spec: ociv1.OCIRepositorySpec{
URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost), URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost),
Verify: &ociv1.OCIRepositoryVerification{ Verify: &sourcev1.OCIRepositoryVerification{
Provider: "notation", Provider: "notation",
}, },
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
@ -2037,7 +2037,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureCosign(t *testing
}, },
Spec: ociv1.OCIRepositorySpec{ Spec: ociv1.OCIRepositorySpec{
URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost), URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost),
Verify: &ociv1.OCIRepositoryVerification{ Verify: &sourcev1.OCIRepositoryVerification{
Provider: "cosign", Provider: "cosign",
}, },
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},
@ -2159,7 +2159,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature_keyless(t *testi
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
beforeFunc: func(obj *ociv1.OCIRepository) { beforeFunc: func(obj *ociv1.OCIRepository) {
obj.Spec.Verify.MatchOIDCIdentity = []ociv1.OIDCIdentityMatch{ obj.Spec.Verify.MatchOIDCIdentity = []sourcev1.OIDCIdentityMatch{
{ {
Subject: "^https://github.com/stefanprodan/podinfo.*$", Subject: "^https://github.com/stefanprodan/podinfo.*$",
@ -2181,7 +2181,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature_keyless(t *testi
}, },
want: sreconcile.ResultSuccess, want: sreconcile.ResultSuccess,
beforeFunc: func(obj *ociv1.OCIRepository) { beforeFunc: func(obj *ociv1.OCIRepository) {
obj.Spec.Verify.MatchOIDCIdentity = []ociv1.OIDCIdentityMatch{ obj.Spec.Verify.MatchOIDCIdentity = []sourcev1.OIDCIdentityMatch{
{ {
Subject: "intruder", Subject: "intruder",
Issuer: "^https://honeypot.com$", Issuer: "^https://honeypot.com$",
@ -2208,7 +2208,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature_keyless(t *testi
wantErr: true, wantErr: true,
want: sreconcile.ResultEmpty, want: sreconcile.ResultEmpty,
beforeFunc: func(obj *ociv1.OCIRepository) { beforeFunc: func(obj *ociv1.OCIRepository) {
obj.Spec.Verify.MatchOIDCIdentity = []ociv1.OIDCIdentityMatch{ obj.Spec.Verify.MatchOIDCIdentity = []sourcev1.OIDCIdentityMatch{
{ {
Subject: "intruder", Subject: "intruder",
Issuer: "^https://honeypot.com$", Issuer: "^https://honeypot.com$",
@ -2260,7 +2260,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature_keyless(t *testi
}, },
Spec: ociv1.OCIRepositorySpec{ Spec: ociv1.OCIRepositorySpec{
URL: "oci://ghcr.io/stefanprodan/manifests/podinfo", URL: "oci://ghcr.io/stefanprodan/manifests/podinfo",
Verify: &ociv1.OCIRepositoryVerification{ Verify: &sourcev1.OCIRepositoryVerification{
Provider: "cosign", Provider: "cosign",
}, },
Interval: metav1.Duration{Duration: interval}, Interval: metav1.Duration{Duration: interval},

View File

@ -32,7 +32,8 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
helmv1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/fluxcd/source-controller/internal/helm/registry" "github.com/fluxcd/source-controller/internal/helm/registry"
soci "github.com/fluxcd/source-controller/internal/oci" soci "github.com/fluxcd/source-controller/internal/oci"
stls "github.com/fluxcd/source-controller/internal/tls" stls "github.com/fluxcd/source-controller/internal/tls"
@ -69,7 +70,7 @@ func (o ClientOpts) MustLoginToRegistry() bool {
// auth mechanisms. // auth mechanisms.
// A temporary directory is created to store the certs files if needed and its path is returned along with the options object. It is the // A temporary directory is created to store the certs files if needed and its path is returned along with the options object. It is the
// caller's responsibility to clean up the directory. // caller's responsibility to clean up the directory.
func GetClientOpts(ctx context.Context, c client.Client, obj *helmv1.HelmRepository, url string) (*ClientOpts, string, error) { func GetClientOpts(ctx context.Context, c client.Client, obj *sourcev1.HelmRepository, url string) (*ClientOpts, string, error) {
hrOpts := &ClientOpts{ hrOpts := &ClientOpts{
GetterOpts: []helmgetter.Option{ GetterOpts: []helmgetter.Option{
helmgetter.WithURL(url), helmgetter.WithURL(url),
@ -77,7 +78,7 @@ func GetClientOpts(ctx context.Context, c client.Client, obj *helmv1.HelmReposit
helmgetter.WithPassCredentialsAll(obj.Spec.PassCredentials), helmgetter.WithPassCredentialsAll(obj.Spec.PassCredentials),
}, },
} }
ociRepo := obj.Spec.Type == helmv1.HelmRepositoryTypeOCI ociRepo := obj.Spec.Type == sourcev1.HelmRepositoryTypeOCI
var ( var (
certSecret *corev1.Secret certSecret *corev1.Secret
@ -135,7 +136,7 @@ func GetClientOpts(ctx context.Context, c client.Client, obj *helmv1.HelmReposit
return nil, "", fmt.Errorf("failed to configure login options: %w", err) return nil, "", fmt.Errorf("failed to configure login options: %w", err)
} }
} }
} else if obj.Spec.Provider != helmv1.GenericOCIProvider && obj.Spec.Type == helmv1.HelmRepositoryTypeOCI && ociRepo { } else if obj.Spec.Provider != sourcev1beta2.GenericOCIProvider && obj.Spec.Type == sourcev1.HelmRepositoryTypeOCI && ociRepo {
authenticator, authErr := soci.OIDCAuth(ctx, obj.Spec.URL, obj.Spec.Provider) authenticator, authErr := soci.OIDCAuth(ctx, obj.Spec.URL, obj.Spec.Provider)
if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) { if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) {
return nil, "", fmt.Errorf("failed to get credential from '%s': %w", obj.Spec.Provider, authErr) return nil, "", fmt.Errorf("failed to get credential from '%s': %w", obj.Spec.Provider, authErr)

View File

@ -29,7 +29,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
helmv1 "github.com/fluxcd/source-controller/api/v1beta2" helmv1 "github.com/fluxcd/source-controller/api/v1"
) )
func TestGetClientOpts(t *testing.T) { func TestGetClientOpts(t *testing.T) {

View File

@ -22,7 +22,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/predicate"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1"
) )
// HelmRepositoryOCIMigrationPredicate implements predicate functions to allow // HelmRepositoryOCIMigrationPredicate implements predicate functions to allow

View File

@ -25,8 +25,7 @@ import (
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions" "github.com/fluxcd/pkg/runtime/conditions"
v1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
) )
func TestHelmRepositoryOCIMigrationPredicate_Create(t *testing.T) { func TestHelmRepositoryOCIMigrationPredicate_Create(t *testing.T) {
@ -161,7 +160,7 @@ func TestHelmRepositoryOCIMigrationPredicate_Update(t *testing.T) {
Type: sourcev1.HelmRepositoryTypeDefault, Type: sourcev1.HelmRepositoryTypeDefault,
} }
oldObj.Status = sourcev1.HelmRepositoryStatus{ oldObj.Status = sourcev1.HelmRepositoryStatus{
Artifact: &v1.Artifact{}, Artifact: &sourcev1.Artifact{},
URL: "http://some-address", URL: "http://some-address",
ObservedGeneration: 3, ObservedGeneration: 3,
} }

22
main.go
View File

@ -50,7 +50,7 @@ import (
"github.com/fluxcd/pkg/runtime/pprof" "github.com/fluxcd/pkg/runtime/pprof"
"github.com/fluxcd/pkg/runtime/probes" "github.com/fluxcd/pkg/runtime/probes"
v1 "github.com/fluxcd/source-controller/api/v1" "github.com/fluxcd/source-controller/api/v1"
"github.com/fluxcd/source-controller/api/v1beta2" "github.com/fluxcd/source-controller/api/v1beta2"
// +kubebuilder:scaffold:imports // +kubebuilder:scaffold:imports
@ -199,7 +199,7 @@ func main() {
DependencyRequeueInterval: requeueDependency, DependencyRequeueInterval: requeueDependency,
RateLimiter: helper.GetRateLimiter(rateLimiterOptions), RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
}); err != nil { }); err != nil {
setupLog.Error(err, "unable to create controller", "controller", v1beta2.GitRepositoryKind) setupLog.Error(err, "unable to create controller", "controller", v1.GitRepositoryKind)
os.Exit(1) os.Exit(1)
} }
@ -216,7 +216,7 @@ func main() {
}).SetupWithManagerAndOptions(mgr, controller.HelmRepositoryReconcilerOptions{ }).SetupWithManagerAndOptions(mgr, controller.HelmRepositoryReconcilerOptions{
RateLimiter: helper.GetRateLimiter(rateLimiterOptions), RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
}); err != nil { }); err != nil {
setupLog.Error(err, "unable to create controller", "controller", v1beta2.HelmRepositoryKind) setupLog.Error(err, "unable to create controller", "controller", v1.HelmRepositoryKind)
os.Exit(1) os.Exit(1)
} }
@ -234,7 +234,7 @@ func main() {
}).SetupWithManagerAndOptions(ctx, mgr, controller.HelmChartReconcilerOptions{ }).SetupWithManagerAndOptions(ctx, mgr, controller.HelmChartReconcilerOptions{
RateLimiter: helper.GetRateLimiter(rateLimiterOptions), RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
}); err != nil { }); err != nil {
setupLog.Error(err, "unable to create controller", "controller", v1beta2.HelmChartKind) setupLog.Error(err, "unable to create controller", "controller", v1.HelmChartKind)
os.Exit(1) os.Exit(1)
} }
@ -247,7 +247,7 @@ func main() {
}).SetupWithManagerAndOptions(mgr, controller.BucketReconcilerOptions{ }).SetupWithManagerAndOptions(mgr, controller.BucketReconcilerOptions{
RateLimiter: helper.GetRateLimiter(rateLimiterOptions), RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
}); err != nil { }); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Bucket") setupLog.Error(err, "unable to create controller", "controller", v1beta2.BucketKind)
os.Exit(1) os.Exit(1)
} }
@ -260,7 +260,7 @@ func main() {
}).SetupWithManagerAndOptions(mgr, controller.OCIRepositoryReconcilerOptions{ }).SetupWithManagerAndOptions(mgr, controller.OCIRepositoryReconcilerOptions{
RateLimiter: helper.GetRateLimiter(rateLimiterOptions), RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
}); err != nil { }); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "OCIRepository") setupLog.Error(err, "unable to create controller", "controller", v1beta2.OCIRepositoryKind)
os.Exit(1) os.Exit(1)
} }
// +kubebuilder:scaffold:builder // +kubebuilder:scaffold:builder
@ -348,11 +348,11 @@ func mustSetupManager(metricsAddr, healthAddr string, maxConcurrent int,
}, },
Cache: ctrlcache.Options{ Cache: ctrlcache.Options{
ByObject: map[ctrlclient.Object]ctrlcache.ByObject{ ByObject: map[ctrlclient.Object]ctrlcache.ByObject{
&v1.GitRepository{}: {Label: watchSelector}, &v1.GitRepository{}: {Label: watchSelector},
&v1beta2.HelmRepository{}: {Label: watchSelector}, &v1.HelmRepository{}: {Label: watchSelector},
&v1beta2.HelmChart{}: {Label: watchSelector}, &v1.HelmChart{}: {Label: watchSelector},
&v1beta2.Bucket{}: {Label: watchSelector}, &v1beta2.Bucket{}: {Label: watchSelector},
&v1beta2.OCIRepository{}: {Label: watchSelector}, &v1beta2.OCIRepository{}: {Label: watchSelector},
}, },
}, },
Metrics: metricsserver.Options{ Metrics: metricsserver.Options{