Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static HelmRepository OCI #1243

Merged
merged 4 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions api/v1beta2/helmrepository_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const (
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"`

Expand Down Expand Up @@ -88,13 +89,13 @@ type HelmRepositorySpec struct {
// 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"`
// +optional
Interval metav1.Duration `json:"interval,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 repository.
// and for remote OCI Repository operations like pulling for an OCI helm
// chart by the associated HelmChart.
// Its default value is 60s.
// +kubebuilder:default:="60s"
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m))+$"
// +optional
Expand Down Expand Up @@ -169,7 +170,19 @@ func (in *HelmRepository) SetConditions(conditions []metav1.Condition) {
// GetRequeueAfter returns the duration after which the source must be
// reconciled again.
func (in HelmRepository) GetRequeueAfter() time.Duration {
return in.Spec.Interval.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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,10 @@ spec:
of this HelmRepository.
type: boolean
timeout:
default: 60s
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 repository. Its default value is 60s.
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:
Expand All @@ -373,9 +373,9 @@ spec:
url:
description: URL of the Helm repository, a valid URL contains at least
a protocol and host.
pattern: ^(http|https|oci)://.*$
type: string
required:
- interval
- url
type: object
status:
Expand Down
8 changes: 6 additions & 2 deletions docs/api/v1beta2/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,7 @@ Kubernetes meta/v1.Duration
</em>
</td>
<td>
<em>(Optional)</em>
<p>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.</p>
Expand All @@ -883,7 +884,8 @@ Kubernetes meta/v1.Duration
<td>
<em>(Optional)</em>
<p>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 repository.
and for remote OCI Repository operations like pulling for an OCI helm
chart by the associated HelmChart.
Its default value is 60s.</p>
</td>
</tr>
Expand Down Expand Up @@ -2583,6 +2585,7 @@ Kubernetes meta/v1.Duration
</em>
</td>
<td>
<em>(Optional)</em>
<p>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.</p>
Expand All @@ -2600,7 +2603,8 @@ Kubernetes meta/v1.Duration
<td>
<em>(Optional)</em>
<p>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 repository.
and for remote OCI Repository operations like pulling for an OCI helm
chart by the associated HelmChart.
Its default value is 60s.</p>
</td>
</tr>
Expand Down
70 changes: 38 additions & 32 deletions docs/spec/v1beta2/helmrepositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
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.
Instead a validation of the Helm repository is performed and the outcome is reported in the
`.status.conditions` field.
- 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

Expand Down Expand Up @@ -113,9 +113,11 @@ In the above example:

- A HelmRepository named `podinfo` is created, indicated by the
`.metadata.name` field.
- The source-controller performs the Helm repository url validation i.e. the url
is a valid OCI registry url, every five minutes with the information indicated by the
`.spec.interval` and `.spec.url` fields.
- 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`.

Expand All @@ -129,25 +131,12 @@ You can run this example by saving the manifest into `helmrepository.yaml`.

```console
NAME URL AGE READY STATUS
podinfo oci://ghcr.io/stefanprodan/charts 3m22s True Helm repository "podinfo" is ready
podinfo oci://ghcr.io/stefanprodan/charts 3m22s
```

3. Run `kubectl describe helmrepository podinfo` to see the [Conditions](#conditions)
in the HelmRepository's Status:

```console
...
Status:
Conditions:
Last Transition Time: 2022-05-12T14:02:12Z
Message: Helm repository "podinfo" is ready
Observed Generation: 1
Reason: Succeeded
Status: True
Type: Ready
Observed Generation: 1
Events: <none>
```
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

Expand Down Expand Up @@ -360,8 +349,12 @@ for more information about setting up GKE Workload Identity.

### Interval

`.spec.interval` is a required field that specifies the interval which the
Helm repository index must be consulted at.
**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
Expand All @@ -387,11 +380,14 @@ For Helm repositories which require authentication, see [Secret reference](#secr

### 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. The default value
is `60s`.
e.g. `1m30s` for a timeout of one minute and thirty seconds. When not set, the
default value is `1m`.

### Secret reference

Expand Down Expand Up @@ -537,6 +533,9 @@ 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
Expand All @@ -547,6 +546,10 @@ 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

Expand Down Expand Up @@ -648,6 +651,10 @@ 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.

Expand Down Expand Up @@ -713,9 +720,11 @@ specific HelmRepository, e.g. `flux logs --level=error --kind=HelmRepository --n

## HelmRepository Status

### Artifact
**Note:** This section does not apply to [OCI Helm
Repositories](#helm-oci-repository), they do not contain any information in the
status.

**Note:** This section does not apply to [OCI Helm Repositories](#helm-oci-repository), they do not emit artifacts.
### Artifact

The HelmRepository reports the last fetched repository index as an Artifact
object in the `.status.artifact` of the resource.
Expand Down Expand Up @@ -757,9 +766,6 @@ and reports `Reconciling` and `Stalled` conditions where applicable to
provide better (timeout) support to solutions polling the HelmRepository to become
`Ready`.

OCI Helm repositories use only `Reconciling`, `Ready`, `FetchFailed`, and `Stalled`
condition types.

#### Reconciling HelmRepository

The source-controller marks a HelmRepository as _reconciling_ when one of the following
Expand Down
2 changes: 0 additions & 2 deletions hack/ci/e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ kubectl -n source-system rollout status deploy/source-controller --timeout=1m
kubectl -n source-system wait gitrepository/gitrepository-sample --for=condition=ready --timeout=1m
kubectl -n source-system wait ocirepository/ocirepository-sample --for=condition=ready --timeout=1m
kubectl -n source-system wait helmrepository/helmrepository-sample --for=condition=ready --timeout=1m
kubectl -n source-system wait helmrepository/helmrepository-sample-oci --for=condition=ready --timeout=1m
kubectl -n source-system wait helmchart/helmchart-sample --for=condition=ready --timeout=1m
kubectl -n source-system wait helmchart/helmchart-sample-oci --for=condition=ready --timeout=1m
kubectl -n source-system delete -f "${ROOT_DIR}/config/samples"
Expand Down Expand Up @@ -145,7 +144,6 @@ kubectl -n source-system wait gitrepository/large-repo --for=condition=ready --t

echo "Run HelmChart from OCI registry tests"
kubectl -n source-system apply -f "${ROOT_DIR}/config/testdata/helmchart-from-oci/source.yaml"
kubectl -n source-system wait helmrepository/podinfo --for=condition=ready --timeout=1m
kubectl -n source-system wait helmchart/podinfo --for=condition=ready --timeout=1m
kubectl -n source-system wait helmchart/podinfo-keyless --for=condition=ready --timeout=1m

Expand Down
11 changes: 9 additions & 2 deletions internal/controller/helmchart_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controller

import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/url"
Expand Down Expand Up @@ -139,6 +140,12 @@ type HelmChartReconciler struct {
patchOptions []patch.Option
}

// RegistryClientGeneratorFunc is a function that returns a registry client
// and an optional file name.
// The file is used to store the registry client credentials.
// The caller is responsible for deleting the file.
type RegistryClientGeneratorFunc func(tlsConfig *tls.Config, isLogin bool) (*helmreg.Client, string, error)

func (r *HelmChartReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
return r.SetupWithManagerAndOptions(ctx, mgr, HelmChartReconcilerOptions{})
}
Expand Down Expand Up @@ -507,7 +514,7 @@ func (r *HelmChartReconciler) reconcileSource(ctx context.Context, sp *patch.Ser
func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *helmv1.HelmChart,
repo *helmv1.HelmRepository, b *chart.Build) (sreconcile.Result, error) {
// Used to login with the repository declared provider
ctxTimeout, cancel := context.WithTimeout(ctx, repo.Spec.Timeout.Duration)
ctxTimeout, cancel := context.WithTimeout(ctx, repo.GetTimeout())
defer cancel()

normalizedURL, err := repository.NormalizeURL(repo.Spec.URL)
Expand Down Expand Up @@ -992,7 +999,7 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
}

// Used to login with the repository declared provider
ctxTimeout, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
ctxTimeout, cancel := context.WithTimeout(ctx, obj.GetTimeout())
defer cancel()

clientOpts, certsTmpDir, err := getter.GetClientOpts(ctxTimeout, r.Client, obj, normalizedURL)
Expand Down
3 changes: 2 additions & 1 deletion internal/controller/helmchart_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func TestHelmChartReconciler_Reconcile(t *testing.T) {
{
name: "Stalling on invalid repository URL",
beforeFunc: func(repository *helmv1.HelmRepository) {
repository.Spec.URL = "://unsupported" // Invalid URL
repository.Spec.URL = "https://unsupported/foo://" // Invalid URL
},
assertFunc: func(g *WithT, obj *helmv1.HelmChart, _ *helmv1.HelmRepository) {
key := client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}
Expand Down Expand Up @@ -293,6 +293,7 @@ func TestHelmChartReconciler_Reconcile(t *testing.T) {
}

g.Expect(testEnv.CreateAndWait(ctx, &repository)).To(Succeed())
defer func() { g.Expect(testEnv.Delete(ctx, &repository)).To(Succeed()) }()

obj := helmv1.HelmChart{
ObjectMeta: metav1.ObjectMeta{
Expand Down
Loading
Loading