Skip to content

Commit

Permalink
feat: Introduce common serve and push bundle commands (#359)
Browse files Browse the repository at this point in the history
Co-authored-by: Dimitri Koshkin <dimitri.koshkin@gmail.com>
  • Loading branch information
jimmidyson and dkoshkin committed Mar 14, 2023
1 parent a13862b commit 84ad6e3
Show file tree
Hide file tree
Showing 17 changed files with 290 additions and 452 deletions.
56 changes: 48 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,32 @@ windows/arm64
All images in the images config file must support all the requested platforms.

The output file will be a tarball that can be seeded into a registry,
or that can be untarred and used as the storage directory for a Docker registry
or that can be untarred and used as the storage directory for an OCI registry
served via `registry:2`.

#### Pushing an image bundle

***This command is deprecated - see [Pushing a bundle](#pushing-a-bundle-supports-both-image-or-helm-chart)***

```shell
mindthegap push image-bundle --image-bundle <path/to/images.tar> \
--to-registry <registry.address> \
[--to-registry-insecure-skip-tls-verify]
```

All images in the image bundle tar file will be pushed to the target docker registry.
All images in the image bundle tar file will be pushed to the target OCI registry.

#### Serving an image bundle

***This command is deprecated - see [Serving a bundle](#serving-a-bundle-supports-both-image-or-helm-chart)***

```shell
mindthegap serve image-bundle --image-bundle <path/to/images.tar> \
[--listen-address <listen.address>] \
[--listen-port <listen.port>]
```

Start a Docker registry serving the contents of the image bundle. Note that the Docker registry will
Start an OCI registry serving the contents of the image bundle. Note that the OCI registry will
be in read-only mode to reflect the source of the data being a static tarball so pushes to this
registry will fail.

Expand Down Expand Up @@ -96,31 +100,67 @@ See the [example helm-charts.yaml](helm-example.yaml) for the structure of the
Helm charts config file.

The output file will be a tarball that can be seeded into a registry,
or that can be untarred and used as the storage directory for a Docker registry
or that can be untarred and used as the storage directory for an OCI registry
served via `registry:2`.

#### Pushing a Helm chart bundle

***This command is deprecated - see [Pushing a bundle](#pushing-a-bundle-supports-both-image-or-helm-chart)***

```shell
mindthegap push helm-bundle --image-bundle <path/to/helm-charts.tar> \
--to-registry <registry.address> \
[--to-registry-insecure-skip-tls-verify]
```

All Helm charts in the bundle tar file will be pushed to the target OCI registry.

#### Serving a Helm chart bundle

***This command is deprecated - see [Serving a bundle](#serving-a-bundle-supports-both-image-or-helm-chart)***

```shell
mindthegap serve helm-bundle --helm-bundle <path/to/helm-charts.tar> \
[--listen-address <addr>] \
[--list-port <port>] \
[--tls-cert-file <path/to/cert/file> --tls-private-key-file <path/to/key/file>]
```

Start a Docker registry serving the contents of the image bundle. Note that the Docker registry will
Start an OCI registry serving the contents of the image bundle. Note that the OCI registry will
be in read-only mode to reflect the source of the data being a static tarball so pushes to this
registry will fail.

### Pushing a bundle (supports both image or Helm chart)

```shell
mindthegap push bundle --bundle <path/to/bundle.tar> \
--to-registry <registry.address> \
[--to-registry-insecure-skip-tls-verify]
```

All images in an image bundle tar file, or Helm charts in a chart bundle, will be pushed to the target OCI registry.

### Serving a bundle (supports both image or Helm chart)

```shell
mindthegap serve bundle --bundle <path/to/bundle.tar> \
[--listen-address <listen.address>] \
[--listen-port <listen.port>]
```

Start an OCI registry serving the contents of the image bundle or Helm charts bundle. Note that the OCI registry will
be in read-only mode to reflect the source of the data being a static tarball so pushes to this
registry will fail.

## How does it work?

`mindthegap` starts up a [Docker registry](https://docs.docker.com/registry/)
`mindthegap` starts up an [OCI registry](https://docs.docker.com/registry/)
and then uses [`crane`](https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane.md)
as a library to copy the specified images for all specified platforms into the running registry. The
resulting registry storage is then tarred up, resulting in a tarball of the specified images.

The resulting tarball can be loaded into a running Docker registry, or
be used as the initial storage for running your own registry from via Docker
The resulting tarball can be loaded into a running OCI registry, or
be used as the initial storage for running your own registry via Docker
or in a Kubernetes cluster.

## Building
Expand Down
2 changes: 1 addition & 1 deletion cmd/mindthegap/importcmd/imagebundle/image_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func NewCommand(out output.Output) *cobra.Command {
cleaner.AddCleanupFn(func() { _ = os.RemoveAll(ociExportsTempDir) })

// Import the images from the merged bundle config.
for registryName, registryConfig := range cfg {
for registryName, registryConfig := range *cfg {
for imageName, imageTags := range registryConfig.Images {
for _, imageTag := range imageTags {
srcImageName := fmt.Sprintf("%s/%s:%s", reg.Address(), imageName, imageTag)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2021 D2iQ, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package imagebundle
package bundle

import (
"fmt"
Expand All @@ -26,9 +26,9 @@ import (
"github.com/mesosphere/mindthegap/images/httputils"
)

func NewCommand(out output.Output) *cobra.Command {
func NewCommand(out output.Output, bundleCmdName string) *cobra.Command {
var (
imageBundleFiles []string
bundleFiles []string
destRegistryURI flags.RegistryURI
destRegistryCACertificateFile string
destRegistrySkipTLSVerify bool
Expand All @@ -38,26 +38,26 @@ func NewCommand(out output.Output) *cobra.Command {
)

cmd := &cobra.Command{
Use: "image-bundle",
Short: "Push images from an image bundle into an existing OCI registry",
Use: bundleCmdName,
Short: "Push from bundles into an existing OCI registry",
RunE: func(cmd *cobra.Command, args []string) error {
cleaner := cleanup.NewCleaner()
defer cleaner.Cleanup()

out.StartOperation("Creating temporary directory")
tempDir, err := os.MkdirTemp("", ".image-bundle-*")
tempDir, err := os.MkdirTemp("", ".bundle-*")
if err != nil {
out.EndOperation(false)
return fmt.Errorf("failed to create temporary directory: %w", err)
}
cleaner.AddCleanupFn(func() { _ = os.RemoveAll(tempDir) })
out.EndOperation(true)

imageBundleFiles, err = utils.FilesWithGlobs(imageBundleFiles)
bundleFiles, err = utils.FilesWithGlobs(bundleFiles)
if err != nil {
return err
}
cfg, _, err := utils.ExtractBundles(tempDir, out, imageBundleFiles...)
imagesCfg, chartsCfg, err := utils.ExtractBundles(tempDir, out, bundleFiles...)
if err != nil {
return err
}
Expand Down Expand Up @@ -125,20 +125,41 @@ func NewCommand(out output.Output) *cobra.Command {
)
}

return pushImages(
cfg,
reg.Address(),
destRegistryURI.Address(),
remoteOpts,
out,
prePushFuncs...,
)
if imagesCfg != nil {
err := pushImages(
*imagesCfg,
reg.Address(),
destRegistryURI.Address(),
remoteOpts,
out,
prePushFuncs...,
)
if err != nil {
return err
}
}

if chartsCfg != nil {
err := pushOCIArtifacts(
*chartsCfg,
fmt.Sprintf("%s/charts", reg.Address()),
destRegistryURI.Address(),
remoteOpts,
out,
prePushFuncs...,
)
if err != nil {
return err
}
}

return nil
},
}

cmd.Flags().StringSliceVar(&imageBundleFiles, "image-bundle", nil,
cmd.Flags().StringSliceVar(&bundleFiles, bundleCmdName, nil,
"Tarball containing list of images to push. Can also be a glob pattern.")
_ = cmd.MarkFlagRequired("image-bundle")
_ = cmd.MarkFlagRequired(bundleCmdName)
cmd.Flags().Var(&destRegistryURI, "to-registry", "Registry to push images to. "+
"TLS verification will be skipped when using an http:// registry.")
_ = cmd.MarkFlagRequired("to-registry")
Expand Down Expand Up @@ -228,3 +249,68 @@ func pushImages(

return nil
}

func pushOCIArtifacts(
cfg config.HelmChartsConfig,
sourceRegistry, destRegistry string,
remoteOpts []remote.Option,
out output.Output,
prePushFuncs ...prePushFunc,
) error {
// Sort repositories for deterministic ordering.
repoNames := cfg.SortedRepositoryNames()

for _, repoName := range repoNames {
repoConfig := cfg.Repositories[repoName]

// Sort charts for deterministic ordering.
chartNames := repoConfig.SortedChartNames()

for _, chartName := range chartNames {
chartVersions := repoConfig.Charts[chartName]

for _, prePush := range prePushFuncs {
if err := prePush("", destRegistry); err != nil {
return fmt.Errorf("pre-push func failed: %w", err)
}
}

for _, chartVersion := range chartVersions {
out.StartOperation(
fmt.Sprintf("Copying %s:%s (from bundle) to %s/%s:%s",
chartName, chartVersion,
destRegistry, chartName, chartVersion,
),
)

srcChart := fmt.Sprintf("%s/%s:%s", sourceRegistry, chartName, chartVersion)
srcChartRef, err := name.ParseReference(srcChart, name.StrictValidation)
if err != nil {
out.EndOperation(false)
return err
}
src, err := remote.Image(srcChartRef)
if err != nil {
out.EndOperation(false)
return err
}

destChart := fmt.Sprintf("%s/%s:%s", destRegistry, chartName, chartVersion)
destChartRef, err := name.ParseReference(destChart, name.StrictValidation)
if err != nil {
out.EndOperation(false)
return err
}

if err := remote.Write(destChartRef, src, remoteOpts...); err != nil {
out.EndOperation(false)
return err
}

out.EndOperation(true)
}
}
}

return nil
}
Loading

0 comments on commit 84ad6e3

Please sign in to comment.