Skip to content

Commit

Permalink
Retag referenced images when they are relocated (#3231)
Browse files Browse the repository at this point in the history
* feat: Retag referenced images when they are relocated

During relocation of referenced images, the images will no longer loose
their tag and name.

Signed-off-by: Kim Christensen <kimworking@gmail.com>

* Make unstable unit test stable

Signed-off-by: Kim Christensen <kimworking@gmail.com>

---------

Signed-off-by: Kim Christensen <kimworking@gmail.com>
Co-authored-by: schristoff <28318173+schristoff@users.noreply.github.com>
  • Loading branch information
kichristensen and schristoff authored Dec 11, 2024
1 parent ebd9d78 commit 6f85971
Show file tree
Hide file tree
Showing 37 changed files with 1,644 additions and 126 deletions.
2 changes: 2 additions & 0 deletions cmd/porter/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ The docker driver builds the bundle image using the local Docker host. To use a
"Define an individual key-value pair for the custom section in the form of NAME=VALUE. Use dot notation to specify a nested custom field. May be specified multiple times. Max length is 5,000 characters when used as a build argument.")
f.BoolVar(&opts.InsecureRegistry, "insecure-registry", false,
"Don't require TLS when pulling referenced images")
f.BoolVar(&opts.PreserveTags, "preserve-tags", false, "Preserve the original tag name on referenced images")

// Allow configuring the --driver flag with build-driver, to avoid conflicts with other commands
cmd.Flag("driver").Annotations = map[string][]string{
Expand Down Expand Up @@ -180,6 +181,7 @@ Note: if overrides for registry/tag/reference are provided, this command only re
}
f.BoolVar(&opts.AutoBuildDisabled, "autobuild-disabled", false, "Do not automatically build the bundle from source when the last build is out-of-date.")
f.BoolVar(&opts.SignBundle, "sign-bundle", false, "Sign the bundle using the configured signing plugin")
f.BoolVar(&opts.PreserveTags, "preserve-tags", false, "Preserve the original tag name on referenced images")

return &cmd
}
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/references/cli/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ porter build [flags]
--name string Override the bundle name
--no-cache Do not use the Docker cache when building the bundle image.
--no-lint Do not run the linter
--preserve-tags Preserve the original tag name on referenced images
--secret stringArray Secret file to expose to the build (format: id=mysecret,src=/local/secret). Custom values are accessible as build arguments in the template Dockerfile and in the manifest using template variables. May be specified multiple times.
--ssh stringArray SSH agent socket or keys to expose to the build (format: default|<id>[=<socket>|<key>[,<key>]]). May be specified multiple times.
--version string Override the bundle version
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/references/cli/bundles_build.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ porter bundles build [flags]
--name string Override the bundle name
--no-cache Do not use the Docker cache when building the bundle image.
--no-lint Do not run the linter
--preserve-tags Preserve the original tag name on referenced images
--secret stringArray Secret file to expose to the build (format: id=mysecret,src=/local/secret). Custom values are accessible as build arguments in the template Dockerfile and in the manifest using template variables. May be specified multiple times.
--ssh stringArray SSH agent socket or keys to expose to the build (format: default|<id>[=<socket>|<key>[,<key>]]). May be specified multiple times.
--version string Override the bundle version
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/references/cli/publish.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ porter publish [flags]
--force Force push the bundle to overwrite the previously published bundle
-h, --help help for publish
--insecure-registry Don't require TLS for the registry
--preserve-tags Preserve the original tag name on referenced images
-r, --reference string Use a bundle in an OCI registry specified by the given reference.
--registry string Override the registry portion of the bundle reference, e.g. docker.io, myregistry.com/myorg
--sign-bundle Sign the bundle using the configured signing plugin
Expand Down
10 changes: 10 additions & 0 deletions pkg/cnab/cnab-to-oci/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,13 @@ func (o RegistryOptions) toCraneOptions() []crane.Option {
}
return result
}

type PushBundleOptions struct {
RegistryOptions
}

