diff --git a/cmd/dt/relocate/relocate.go b/cmd/dt/relocate/relocate.go index 654dde5..048b624 100644 --- a/cmd/dt/relocate/relocate.go +++ b/cmd/dt/relocate/relocate.go @@ -12,6 +12,7 @@ import ( // NewCmd builds a new relocate command func NewCmd(cfg *config.Config) *cobra.Command { valuesFiles := []string{"values.yaml"} + skipImageRefs := false cmd := &cobra.Command{ Use: "relocate CHART_PATH OCI_URI", Short: "Relocates a Helm chart", @@ -35,6 +36,7 @@ func NewCmd(cfg *config.Config) *cobra.Command { relocator.WithLog(l), relocator.Recursive, relocator.WithAnnotationsKey(cfg.AnnotationsKey), relocator.WithValuesFiles(valuesFiles...), + relocator.WithSkipImageRefs(skipImageRefs), ) }); err != nil { return l.Failf("failed to relocate Helm chart %q: %w", chartPath, err) @@ -46,6 +48,7 @@ func NewCmd(cfg *config.Config) *cobra.Command { } cmd.PersistentFlags().StringSliceVar(&valuesFiles, "values", valuesFiles, "values files to relocate images (can specify multiple)") + cmd.PersistentFlags().BoolVar(&skipImageRefs, "skip-image-refs", skipImageRefs, "Skip updating image references in values.yaml and Images.lock") return cmd } diff --git a/cmd/dt/unwrap/unwrap.go b/cmd/dt/unwrap/unwrap.go index d1c87c7..447c530 100644 --- a/cmd/dt/unwrap/unwrap.go +++ b/cmd/dt/unwrap/unwrap.go @@ -42,6 +42,8 @@ type Config struct { TempDirectory string Version string Carvelize bool + SkipImageRefs bool + SkipImages bool KeepArtifacts bool FetchArtifacts bool Auth Auth @@ -139,6 +141,20 @@ func WithCarvelize(carvelize bool) func(c *Config) { } } +// WithSkipImageRefs configures the WithSkipImageRefs of the WrapConfig +func WithSkipImageRefs(skipimagerefs bool) func(c *Config) { + return func(c *Config) { + c.SkipImageRefs = skipimagerefs + } +} + +// WithSkipImages configures the WithSkipImages of the WrapConfig +func WithSkipImages(skipimages bool) func(c *Config) { + return func(c *Config) { + c.SkipImages = skipimages + } +} + // WithFetchArtifacts configures the FetchArtifacts of the WrapConfig func WithFetchArtifacts(fetchArtifacts bool) func(c *Config) { return func(c *Config) { @@ -279,6 +295,7 @@ func unwrapChart(inputChart, registryURL, pushChartURL string, opts ...Option) ( return relocator.RelocateChartDir( wrap.ChartDir(), registryURL, relocator.WithLog(l), relocator.Recursive, relocator.WithAnnotationsKey(cfg.AnnotationsKey), relocator.WithValuesFiles(cfg.ValuesFiles...), + relocator.WithSkipImageRefs(cfg.SkipImageRefs), ) }); err != nil { return "", l.Failf("failed to relocate %q: %w", chartPath, err) @@ -287,7 +304,7 @@ func unwrapChart(inputChart, registryURL, pushChartURL string, opts ...Option) ( images := getImageList(wrap, l) - if len(images) > 0 { + if len(images) > 0 && !cfg.SkipImages { // If we are not in interactive mode, we do not show the list of images if cfg.Interactive { showImagesSummary(images, l) @@ -435,9 +452,11 @@ func pushChart(ctx context.Context, wrap wrapping.Wrap, pushChartURL string, cfg // NewCmd returns a new unwrap command func NewCmd(cfg *config.Config) *cobra.Command { var ( - sayYes bool - pushChartURL string - version string + sayYes bool + pushChartURL string + version string + skipImageRefs bool + skipImages bool ) valuesFiles := []string{"values.yaml"} cmd := &cobra.Command{ @@ -471,6 +490,8 @@ func NewCmd(cfg *config.Config) *cobra.Command { WithTempDirectory(tempDir), WithUsePlainHTTP(cfg.UsePlainHTTP), WithValuesFiles(valuesFiles...), + WithSkipImageRefs(skipImageRefs), + WithSkipImages(skipImages), ) if err != nil { return err @@ -489,6 +510,8 @@ func NewCmd(cfg *config.Config) *cobra.Command { cmd.PersistentFlags().StringVar(&pushChartURL, "push-chart-url", pushChartURL, "push the unwrapped Helm chart to the given URL") cmd.PersistentFlags().BoolVar(&sayYes, "yes", sayYes, "respond 'yes' to any yes/no question") cmd.PersistentFlags().StringSliceVar(&valuesFiles, "values", valuesFiles, "values files to relocate images (can specify multiple)") + cmd.PersistentFlags().BoolVar(&skipImageRefs, "skip-image-refs", skipImageRefs, "Skip updating image references in values.yaml and Images.lock") + cmd.PersistentFlags().BoolVar(&skipImages, "skip-images", skipImageRefs, "Skip pushing images") return cmd } diff --git a/cmd/dt/wrap/wrap.go b/cmd/dt/wrap/wrap.go index 15d9990..4ec07bd 100644 --- a/cmd/dt/wrap/wrap.go +++ b/cmd/dt/wrap/wrap.go @@ -44,6 +44,7 @@ type Config struct { Carvelize bool KeepArtifacts bool FetchArtifacts bool + SkipImages bool Auth Auth ContainerRegistryAuth Auth OutputFile string @@ -129,6 +130,13 @@ func WithFetchArtifacts(fetchArtifacts bool) func(c *Config) { } } +// WithSkipImages configures the WithSkipImages of the WrapConfig +func WithSkipImages(skipimages bool) func(c *Config) { + return func(c *Config) { + c.SkipImages = skipimages + } +} + // WithVersion configures the Version of the WrapConfig func WithVersion(version string) func(c *Config) { return func(c *Config) { @@ -400,10 +408,11 @@ func wrapChart(inputPath string, opts ...Option) (string, error) { outputFile = filepath.Join(filepath.Dir(chartRoot), outputBaseName) } } - if err := pullImages(wrap, subCfg); err != nil { - return "", err + if !cfg.SkipImages { + if err := pullImages(wrap, subCfg); err != nil { + return "", err + } } - if cfg.Carvelize { if err := l.Section(fmt.Sprintf("Generating Carvel bundle for Helm chart %q", chartPath), func(childLog log.SectionLogger) error { return carvelize.GenerateBundle( @@ -439,7 +448,8 @@ func NewCmd(cfg *config.Config) *cobra.Command { var platforms []string var fetchArtifacts bool var carvelize bool - examples := ` # Wrap a Helm chart from a local folder + var skipImages bool + var examples = ` # Wrap a Helm chart from a local folder $ dt wrap examples/mariadb # Wrap a Helm chart in an OCI registry @@ -475,6 +485,7 @@ This command will pull all the container images and wrap it into a single tarbal WithUsePlainHTTP(cfg.UsePlainHTTP), WithInsecure(cfg.Insecure), WithOutputFile(outputFile), WithTempDirectory(tmpDir), + WithSkipImages(skipImages), ) if err != nil { if _, ok := err.(*log.LoggedError); ok { @@ -496,6 +507,7 @@ This command will pull all the container images and wrap it into a single tarbal cmd.PersistentFlags().StringSliceVar(&platforms, "platforms", platforms, "platforms to include in the Images.lock file") cmd.PersistentFlags().BoolVar(&carvelize, "add-carvel-bundle", carvelize, "whether the wrap should include a Carvel bundle or not") cmd.PersistentFlags().BoolVar(&fetchArtifacts, "fetch-artifacts", fetchArtifacts, "fetch remote metadata and signature artifacts") + cmd.PersistentFlags().BoolVar(&skipImages, "skip-images", skipImages, "skip fetching images") return cmd } diff --git a/pkg/relocator/chart.go b/pkg/relocator/chart.go index dba6ae1..d11103f 100644 --- a/pkg/relocator/chart.go +++ b/pkg/relocator/chart.go @@ -26,6 +26,11 @@ type RelocationResult struct { } func relocateChart(chart *cu.Chart, prefix string, cfg *RelocateConfig) error { + var allErrors error + if cfg.SkipImageRefs { + return allErrors + } + valuesReplRes, err := relocateValues(chart, prefix) if err != nil { return fmt.Errorf("failed to relocate chart: %v", err) @@ -39,8 +44,6 @@ func relocateChart(chart *cu.Chart, prefix string, cfg *RelocateConfig) error { } } - var allErrors error - // TODO: Compare annotations with values replacements annotationsRelocResult, err := relocateAnnotations(chart, prefix) if err != nil { diff --git a/pkg/relocator/chart_test.go b/pkg/relocator/chart_test.go index 721d348..d25831e 100644 --- a/pkg/relocator/chart_test.go +++ b/pkg/relocator/chart_test.go @@ -84,4 +84,66 @@ func TestRelocateChartDir(t *testing.T) { assert.Equal(t, expectedData, relocatedImagesData) }) + + // create a new chart dir to reset for the SkipImageRef tests + chartDir = sb.TempFile() + require.NoError(t, tu.RenderScenario(scenarioDir, chartDir, map[string]interface{}{"ServerURL": serverURL})) + err = RelocateChartDir(chartDir, "", WithValuesFiles(valuesFiles...), WithSkipImageRefs(true)) + require.NoError(t, err) + + t.Run("Values Relocated SkipImageRef", func(t *testing.T) { + for _, valuesFile := range valuesFiles { + t.Logf("checking %s file", valuesFile) + data, err := os.ReadFile(filepath.Join(chartDir, valuesFile)) + require.NoError(t, err) + relocatedValues, err := tu.NormalizeYAML(string(data)) + require.NoError(t, err) + + expectedData, err := tu.RenderTemplateFile(filepath.Join(scenarioDir, fmt.Sprintf("%s.tmpl", valuesFile)), map[string]string{"ServerURL": serverURL}) + require.NoError(t, err) + + expectedValues, err := tu.NormalizeYAML(expectedData) + require.NoError(t, err) + assert.Equal(t, expectedValues, relocatedValues) + } + }) + + t.Run("Annotations Relocated ", func(t *testing.T) { + c, err := loader.Load(chartDir) + require.NoError(t, err) + + relocatedAnnotations, err := tu.NormalizeYAML(c.Metadata.Annotations["images"]) + require.NoError(t, err) + + require.NotEqual(t, relocatedAnnotations, "") + + expectedData, err := tu.RenderTemplateFile(filepath.Join(scenarioDir, "images.partial.tmpl"), map[string]string{"ServerURL": serverURL}) + require.NoError(t, err) + + expectedAnnotations, err := tu.NormalizeYAML(expectedData) + require.NoError(t, err) + assert.Equal(t, expectedAnnotations, relocatedAnnotations) + }) + + t.Run("ImageLock Relocated SkipImageRef", func(t *testing.T) { + data, err := os.ReadFile(filepath.Join(chartDir, "Images.lock")) + assert.NoError(t, err) + var lockData map[string]interface{} + + require.NoError(t, yaml.Unmarshal(data, &lockData)) + + imagesElemData, err := yaml.Marshal(lockData["images"]) + require.NoError(t, err) + + relocatedImagesData, err := tu.NormalizeYAML(string(imagesElemData)) + require.NoError(t, err) + + expectedData, err := tu.RenderTemplateFile(filepath.Join(scenarioDir, "lock_images.partial.tmpl"), map[string]string{"ServerURL": serverURL}) + require.NoError(t, err) + expectedData, err = tu.NormalizeYAML(expectedData) + require.NoError(t, err) + + assert.Equal(t, expectedData, relocatedImagesData) + + }) } diff --git a/pkg/relocator/options.go b/pkg/relocator/options.go index a2efce3..68a1c7a 100644 --- a/pkg/relocator/options.go +++ b/pkg/relocator/options.go @@ -13,6 +13,7 @@ type RelocateConfig struct { Log log.Logger RelocateLockFile bool Recursive bool + SkipImageRefs bool ValuesFiles []string } @@ -20,6 +21,7 @@ type RelocateConfig struct { func NewRelocateConfig(opts ...RelocateOption) *RelocateConfig { cfg := &RelocateConfig{ Log: silentLog.NewLogger(), + SkipImageRefs: false, RelocateLockFile: true, ImageLockConfig: *imagelock.NewImagesLockConfig(), ValuesFiles: []string{"values.yaml"}, @@ -46,6 +48,13 @@ func WithAnnotationsKey(str string) func(rc *RelocateConfig) { } } +// WithSkipImageRefs configures the skipImageRefs configuration +func WithSkipImageRefs(skipimagerefs bool) func(rc *RelocateConfig) { + return func(rc *RelocateConfig) { + rc.SkipImageRefs = skipimagerefs + } +} + // WithRelocateLockFile configures the RelocateLockFile configuration func WithRelocateLockFile(relocateLock bool) func(rc *RelocateConfig) { return func(rc *RelocateConfig) {