Skip to content

Commit

Permalink
Add artifactPullSecret to spec.distribution
Browse files Browse the repository at this point in the history
ahothan committed Jan 16, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent e916d85 commit 73eb96b
Showing 10 changed files with 183 additions and 9 deletions.
5 changes: 5 additions & 0 deletions api/v1/fluxinstance_types.go
Original file line number Diff line number Diff line change
@@ -111,6 +111,11 @@ type Distribution struct {
// +kubebuilder:validation:Pattern="^oci://.*$"
// +optional
Artifact string `json:"artifact,omitempty"`

// ArtifactPullSecret is the name of the Kubernetes secret
// to use for pulling the Kubernetes manifests for the distribution specified in the Artifact field.
// +optional
ArtifactPullSecret string `json:"artifactPullSecret,omitempty"`
}

// Component is the name of a controller to install.
5 changes: 5 additions & 0 deletions config/crd/bases/fluxcd.controlplane.io_fluxinstances.yaml
Original file line number Diff line number Diff line change
@@ -135,6 +135,11 @@ spec:
e.g. 'oci://ghcr.io/controlplaneio-fluxcd/flux-operator-manifests:latest'.
pattern: ^oci://.*$
type: string
artifactPullSecret:
description: |-
ArtifactPullSecret is the name of the Kubernetes secret
to use for pulling the Kubernetes manifests for the distribution specified in the Artifact field.
type: string
imagePullSecret:
description: |-
ImagePullSecret is the name of the Kubernetes secret
19 changes: 19 additions & 0 deletions docs/api/v1/fluxinstance.md
Original file line number Diff line number Diff line change
@@ -253,6 +253,25 @@ spec:
artifact: "oci://ghcr.io/controlplaneio-fluxcd/flux-operator-manifests"
```

#### Distribution artifact pull secret

The `.spec.distribution.artifactPullSecret` field is optional and specifies the name of the Kubernetes secret
that contains the credentials to pull the Flux distribution manifests from a private registry.

Example using a private registry:

```yaml
spec:
distribution:
version: "2.3.x"
registry: "ghcr.io/controlplaneio-fluxcd/distroless"
artifact: "oci://private.registry.com/controlplaneio-fluxcd/flux-operator-manifests"
artifactPullSecret: "flux-private-auth"
```

The manifest pull secret must be created in the same namespace where the FluxInstance is deployed
and must be of type `kubernetes.io/dockerconfigjson`.

### Components configuration

The `.spec.components` field is optional and specifies the list of Flux components to install.
19 changes: 19 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/google/cel-go v0.22.0
github.com/google/go-containerregistry v0.20.2
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20241111191718-6bce25ecf029
github.com/gosimple/slug v1.15.0
github.com/onsi/ginkgo/v2 v2.22.2
github.com/onsi/gomega v1.36.2
@@ -37,27 +38,42 @@ require (

require (
cel.dev/expr v0.18.0 // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.47 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.18.11 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.16.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230510185313-f5e39e5f34c7 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/cli v27.2.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
@@ -82,6 +98,7 @@ require (
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230516205744-dbecb1de8cfa // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
@@ -93,6 +110,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
@@ -124,6 +142,7 @@ require (
github.com/xlab/treeprint v1.2.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sync v0.10.0 // indirect
97 changes: 95 additions & 2 deletions go.sum

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions internal/builder/digest.go
Original file line number Diff line number Diff line change
@@ -8,12 +8,13 @@ import (
"fmt"
"strings"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
)

// GetArtifactDigest looks up an artifact from an OCI repository and returns the digest of the artifact.
func GetArtifactDigest(ctx context.Context, ociURL string) (string, error) {
digest, err := crane.Digest(strings.TrimPrefix(ociURL, "oci://"), crane.WithContext(ctx))
func GetArtifactDigest(ctx context.Context, ociURL string, keyChain authn.Keychain) (string, error) {
digest, err := crane.Digest(strings.TrimPrefix(ociURL, "oci://"), crane.WithContext(ctx), crane.WithAuthFromKeychain(keyChain))
if err != nil {
return "", fmt.Errorf("fetching digest for artifact %s failed: %w", ociURL, err)
}
5 changes: 3 additions & 2 deletions internal/builder/pull.go
Original file line number Diff line number Diff line change
@@ -9,14 +9,15 @@ import (
"strings"

"github.com/fluxcd/pkg/tar"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
)

// PullArtifact downloads an artifact from an OCI repository and extracts the content
// of the first tgz layer to the given destination directory.
// It returns the digest of the artifact.
func PullArtifact(ctx context.Context, ociURL, dstDir string) (string, error) {
img, err := crane.Pull(strings.TrimPrefix(ociURL, "oci://"), crane.WithContext(ctx))
func PullArtifact(ctx context.Context, ociURL, dstDir string, keyChain authn.Keychain) (string, error) {
img, err := crane.Pull(strings.TrimPrefix(ociURL, "oci://"), crane.WithContext(ctx), crane.WithAuthFromKeychain(keyChain))
if err != nil {
return "", fmt.Errorf("pulling artifact %s failed: %w", ociURL, err)
}
9 changes: 8 additions & 1 deletion internal/controller/fluxinstance_artifact_controller.go
Original file line number Diff line number Diff line change
@@ -69,7 +69,14 @@ func (r *FluxInstanceArtifactReconciler) reconcile(ctx context.Context,

// Fetch the latest digest of the distribution manifests.
artifactURL := obj.Spec.Distribution.Artifact
artifactDigest, err := builder.GetArtifactDigest(ctx, artifactURL)
keyChain, err := GetDistributionKeychain(ctx, r.Client, obj)
if err != nil {
msg := fmt.Sprintf("failed to get distribution key chain: %s", err.Error())
r.Event(obj, corev1.EventTypeWarning, meta.ArtifactFailedReason, msg)
return ctrl.Result{}, err
}

artifactDigest, err := builder.GetArtifactDigest(ctx, artifactURL, keyChain)
if err != nil {
msg := fmt.Sprintf("fetch failed: %s", err.Error())
r.Event(obj, corev1.EventTypeWarning, meta.ArtifactFailedReason, msg)
4 changes: 3 additions & 1 deletion internal/controller/fluxinstance_artifact_controller_test.go
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ import (

"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
"github.com/google/go-containerregistry/pkg/authn"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
@@ -33,7 +34,8 @@ func TestFluxInstanceArtifactReconciler(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

latestArtifactRevision, err := builder.GetArtifactDigest(ctx, cpLatestManifestsURL)
var keyChain authn.Keychain
latestArtifactRevision, err := builder.GetArtifactDigest(ctx, cpLatestManifestsURL, keyChain)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(latestArtifactRevision).To(HavePrefix("sha256:"))
g.Expect(strings.TrimPrefix(latestArtifactRevision, "sha256:")).To(HaveLen(64))
24 changes: 23 additions & 1 deletion internal/controller/fluxinstance_controller.go
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@ import (
"github.com/fluxcd/pkg/ssa"
"github.com/fluxcd/pkg/ssa/normalize"
ssautil "github.com/fluxcd/pkg/ssa/utils"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/authn/k8schain"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -216,6 +218,21 @@ func (r *FluxInstanceReconciler) reconcile(ctx context.Context,
return requeueAfter(obj), nil
}

// Get a keychain from the artifactPullSecret secret if provided
func GetDistributionKeychain(ctx context.Context, kubeClient client.Client, obj *fluxcdv1.FluxInstance) (authn.Keychain, error) {
artifactPullSecret := obj.Spec.Distribution.ArtifactPullSecret
if artifactPullSecret == "" {
return nil, nil
}
// the secret must be defined in the same namespace as the FluxInstance resource
ns := obj.GetNamespace()
secret := corev1.Secret{}
if err := kubeClient.Get(ctx, client.ObjectKey{Name: artifactPullSecret, Namespace: ns}, &secret); err != nil {
return nil, err
}
return k8schain.NewFromPullSecrets(ctx, []corev1.Secret{secret})
}

// fetch pulls the distribution OCI artifact and
// extracts the manifests to the temporary directory.
// If the distribution artifact URL is not provided,
@@ -230,7 +247,12 @@ func (r *FluxInstanceReconciler) fetch(ctx context.Context,
ctxPull, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

artifactDigest, err := builder.PullArtifact(ctxPull, artifactURL, tmpDir)
// Create a keychain for the distribution artifact.
keyChain, err := GetDistributionKeychain(ctxPull, r.Client, obj)
if err != nil {
return "", "", err
}
artifactDigest, err := builder.PullArtifact(ctxPull, artifactURL, tmpDir, keyChain)
if err != nil {
return "", "", err
}

0 comments on commit 73eb96b

Please sign in to comment.