mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-02-14 18:09:51 +00:00
add support for helm authentication when storing charts
This commit is contained in:
@@ -2,6 +2,7 @@ package cli
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
|
||||
"github.com/rancherfederal/hauler/cmd/hauler/cli/store"
|
||||
)
|
||||
@@ -263,7 +264,10 @@ func addStoreAddImage() *cobra.Command {
|
||||
}
|
||||
|
||||
func addStoreAddChart() *cobra.Command {
|
||||
o := &store.AddChartOpts{RootOpts: rootStoreOpts}
|
||||
o := &store.AddChartOpts{
|
||||
RootOpts: rootStoreOpts,
|
||||
ChartOpts: &action.ChartPathOptions{},
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "chart",
|
||||
|
||||
@@ -2,10 +2,10 @@ package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/spf13/cobra"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
|
||||
"github.com/rancherfederal/ocil/pkg/artifacts/file"
|
||||
"github.com/rancherfederal/ocil/pkg/artifacts/image"
|
||||
@@ -96,43 +96,53 @@ func storeImage(ctx context.Context, s *store.Layout, i v1alpha1.Image) error {
|
||||
|
||||
type AddChartOpts struct {
|
||||
*RootOpts
|
||||
Version string
|
||||
RepoURL string
|
||||
|
||||
// TODO: Support helm auth
|
||||
ChartOpts *action.ChartPathOptions
|
||||
}
|
||||
|
||||
func (o *AddChartOpts) AddFlags(cmd *cobra.Command) {
|
||||
f := cmd.Flags()
|
||||
|
||||
f.StringVarP(&o.RepoURL, "repo", "r", "", "Chart repository URL")
|
||||
f.StringVar(&o.Version, "version", "", "(Optional) Version of the chart to download, defaults to latest if not specified")
|
||||
f.StringVar(&o.ChartOpts.RepoURL, "repo", "", "chart repository url where to locate the requested chart")
|
||||
f.StringVar(&o.ChartOpts.Version, "version", "", "specify a version constraint for the chart version to use. This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0). If this is not specified, the latest version is used")
|
||||
f.BoolVar(&o.ChartOpts.Verify, "verify", false, "verify the package before using it")
|
||||
f.StringVar(&o.ChartOpts.Username, "username", "", "chart repository username where to locate the requested chart")
|
||||
f.StringVar(&o.ChartOpts.Password, "password", "", "chart repository password where to locate the requested chart")
|
||||
f.StringVar(&o.ChartOpts.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
|
||||
f.StringVar(&o.ChartOpts.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
|
||||
f.BoolVar(&o.ChartOpts.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
|
||||
f.StringVar(&o.ChartOpts.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
||||
}
|
||||
|
||||
func AddChartCmd(ctx context.Context, o *AddChartOpts, s *store.Layout, chartName string) error {
|
||||
path := ""
|
||||
if _, err := os.Stat(chartName); err == nil {
|
||||
path = chartName
|
||||
}
|
||||
// TODO: Reduce duplicates between api chart and upstream helm opts
|
||||
cfg := v1alpha1.Chart{
|
||||
Name: chartName,
|
||||
RepoURL: o.RepoURL,
|
||||
Version: o.Version,
|
||||
Path: path,
|
||||
RepoURL: o.ChartOpts.RepoURL,
|
||||
Version: o.ChartOpts.Version,
|
||||
}
|
||||
|
||||
return storeChart(ctx, s, cfg)
|
||||
return storeChart(ctx, s, cfg, o.ChartOpts)
|
||||
}
|
||||
|
||||
func storeChart(ctx context.Context, s *store.Layout, cfg v1alpha1.Chart) error {
|
||||
func storeChart(ctx context.Context, s *store.Layout, cfg v1alpha1.Chart, opts *action.ChartPathOptions) error {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
chrt, err := chart.NewChart(cfg)
|
||||
// TODO: This shouldn't be necessary
|
||||
opts.RepoURL = cfg.RepoURL
|
||||
opts.Version = cfg.Version
|
||||
|
||||
chrt, err := chart.NewChart(cfg.Name, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ref, err := reference.NewTagged(chrt.Name, chrt.Version)
|
||||
c, err := chrt.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ref, err := reference.NewTagged(c.Name(), c.Metadata.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
|
||||
"github.com/rancherfederal/ocil/pkg/store"
|
||||
@@ -106,7 +107,8 @@ func SyncCmd(ctx context.Context, o *SyncOpts, s *store.Layout) error {
|
||||
}
|
||||
|
||||
for _, ch := range cfg.Spec.Charts {
|
||||
err := storeChart(ctx, s, ch)
|
||||
// TODO: Provide a way to configure syncs
|
||||
err := storeChart(ctx, s, ch, &action.ChartPathOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -134,7 +136,7 @@ func SyncCmd(ctx context.Context, o *SyncOpts, s *store.Layout) error {
|
||||
}
|
||||
|
||||
for _, cfg := range cfg.Spec.Charts {
|
||||
tc, err := tchart.NewThickChart(cfg)
|
||||
tc, err := tchart.NewThickChart(cfg, &action.ChartPathOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ type Chart struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
RepoURL string `json:"repoURL,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
type ThickCharts struct {
|
||||
|
||||
@@ -3,6 +3,7 @@ package chart
|
||||
import (
|
||||
"github.com/rancherfederal/ocil/pkg/artifacts"
|
||||
"github.com/rancherfederal/ocil/pkg/artifacts/image"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
"github.com/rancherfederal/hauler/pkg/content/chart"
|
||||
@@ -19,8 +20,8 @@ type tchart struct {
|
||||
contents map[string]artifacts.OCI
|
||||
}
|
||||
|
||||
func NewThickChart(cfg v1alpha1.ThickChart) (artifacts.OCICollection, error) {
|
||||
o, err := chart.NewChart(cfg.Chart)
|
||||
func NewThickChart(cfg v1alpha1.ThickChart, opts *action.ChartPathOptions) (artifacts.OCICollection, error) {
|
||||
o, err := chart.NewChart(cfg.Chart.Name, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
"github.com/rancherfederal/ocil/pkg/layer"
|
||||
|
||||
"github.com/rancherfederal/ocil/pkg/consts"
|
||||
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
)
|
||||
|
||||
var _ artifacts.OCI = (*Chart)(nil)
|
||||
@@ -32,65 +30,45 @@ var _ artifacts.OCI = (*Chart)(nil)
|
||||
// Chart implements the OCI interface for Chart API objects. API spec values are
|
||||
// stored into the Repo, Name, and Version fields.
|
||||
type Chart struct {
|
||||
Repo string
|
||||
Name string
|
||||
Version string
|
||||
|
||||
path string
|
||||
annotations map[string]string
|
||||
}
|
||||
|
||||
// NewChart is a helper method that returns NewLocalChart or NewRemoteChart depending on v1alpha1.Chart contents
|
||||
func NewChart(cfg v1alpha1.Chart) (*Chart, error) {
|
||||
var (
|
||||
ch *Chart
|
||||
err error
|
||||
)
|
||||
if cfg.Path != "" {
|
||||
ch, err = NewLocalChart(cfg.Path)
|
||||
} else {
|
||||
ch, err = NewRemoteChart(cfg.Name, cfg.RepoURL, cfg.Version)
|
||||
}
|
||||
return ch, err
|
||||
}
|
||||
|
||||
func NewLocalChart(path string) (*Chart, error) {
|
||||
c, err := loader.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Chart{
|
||||
Name: c.Name(),
|
||||
Version: c.Metadata.Version,
|
||||
|
||||
path: path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewRemoteChart(name, repo, version string) (*Chart, error) {
|
||||
func NewChart(name string, opts *action.ChartPathOptions) (*Chart, error) {
|
||||
cpo := action.ChartPathOptions{
|
||||
RepoURL: repo,
|
||||
Version: version,
|
||||
RepoURL: opts.RepoURL,
|
||||
Version: opts.Version,
|
||||
|
||||
CaFile: opts.CaFile,
|
||||
CertFile: opts.CertFile,
|
||||
KeyFile: opts.KeyFile,
|
||||
InsecureSkipTLSverify: opts.InsecureSkipTLSverify,
|
||||
Keyring: opts.Keyring,
|
||||
Password: opts.Password,
|
||||
PassCredentialsAll: opts.PassCredentialsAll,
|
||||
Username: opts.Username,
|
||||
Verify: opts.Verify,
|
||||
}
|
||||
|
||||
cp, err := cpo.LocateChart(name, cli.New())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := loader.Load(cp)
|
||||
chartPath, err := cpo.LocateChart(name, cli.New())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// c, err := loader.Loader(chartPath)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// ch, err := c.Load()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
return &Chart{
|
||||
Repo: repo,
|
||||
Name: c.Name(),
|
||||
Version: c.Metadata.Version,
|
||||
|
||||
path: cp,
|
||||
}, nil
|
||||
path: chartPath,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (h *Chart) MediaType() string {
|
||||
|
||||
@@ -2,13 +2,13 @@ package chart_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/mholt/archiver/v3"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
|
||||
"github.com/rancherfederal/ocil/pkg/consts"
|
||||
|
||||
@@ -19,7 +19,7 @@ var (
|
||||
chartpath = "../../../testdata/podinfo-6.0.3.tgz"
|
||||
)
|
||||
|
||||
func TestNewLocalChart(t *testing.T) {
|
||||
func TestNewChart(t *testing.T) {
|
||||
tmpdir, err := os.MkdirTemp("", "hauler")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -30,20 +30,9 @@ func TestNewLocalChart(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := v1.Descriptor{
|
||||
MediaType: consts.ChartLayerMediaType,
|
||||
Size: 13524,
|
||||
Digest: v1.Hash{
|
||||
Algorithm: "sha256",
|
||||
Hex: "e30b95a08787de69ffdad3c232d65cfb131b5b50c6fd44295f48a078fceaa44e",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
ocispec.AnnotationTitle: filepath.Base(chartpath),
|
||||
},
|
||||
}
|
||||
|
||||
type args struct {
|
||||
path string
|
||||
name string
|
||||
opts *action.ChartPathOptions
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -54,9 +43,20 @@ func TestNewLocalChart(t *testing.T) {
|
||||
{
|
||||
name: "should create from a chart archive",
|
||||
args: args{
|
||||
path: chartpath,
|
||||
name: chartpath,
|
||||
opts: &action.ChartPathOptions{},
|
||||
},
|
||||
want: v1.Descriptor{
|
||||
MediaType: consts.ChartLayerMediaType,
|
||||
Size: 13524,
|
||||
Digest: v1.Hash{
|
||||
Algorithm: "sha256",
|
||||
Hex: "e30b95a08787de69ffdad3c232d65cfb131b5b50c6fd44295f48a078fceaa44e",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
ocispec.AnnotationTitle: "podinfo-6.0.3.tgz",
|
||||
},
|
||||
},
|
||||
want: want,
|
||||
wantErr: false,
|
||||
},
|
||||
// TODO: This isn't matching digests b/c of file timestamps not being respected
|
||||
@@ -68,10 +68,30 @@ func TestNewLocalChart(t *testing.T) {
|
||||
// want: want,
|
||||
// wantErr: false,
|
||||
// },
|
||||
{
|
||||
// TODO: Use a mock helm server
|
||||
name: "should fetch a remote chart",
|
||||
args: args{
|
||||
name: "ingress-nginx",
|
||||
opts: &action.ChartPathOptions{RepoURL: "https://kubernetes.github.io/ingress-nginx", Version: "4.0.16"},
|
||||
},
|
||||
want: v1.Descriptor{
|
||||
MediaType: consts.ChartLayerMediaType,
|
||||
Size: 38591,
|
||||
Digest: v1.Hash{
|
||||
Algorithm: "sha256",
|
||||
Hex: "b0ea91f7febc6708ad9971871d2de6e8feb2072110c3add6dd7082d90753caa2",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
ocispec.AnnotationTitle: "ingress-nginx-4.0.16.tgz",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := chart.NewLocalChart(tt.args.path)
|
||||
got, err := chart.NewChart(tt.args.name, tt.args.opts)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewLocalChart() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
@@ -88,8 +108,8 @@ func TestNewLocalChart(t *testing.T) {
|
||||
}
|
||||
desc := m.Layers[0]
|
||||
|
||||
if !reflect.DeepEqual(desc, want) {
|
||||
t.Errorf("%v | %v", desc, want)
|
||||
if !reflect.DeepEqual(desc, tt.want) {
|
||||
t.Errorf("got: %v\nwant: %v", desc, tt.want)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user