Skip to content

Commit 5009149

Browse files
Merge pull request #20466 from smarterclayton/extract
Add `oc image extract` to support release tooling
2 parents 14e38d6 + 76fbf4e commit 5009149

File tree

21 files changed

+1837
-673
lines changed

21 files changed

+1837
-673
lines changed

contrib/completions/bash/oc

Lines changed: 54 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contrib/completions/zsh/oc

Lines changed: 54 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/man/man1/.files_generated_oc

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/man/man1/oc-image-extract.1

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/oc/cli/image/append/append.go

Lines changed: 15 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import (
99
"io/ioutil"
1010
"net/http"
1111
"os"
12-
"regexp"
13-
"runtime"
1412
"strconv"
1513
"time"
1614

@@ -20,8 +18,6 @@ import (
2018

2119
"github.com/docker/distribution"
2220
distributioncontext "github.com/docker/distribution/context"
23-
"github.com/docker/distribution/manifest/manifestlist"
24-
"github.com/docker/distribution/manifest/schema1"
2521
"github.com/docker/distribution/manifest/schema2"
2622
"github.com/docker/distribution/reference"
2723
"github.com/docker/distribution/registry/client"
@@ -38,6 +34,8 @@ import (
3834
"github.com/openshift/origin/pkg/image/dockerlayer/add"
3935
"github.com/openshift/origin/pkg/image/registryclient"
4036
"github.com/openshift/origin/pkg/image/registryclient/dockercredentials"
37+
imagemanifest "github.com/openshift/origin/pkg/oc/cli/image/manifest"
38+
"github.com/openshift/origin/pkg/oc/cli/image/workqueue"
4139
)
4240

4341
var (
@@ -86,10 +84,7 @@ type AppendImageOptions struct {
8684
DropHistory bool
8785
CreatedAt string
8886

89-
OSFilter *regexp.Regexp
90-
DefaultOSFilter bool
91-
92-
FilterByOS string
87+
FilterOptions imagemanifest.FilterOptions
9388

9489
MaxPerRegistry int
9590

@@ -100,12 +95,6 @@ type AppendImageOptions struct {
10095
genericclioptions.IOStreams
10196
}
10297

103-
// schema2ManifestOnly specifically requests a manifest list first
104-
var schema2ManifestOnly = distribution.WithManifestMediaTypes([]string{
105-
manifestlist.MediaTypeManifestList,
106-
schema2.MediaTypeManifest,
107-
})
108-
10998
func NewAppendImageOptions(streams genericclioptions.IOStreams) *AppendImageOptions {
11099
return &AppendImageOptions{
111100
IOStreams: streams,
@@ -129,9 +118,10 @@ func NewCmdAppendImage(name string, streams genericclioptions.IOStreams) *cobra.
129118
}
130119

131120
flag := cmd.Flags()
121+
o.FilterOptions.Bind(flag)
122+
132123
flag.BoolVar(&o.DryRun, "dry-run", o.DryRun, "Print the actions that would be taken and exit without writing to the destination.")
133124
flag.BoolVar(&o.Insecure, "insecure", o.Insecure, "Allow push and pull operations to registries to be made over HTTP")
134-
flag.StringVar(&o.FilterByOS, "filter-by-os", o.FilterByOS, "A regular expression to control which images are mirrored. Images will be passed as '<platform>/<architecture>[/<variant>]'.")
135125

136126
flag.StringVar(&o.From, "from", o.From, "The image to use as a base. If empty, a new scratch image is created.")
137127
flag.StringVar(&o.To, "to", o.To, "The Docker repository tag to upload the appended image to.")
@@ -148,17 +138,8 @@ func NewCmdAppendImage(name string, streams genericclioptions.IOStreams) *cobra.
148138
}
149139

150140
func (o *AppendImageOptions) Complete(cmd *cobra.Command, args []string) error {
151-
pattern := o.FilterByOS
152-
if len(pattern) == 0 && !cmd.Flags().Changed("filter-by-os") {
153-
o.DefaultOSFilter = true
154-
pattern = regexp.QuoteMeta(fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH))
155-
}
156-
if len(pattern) > 0 {
157-
re, err := regexp.Compile(pattern)
158-
if err != nil {
159-
return fmt.Errorf("--filter-by-os was not a valid regular expression: %v", err)
160-
}
161-
o.OSFilter = re
141+
if err := o.FilterOptions.Complete(cmd.Flags()); err != nil {
142+
return err
162143
}
163144

164145
for _, arg := range args {
@@ -175,20 +156,6 @@ func (o *AppendImageOptions) Complete(cmd *cobra.Command, args []string) error {
175156
return nil
176157
}
177158

178-
// includeDescriptor returns true if the provided manifest should be included.
179-
func (o *AppendImageOptions) includeDescriptor(d *manifestlist.ManifestDescriptor, hasMultiple bool) bool {
180-
if o.OSFilter == nil {
181-
return true
182-
}
183-
if o.DefaultOSFilter && !hasMultiple {
184-
return true
185-
}
186-
if len(d.Platform.Variant) > 0 {
187-
return o.OSFilter.MatchString(fmt.Sprintf("%s/%s/%s", d.Platform.OS, d.Platform.Architecture, d.Platform.Variant))
188-
}
189-
return o.OSFilter.MatchString(fmt.Sprintf("%s/%s", d.Platform.OS, d.Platform.Architecture))
190-
}
191-
192159
func (o *AppendImageOptions) Run() error {
193160
var createdAt *time.Time
194161
if len(o.CreatedAt) > 0 {
@@ -256,97 +223,16 @@ func (o *AppendImageOptions) Run() error {
256223
return err
257224
}
258225
fromRepo = repo
259-
var srcDigest digest.Digest
260-
if len(from.Tag) > 0 {
261-
desc, err := repo.Tags(ctx).Get(ctx, from.Tag)
262-
if err != nil {
263-
return err
264-
}
265-
srcDigest = desc.Digest
266-
} else {
267-
srcDigest = digest.Digest(from.ID)
268-
}
269-
manifests, err := repo.Manifests(ctx)
270-
if err != nil {
271-
return err
272-
}
273-
srcManifest, err := manifests.Get(ctx, srcDigest, schema2ManifestOnly)
274-
if err != nil {
275-
return err
276-
}
277226

278-
originalSrcDigest := srcDigest
279-
srcManifests, srcManifest, srcDigest, err := processManifestList(ctx, srcDigest, srcManifest, manifests, *from, o.includeDescriptor)
227+
srcManifest, _, location, err := imagemanifest.FirstManifest(ctx, *from, repo, o.FilterOptions.Include)
280228
if err != nil {
281-
return err
229+
return fmt.Errorf("unable to read image %s: %v", from, err)
282230
}
283-
if len(srcManifests) == 0 {
284-
return fmt.Errorf("filtered all images from %s", from)
285-
}
286-
287-
var location string
288-
if srcDigest == originalSrcDigest {
289-
location = fmt.Sprintf("manifest %s", srcDigest)
290-
} else {
291-
location = fmt.Sprintf("manifest %s in manifest list %s", srcDigest, originalSrcDigest)
231+
base, layers, err = imagemanifest.ManifestToImageConfig(ctx, srcManifest, repo.Blobs(ctx), location)
232+
if err != nil {
233+
return fmt.Errorf("unable to parse image %s: %v", from, err)
292234
}
293235

294-
switch t := srcManifest.(type) {
295-
case *schema2.DeserializedManifest:
296-
if t.Config.MediaType != schema2.MediaTypeImageConfig {
297-
return fmt.Errorf("unable to append layers to images with config %s from %s", t.Config.MediaType, location)
298-
}
299-
configJSON, err := repo.Blobs(ctx).Get(ctx, t.Config.Digest)
300-
if err != nil {
301-
return fmt.Errorf("unable to find manifest for image %s: %v", *from, err)
302-
}
303-
glog.V(4).Infof("Raw image config json:\n%s", string(configJSON))
304-
config := &docker10.DockerImageConfig{}
305-
if err := json.Unmarshal(configJSON, &config); err != nil {
306-
return fmt.Errorf("the source image manifest could not be parsed: %v", err)
307-
}
308-
309-
base = config
310-
layers = t.Layers
311-
base.Size = 0
312-
for _, layer := range t.Layers {
313-
base.Size += layer.Size
314-
}
315-
316-
case *schema1.SignedManifest:
317-
if glog.V(4) {
318-
_, configJSON, _ := srcManifest.Payload()
319-
glog.Infof("Raw image config json:\n%s", string(configJSON))
320-
}
321-
if len(t.History) == 0 {
322-
return fmt.Errorf("input image is in an unknown format: no v1Compatibility history")
323-
}
324-
config := &docker10.DockerV1CompatibilityImage{}
325-
if err := json.Unmarshal([]byte(t.History[0].V1Compatibility), &config); err != nil {
326-
return err
327-
}
328-
329-
base = &docker10.DockerImageConfig{}
330-
if err := docker10.Convert_DockerV1CompatibilityImage_to_DockerImageConfig(config, base); err != nil {
331-
return err
332-
}
333-
334-
// schema1 layers are in reverse order
335-
layers = make([]distribution.Descriptor, 0, len(t.FSLayers))
336-
for i := len(t.FSLayers) - 1; i >= 0; i-- {
337-
layer := distribution.Descriptor{
338-
MediaType: schema2.MediaTypeLayer,
339-
Digest: t.FSLayers[i].BlobSum,
340-
// size must be reconstructed from the blobs
341-
}
342-
// we must reconstruct the tar sum from the blobs
343-
add.AddLayerToConfig(base, layer, "")
344-
layers = append(layers, layer)
345-
}
346-
347-
default:
348-
return fmt.Errorf("unable to append layers to images of type %T from %s", srcManifest, location)
349-
}
350236
} else {
351237
base = add.NewEmptyConfig()
352238
layers = []distribution.Descriptor{add.AddScratchLayerToConfig(base)}
@@ -445,8 +331,8 @@ func (o *AppendImageOptions) Run() error {
445331
// upload base layers in parallel
446332
stopCh := make(chan struct{})
447333
defer close(stopCh)
448-
q := newWorkQueue(o.MaxPerRegistry, stopCh)
449-
err = q.Try(func(w Try) {
334+
q := workqueue.New(o.MaxPerRegistry, stopCh)
335+
err = q.Try(func(w workqueue.Try) {
450336
for i := range layers[:numLayers] {
451337
layer := &layers[i]
452338
index := i
@@ -551,7 +437,7 @@ func (o *AppendImageOptions) Run() error {
551437
if err != nil {
552438
return fmt.Errorf("unable to upload the new image manifest: %v", err)
553439
}
554-
toDigest, err := putManifestInCompatibleSchema(ctx, manifest, to.Tag, toManifests, fromRepo.Blobs(ctx), toRepo.Named())
440+
toDigest, err := imagemanifest.PutManifestInCompatibleSchema(ctx, manifest, to.Tag, toManifests, fromRepo.Blobs(ctx), toRepo.Named())
555441
if err != nil {
556442
return fmt.Errorf("unable to convert the image to a compatible schema version: %v", err)
557443
}

0 commit comments

Comments
 (0)