func WithRegistryOptions(registryOpts RegistryOptions) func(*PushBundleOptions) {
return func(opts *PushBundleOptions) {
opts.RegistryOptions = registryOpts
}
}
54 changes: 54 additions & 0 deletions pkg/cnab/cnab-to-oci/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"get.porter.sh/porter/pkg/cnab"
configadapter "get.porter.sh/porter/pkg/cnab/config-adapter"
"get.porter.sh/porter/pkg/portercontext"
"get.porter.sh/porter/pkg/tracing"
"github.com/cnabio/cnab-go/driver/docker"
Expand Down Expand Up @@ -99,6 +100,7 @@ func (r *Registry) PullBundle(ctx context.Context, ref cnab.OCIReference, opts R
return bundleRef, nil
}

// PushBundle pushes a bundle to an OCI registry.
func (r *Registry) PushBundle(ctx context.Context, bundleRef cnab.BundleReference, opts RegistryOptions) (cnab.BundleReference, error) {
ctx, log := tracing.StartSpan(ctx)
defer log.EndSpan()
Expand Down Expand Up @@ -158,6 +160,17 @@ func (r *Registry) PushBundle(ctx context.Context, bundleRef cnab.BundleReferenc
}
bundleRef.Digest = d.Digest

stamp, err := configadapter.LoadStamp(bundleRef.Definition)
if err != nil {
return cnab.BundleReference{}, log.Errorf("error loading stamp from bundle: %w", err)
}
if stamp.PreserveTags {
err = preserveRelocatedImageTags(ctx, bundleRef, opts)
if err != nil {
return cnab.BundleReference{}, log.Error(fmt.Errorf("error preserving tags on relocated images: %w", err))
}
}

log.Infof("Bundle %s pushed successfully, with digest %q\n", bundleRef.Reference, d.Digest)
return bundleRef, nil
}
Expand Down Expand Up @@ -466,3 +479,44 @@ func GetInsecureRegistryTransport() *http.Transport {
skipTLS.TLSClientConfig.InsecureSkipVerify = true
return skipTLS
}

func preserveRelocatedImageTags(ctx context.Context, bundleRef cnab.BundleReference, opts RegistryOptions) error {
_, log := tracing.StartSpan(ctx)
defer log.EndSpan()

if len(bundleRef.Definition.Images) <= 0 {
log.Debugf("No images to preserve tags on")
return nil
}

log.Infof("Tagging relocated images...")
for _, image := range bundleRef.Definition.Images {
imageRef, err := cnab.ParseOCIReference(image.Image)
if err != nil {
return log.Errorf("error parsing image reference %s: %w", image.Image, err)
}

if !imageRef.HasTag() {
log.Debugf("Image %s has no tag, skipping", imageRef)
continue
}

if relocImage, ok := bundleRef.RelocationMap[image.Image]; ok {
relocRef, err := cnab.ParseOCIReference(relocImage)
if err != nil {
return log.Errorf("error parsing image reference %s: %w", relocImage, err)
}

dstRef := fmt.Sprintf("%s/%s:%s", relocRef.Registry(), imageRef.Repository(), imageRef.Tag())
log.Debugf("Copying image %s to %s", relocRef, dstRef)
err = crane.Copy(relocRef.String(), dstRef, opts.toCraneOptions()...)
if err != nil {
return log.Errorf("error copying image %s to %s: %w", relocRef, dstRef, err)
}
} else {
log.Debugf("No relocation for image %s", imageRef)
}
}

return nil
}
5 changes: 4 additions & 1 deletion pkg/cnab/config-adapter/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,30 @@ type ManifestConverter struct {
Manifest *manifest.Manifest
ImageDigests map[string]string
InstalledMixins []mixin.Metadata
PreserveTags bool
}

func NewManifestConverter(
config *config.Config,
manifest *manifest.Manifest,
imageDigests map[string]string,
mixins []mixin.Metadata,
preserveTags bool,
) *ManifestConverter {
return &ManifestConverter{
config: config,
Manifest: manifest,
ImageDigests: imageDigests,
InstalledMixins: mixins,
PreserveTags: preserveTags,
}
}

func (c *ManifestConverter) ToBundle(ctx context.Context) (cnab.ExtendedBundle, error) {
ctx, span := tracing.StartSpan(ctx)
defer span.EndSpan()

stamp, err := c.GenerateStamp(ctx)
stamp, err := c.GenerateStamp(ctx, c.PreserveTags)
if err != nil {
return cnab.ExtendedBundle{}, span.Error(err)
}
Expand Down
Loading

0 comments on commit 6f85971

Please sign in to comment.