Skip to content
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
89 changes: 8 additions & 81 deletions pkg/auto/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,16 @@ package auto

import (
"context"
"crypto/tls"
"errors"
"fmt"
"log/slog"
"net/http"
"os"
"strings"

"github.com/go-git/go-billy/v5"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/rancher/charts-build-scripts/pkg/logger"
"github.com/rancher/charts-build-scripts/pkg/options"
"github.com/rancher/charts-build-scripts/pkg/path"
"github.com/rancher/charts-build-scripts/pkg/registries"

"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli"
Expand All @@ -30,14 +23,13 @@ type checkAssetFunc func(ctx context.Context, regClient *registry.Client, ociDNS
type pushFunc func(helmClient *registry.Client, data []byte, url string) error

type oci struct {
DNS string
user string
password string
helmClient *registry.Client
registryOptions []remote.Option
loadAsset loadAssetFunc
checkAsset checkAssetFunc
push pushFunc
DNS string
user string
password string
helmClient *registry.Client
loadAsset loadAssetFunc
checkAsset checkAssetFunc
push pushFunc
}

// UpdateOCI pushes Helm charts to an OCI registry
Expand Down Expand Up @@ -78,31 +70,13 @@ func setupOCI(ctx context.Context, ociDNS, ociUser, ociPass string, debug bool)
return nil, err
}

o.registryOptions = setupRegistryReader(ctx, o.DNS, o.user, o.password)

o.loadAsset = loadAsset
o.checkAsset = checkAsset
o.push = push

return o, nil
}

func setupRegistryReader(ctx context.Context, ociDNS, ociUser, ociPass string) []remote.Option {
tr := http.DefaultTransport.(*http.Transport).Clone()
tr.TLSClientConfig = &tls.Config{
InsecureSkipVerify: false,
}

registryClientOpts := []remote.Option{
remote.WithContext(ctx),
remote.WithUserAgent(registries.UaString),
remote.WithAuth(&authn.Basic{Username: ociUser, Password: ociPass}),
remote.WithTransport(tr),
}

return registryClientOpts
}

func setupHelm(ctx context.Context, ociDNS, ociUser, ociPass string, debug bool) (*registry.Client, error) {
settings := cli.New()
actionConfig := new(action.Configuration)
Expand Down Expand Up @@ -206,13 +180,6 @@ func (o *oci) update(ctx context.Context, release *options.ReleaseOptions) ([]st

// Check if the asset version already exists in the OCI registry
// Never overwrite a previously released chart!
existsTest, err := o.checkRegistryTagExists(ctx, o.DNS, chart, version)
if err != nil {
logger.Log(ctx, slog.LevelError, "checkRegistryTagExists")
return pushedAssets, err
}
logger.Log(ctx, slog.LevelWarn, "exists worked?", slog.Bool("exist", existsTest))

exists, err := o.checkAsset(ctx, o.helmClient, o.DNS, chart, version)
if err != nil {
return pushedAssets, err
Expand Down Expand Up @@ -281,18 +248,14 @@ func loadAsset(chart, asset string) ([]byte, error) {

// oci://<oci-dns>/<chart(repository)>:<version>
func buildPushURL(ociDNS, chart, version string) string {
return ociDNS + "/" + chart + ":" + version
return ociDNS + "/rancher/charts/" + chart + ":" + version
}

// checkAsset checks if a specific asset version exists in the OCI registry
func checkAsset(ctx context.Context, helmClient *registry.Client, ociDNS, chart, version string) (bool, error) {
// Once issue is resolved: https://github.com/helm/helm/issues/13368
// Replace by: helmClient.Tags(ociDNS + "/" + chart + ":" + version)
tagsURL := ociDNS + "/rancher/charts/" + chart
logger.Log(ctx, slog.LevelDebug, "checking tags",
slog.String("ociDNS", ociDNS),
slog.String("chart", chart),
slog.String("fullURL", tagsURL))
existingVersions, err := helmClient.Tags(tagsURL)
if err != nil {
if strings.Contains(err.Error(), "unexpected status code 404: name unknown: repository name not known to registry") {
Expand All @@ -311,39 +274,3 @@ func checkAsset(ctx context.Context, helmClient *registry.Client, ociDNS, chart,

return false, nil
}

// checkRegistryTagExists checks if a given source already exists at the target Registry
func (o *oci) checkRegistryTagExists(ctx context.Context, ociDNS, chart, tag string) (bool, error) {
var nameOpts []name.Option
nameOpts = append(nameOpts, name.StrictValidation)
nameOpts = append(nameOpts, name.Insecure)

ociTag := strings.ReplaceAll(tag, "+", "_")

// Build repository reference first (host + path, no tag)
repoStr := ociDNS + "/rancher/charts/" + chart
repo, err := name.NewRepository(repoStr, nameOpts...)
if err != nil {
logger.Log(ctx, slog.LevelError, "failed to parse repository", logger.Err(err))
return false, err
}
// Then create tag reference from repository
dst := repo.Tag(ociTag)

// ----------------------------------------------------
exist := true
if _, err := remote.Head(dst, o.registryOptions...); err != nil {
exist = false

var te *transport.Error
if errors.As(err, &te) && te.StatusCode == http.StatusNotFound {
// 404s are not treated as errors, means the img/tag does not exist
err = nil
} else {
logger.Log(ctx, slog.LevelError, "failure to check prime tag", logger.Err(err))
}
}

logger.Log(ctx, slog.LevelDebug, "checking", slog.Bool("exist", exist), slog.String("dst", dst.Name()))
return exist, err
}
Loading
Loading