Skip to content

Commit

Permalink
Merge pull request #975 from rancher/add_oci_helm_chart_support
Browse files Browse the repository at this point in the history
Add basic support for OCI registry based charts
  • Loading branch information
thardeck authored Sep 16, 2022
2 parents 21ac121 + ef5082b commit 941f0a5
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 21 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
.idea
*.DS_Store
/fleet
/.vscode
4 changes: 3 additions & 1 deletion docs/gitrepo-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ kustomize:
dir: ./kustomize

helm:
# Use a custom location for the Helm chart. This can refer to any go-getter URL.
# Use a custom location for the Helm chart. This can refer to any go-getter URL or
# OCI registry based helm chart URL e.g. "oci://ghcr.io/fleetrepoci/guestbook".
# This allows one to download charts from most any location. Also know that
# go-getter URL supports adding a digest to validate the download. If repo
# is set below this field is the name of the chart to lookup
Expand All @@ -73,6 +74,7 @@ helm:
releaseName: my-release
# The version of the chart or semver constraint of the chart to find. If a constraint
# is specified it is evaluated each time git changes.
# The version also determines which chart to download from OCI registries.
version: 0.1.0
# Any values that should be placed in the `values.yaml` and passed to helm during
# install.
Expand Down
16 changes: 16 additions & 0 deletions e2e/assets/single-cluster/helm-oci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
kind: GitRepo
apiVersion: fleet.cattle.io/v1alpha1
metadata:
name: helm
spec:
repo: https://github.com/rancher/fleet-examples
branch: add-helm-oci-example
paths:
- single-cluster/helm-oci
targets:
- clusterSelector:
matchExpressions:
- key: provider.cattle.io
operator: NotIn
values:
- harvester
13 changes: 13 additions & 0 deletions e2e/single-cluster/single_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ var _ = Describe("Single Cluster Examples", func() {

})

Context("containing an oci based helm chart", func() {
BeforeEach(func() {
asset = "single-cluster/helm-oci.yaml"
})

It("deploys the helm chart", func() {
Eventually(func() string {
out, _ := k.Namespace("fleet-helm-oci-example").Get("pods")
return out
}, testenv.Timeout).Should(ContainSubstring("frontend-"))
})
})

