Skip to content

Commit

Permalink
chore: unify artifacts code
Browse files Browse the repository at this point in the history
  • Loading branch information
emosbaugh committed Jan 31, 2025
1 parent 3832e06 commit 04775a2
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 253 deletions.
125 changes: 12 additions & 113 deletions cmd/local-artifact-mirror/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,132 +2,31 @@ package main

import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
"os"

"github.com/sirupsen/logrus"
"go.uber.org/multierr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/content/file"
"oras.land/oras-go/v2/registry"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
"oras.land/oras-go/v2/registry/remote/credentials"
"github.com/replicatedhq/embedded-cluster/pkg/artifacts"
)

var (
insecureTransport *http.Transport
)

func init() {
insecureTransport = http.DefaultTransport.(*http.Transport).Clone()
insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}

// DockerConfig represents the content of the '.dockerconfigjson' secret.
type DockerConfig struct {
Auths map[string]DockerConfigEntry `json:"auths"`
}

// DockerConfigEntry represents the content of the '.dockerconfigjson' secret.
type DockerConfigEntry struct {
Username string `json:"username"`
Password string `json:"password"`
}

// registryAuth returns the authentication store to be used when reaching the
// registry. The authentication store is read from the cluster secret named
// 'registry-creds' in the 'kotsadm' namespace.
func registryAuth(ctx context.Context) (credentials.Store, error) {
nsn := types.NamespacedName{Name: "registry-creds", Namespace: "kotsadm"}
var sct corev1.Secret
if err := kubecli.Get(ctx, nsn, &sct); err != nil {
if !errors.IsNotFound(err) {
return nil, fmt.Errorf("unable to get secret: %w", err)
}

// if we can't locate a secret then returns an empty credentials
// store so we attempt to fetch the assets without auth.
logrus.Infof("no registry auth found, trying anonymous access")
return credentials.NewMemoryStore(), nil
}

data, ok := sct.Data[".dockerconfigjson"]
if !ok {
return nil, fmt.Errorf("unable to find secret .dockerconfigjson")
}

var cfg DockerConfig
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("unable to unmarshal secret: %w", err)
}

creds := credentials.NewMemoryStore()
for addr, entry := range cfg.Auths {
creds.Put(ctx, addr, auth.Credential{
Username: entry.Username,
Password: entry.Password,
})
}
return creds, nil
}

// pullArtifact fetches an artifact from the registry pointed by 'from'. The artifact
// is stored in a temporary directory and the path to this directory is returned.
// Callers are responsible for removing the temporary directory when it is no longer
// needed. In case of error, the temporary directory is removed here.
func pullArtifact(ctx context.Context, from string) (string, error) {
store, err := registryAuth(ctx)
tmpdir, err := os.MkdirTemp("", "embedded-cluster-metadata-*")
if err != nil {
return "", fmt.Errorf("unable to get registry auth: %w", err)
return "", fmt.Errorf("create temp dir: %w", err)
}

imgref, err := registry.ParseReference(from)
if err != nil {
return "", fmt.Errorf("unable to parse image reference: %w", err)
}

tmpdir, err := os.MkdirTemp("", "embedded-cluster-artifact-*")
if err != nil {
return "", fmt.Errorf("unable to create temp dir: %w", err)
}

repo, err := remote.NewRepository(from)
if err != nil {
return "", fmt.Errorf("unable to create repository: %w", err)
}

fs, err := file.New(tmpdir)
if err != nil {
return "", fmt.Errorf("unable to create file store: %w", err)
}
defer fs.Close()

repo.Client = &auth.Client{
Client: &http.Client{Transport: insecureTransport},
Credential: store.Get,
}

tag := imgref.Reference
_, tlserr := oras.Copy(ctx, repo, tag, fs, tag, oras.DefaultCopyOptions)
if tlserr == nil {
opts := artifacts.PullOptions{}
err = artifacts.Pull(ctx, kubecli, from, tmpdir, opts)
if err == nil {
return tmpdir, nil
}

// if we fail to fetch the artifact using https we gonna try once more using plain
// http as some versions of the registry were deployed without tls.
repo.PlainHTTP = true
logrus.Infof("unable to fetch artifact using tls, retrying with http")
if _, err := oras.Copy(ctx, repo, tag, fs, tag, oras.DefaultCopyOptions); err != nil {
os.RemoveAll(tmpdir)
err = multierr.Combine(tlserr, err)
return "", fmt.Errorf("unable to fetch artifacts with or without tls: %w", err)
opts.PlainHTTP = true
if err := artifacts.Pull(ctx, kubecli, from, tmpdir, opts); err == nil {
return tmpdir, nil
}
return tmpdir, nil

os.RemoveAll(tmpdir)
return "", fmt.Errorf("pull artifact: %w", err)
}
82 changes: 0 additions & 82 deletions operator/pkg/artifacts/artifacts.go

This file was deleted.

53 changes: 0 additions & 53 deletions operator/pkg/artifacts/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ package artifacts

import (
"context"
"encoding/json"
"fmt"

"github.com/go-logr/logr"
clusterv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"oras.land/oras-go/v2/registry/remote/auth"
"oras.land/oras-go/v2/registry/remote/credentials"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
Expand All @@ -24,17 +19,6 @@ const (
kotsadmNamespace = "kotsadm"
)

// dockerConfig represents the content of the '.dockerconfigjson' secret.
type dockerConfig struct {
Auths map[string]dockerConfigEntry `json:"auths"`
}

// dockerConfigEntry represents the content of the '.dockerconfigjson' secret.
type dockerConfigEntry struct {
Username string `json:"username"`
Password string `json:"password"`
}

func EnsureRegistrySecretInECNamespace(ctx context.Context, cli client.Client, in *clusterv1beta1.Installation) (controllerutil.OperationResult, error) {
op := controllerutil.OperationResultNone

Expand Down Expand Up @@ -75,43 +59,6 @@ func GetRegistryImagePullSecret() corev1.LocalObjectReference {
return corev1.LocalObjectReference{Name: RegistryCredsSecretName}
}

// registryAuth returns the authentication store to be used when reaching the
// registry. The authentication store is read from the cluster secret named
// 'registry-creds' in the 'kotsadm' namespace.
func registryAuth(ctx context.Context, log logr.Logger, cli client.Client) (credentials.Store, error) {
nsn := types.NamespacedName{Name: RegistryCredsSecretName, Namespace: kotsadmNamespace}
var sct corev1.Secret
if err := cli.Get(ctx, nsn, &sct); err != nil {
if !k8serrors.IsNotFound(err) {
return nil, fmt.Errorf("get secret: %w", err)
}
log.Info("Secret registry-creds not found, using anonymous access")
return credentials.NewMemoryStore(), nil
}

data, ok := sct.Data[".dockerconfigjson"]
if !ok {
return nil, fmt.Errorf("secret does not contain .dockerconfigjson")
}

var cfg dockerConfig
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("unmarshal secret: %w", err)
}

creds := credentials.NewMemoryStore()
for addr, entry := range cfg.Auths {
err := creds.Put(ctx, addr, auth.Credential{
Username: entry.Username,
Password: entry.Password,
})
if err != nil {
return nil, fmt.Errorf("put credential for %s: %w", addr, err)
}
}
return creds, nil
}

func applyECOperatorLabels(labels map[string]string, component string) map[string]string {
if labels == nil {
labels = make(map[string]string)
Expand Down
29 changes: 24 additions & 5 deletions operator/pkg/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,7 @@ func CopyVersionMetadataToCluster(ctx context.Context, cli client.Client, in *v1
}

func getRemoteMetadataAirgap(ctx context.Context, cli client.Client, in *v1beta1.Installation) ([]byte, error) {
log := ctrl.LoggerFrom(ctx)

// pull the artifact from the artifact location pointed by EmbeddedClusterMetadata. This property
// points to a repository inside the registry running on the cluster.
location, err := artifacts.Pull(ctx, log, cli, in.Spec.Artifacts.EmbeddedClusterMetadata)
location, err := pullArtifact(ctx, cli, in.Spec.Artifacts.EmbeddedClusterMetadata)
if err != nil {
return nil, fmt.Errorf("pull artifact: %w", err)
}
Expand All @@ -85,6 +81,29 @@ func getRemoteMetadataAirgap(ctx context.Context, cli client.Client, in *v1beta1
return data, nil
}

func pullArtifact(ctx context.Context, cli client.Client, from string) (string, error) {
tmpdir, err := os.MkdirTemp("", "embedded-cluster-metadata-*")
if err != nil {
return "", fmt.Errorf("create temp dir: %w", err)
}

opts := artifacts.PullOptions{}

Check failure on line 90 in operator/pkg/metadata/metadata.go

View workflow job for this annotation

GitHub Actions / Sanitize

undefined: artifacts.PullOptions
err = artifacts.Pull(ctx, cli, from, tmpdir, opts)

Check failure on line 91 in operator/pkg/metadata/metadata.go

View workflow job for this annotation

GitHub Actions / Sanitize

undefined: artifacts.Pull
if err == nil {
return tmpdir, nil
}

// if we fail to fetch the artifact using https we gonna try once more using plain
// http as some versions of the registry were deployed without tls.
opts.PlainHTTP = true
if err := artifacts.Pull(ctx, cli, from, tmpdir, opts); err == nil {

Check failure on line 99 in operator/pkg/metadata/metadata.go

View workflow job for this annotation

GitHub Actions / Sanitize

undefined: artifacts.Pull
return tmpdir, nil
}

os.RemoveAll(tmpdir)
return "", err
}

func getRemoteMetadataOnline(ctx context.Context, cli client.Client, in *v1beta1.Installation) ([]byte, error) {
var metadataURL string
if in.Spec.Config.MetadataOverrideURL != "" {
Expand Down
Loading

0 comments on commit 04775a2

Please sign in to comment.