When("creating a gitrepo resource", func() {
Context("containing a helm chart", func() {
BeforeEach(func() {
Expand Down
6 changes: 3 additions & 3 deletions pkg/bundle/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func Open(ctx context.Context, name, baseDir, file string, opts *Options) (*Bund
}

if file == "-" {
return Read(ctx, name, baseDir, os.Stdin, opts)
return mayCompress(ctx, name, baseDir, os.Stdin, opts)
}

var (
Expand All @@ -65,7 +65,7 @@ func Open(ctx context.Context, name, baseDir, file string, opts *Options) (*Bund
in = f
}

return Read(ctx, name, baseDir, in, opts)
return mayCompress(ctx, name, baseDir, in, opts)
}

// Try accessing the documented, primary fleet.yaml extension first. If that returns an "IsNotExist" error, then we
Expand All @@ -89,7 +89,7 @@ func setupIOReader(baseDir string) (*os.File, error) {
return nil, nil
}

func Read(ctx context.Context, name, baseDir string, bundleSpecReader io.Reader, opts *Options) (*Bundle, error) {
func mayCompress(ctx context.Context, name, baseDir string, bundleSpecReader io.Reader, opts *Options) (*Bundle, error) {
if opts == nil {
opts = &Options{}
}
Expand Down
68 changes: 52 additions & 16 deletions pkg/bundle/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net/url"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"sync"
Expand All @@ -21,6 +22,9 @@ import (
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/downloader"
helmgetter "helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/repo"

"github.com/rancher/fleet/modules/cli/pkg/progress"
Expand Down Expand Up @@ -83,7 +87,7 @@ func readResources(ctx context.Context, spec *fleet.BundleSpec, compress bool, b
return result, nil
}

func ChartPath(helm *fleet.HelmOptions) string {
func checksum(helm *fleet.HelmOptions) string {
if helm == nil {
return "none"
}
Expand Down Expand Up @@ -186,11 +190,12 @@ func addCharts(directories []directory, base string, charts []*fleet.HelmOptions
}

directories = append(directories, directory{
prefix: ChartPath(chart),
base: base,
path: chartURL,
key: ChartPath(chart),
auth: auth,
prefix: checksum(chart),
base: base,
path: chartURL,
key: checksum(chart),
auth: auth,
version: chart.Version,
})
}
}
Expand All @@ -216,11 +221,12 @@ func addDirectory(directories []directory, base, customDir, defaultDir string) (
}

type directory struct {
prefix string
base string
path string
key string
auth Auth
prefix string
base string
path string
key string
version string
auth Auth
}

func readDirectories(ctx context.Context, compress bool, directories ...directory) (map[string][]fleet.BundleResource, error) {
Expand All @@ -241,7 +247,7 @@ func readDirectories(ctx context.Context, compress bool, directories ...director
dir := dir
eg.Go(func() error {
defer sem.Release(1)
resources, err := readDirectory(ctx, compress, dir.prefix, dir.base, dir.path, dir.auth)
resources, err := readDirectory(ctx, compress, dir.prefix, dir.base, dir.path, dir.version, dir.auth)
if err != nil {
return err
}
Expand All @@ -261,10 +267,10 @@ func readDirectories(ctx context.Context, compress bool, directories ...director
return result, eg.Wait()
}

func readDirectory(ctx context.Context, compress bool, prefix, base, name string, auth Auth) ([]fleet.BundleResource, error) {
func readDirectory(ctx context.Context, compress bool, prefix, base, name, version string, auth Auth) ([]fleet.BundleResource, error) {
var resources []fleet.BundleResource

files, err := readContent(ctx, base, name, auth)
files, err := readContent(ctx, base, name, version, auth)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -295,21 +301,36 @@ func readDirectory(ctx context.Context, compress bool, prefix, base, name string
return resources, nil
}

func readContent(ctx context.Context, base, name string, auth Auth) (map[string][]byte, error) {
func readContent(ctx context.Context, base, name, version string, auth Auth) (map[string][]byte, error) {
temp, err := os.MkdirTemp("", "fleet")
if err != nil {
return nil, err
}
defer os.RemoveAll(temp)

src := name

// go-getter does not support downloading OCI registry based files yet
// until this is implemented we use Helm to download charts from OCI based registries
// and provide the downloaded file to go-getter locally
hasOCIURL, err := regexp.MatchString(`^oci:\/\/`, name)
if err != nil {
return nil, err
}
if hasOCIURL {
src, err = downloadOCIChart(name, version, temp)
if err != nil {
return nil, err
}
}

temp = filepath.Join(temp, "content")

base, err = filepath.Abs(base)
if err != nil {
return nil, err
}

src := name
if auth.SSHPrivateKey != nil {
if !strings.ContainsAny(src, "?") {
src += "?"
Expand Down Expand Up @@ -387,6 +408,21 @@ func readContent(ctx context.Context, base, name string, auth Auth) (map[string]
return files, nil
}

// downloadOciChart uses Helm to download charts from OCI based registries
func downloadOCIChart(name, version, path string) (string, error) {
c := downloader.ChartDownloader{
Verify: downloader.VerifyNever,
Getters: helmgetter.All(&cli.EnvSettings{}),
}

saved, _, err := c.DownloadTo(name, version, path)
if err != nil {
return "", err
}

return saved, nil
}

func newHttpGetter(auth Auth) *getter.HttpGetter {
httpGetter := &getter.HttpGetter{
Client: &http.Client{},
Expand Down
2 changes: 1 addition & 1 deletion pkg/bundle/style.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func chartPath(options fleet.BundleDeploymentOptions) (string, string) {
if options.Helm == nil || options.Helm.Chart == "" {
return chartYAML, ""
}
return joinAndClean(options.Helm.Chart, chartYAML), ChartPath(options.Helm) + "/"
return joinAndClean(options.Helm.Chart, chartYAML), checksum(options.Helm) + "/"
}

func kustomizePath(options fleet.BundleDeploymentOptions) string {
Expand Down

0 comments on commit 941f0a5

Please sign in to comment.