Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 88d9a93

Browse files
committedNov 21, 2019
pkg/cli/admin/release/extract: Multi-arch extraction with --filter-by-os
Deprecating the previous --command-os to get consistency with other image handling. --filter-by-os originally landed in openshift/origin@e04b16527b (cli: Mirror images across registries or to S3, 2017-06-04, openshift/origin#14471). The wrapping in: o.FilterOptions.FilterByOS = fmt.Sprintf("^%s/", o.CommandOperatingSystem) guards against the unlikely case that a given --command-os value is a valid prefix for a longer OS, or matches an arch or varient or some such. Also teach oc to extract from standard locations e949088 (Enable all Linux arches in cli-artifacts, 2019-11-07, openshift#153) to avoid choking on hardlinks [1]: $ oc adm release extract --tools registry.svc.ci.openshift.org/ocp/release:4.3.0-0.ci-2019-11-20-121416 error: image did not contain usr/share/openshift/mac/oc Teaching extractTarget about architectures avoids conflicts with multiple architectures trying to use the no-longer-specific-enough oc-linux in targetsByName [2]: $ ./oc adm release extract --command=oc --command-os='*' registry.svc.ci.openshift.org/ocp/release:4.3.0-0.ci-2019-11-20-121416 error: unable to iterate over layer sha256:13caf755813923e69e25ff1a28cc65766d6dcaa12676e5e274db9ec828e55d71 from registry.svc.ci.openshift.org/ocp/4.3-2019-11-20-121416@sha256:6497d5cb7102903baf98bbc0e07144f04827a21dcd800ff61360d25228a2d2ce: unable to find target with mapping name openshift-client-linux-4.3.0-0.ci-2019-11-20-121416.tar.gz Adding --command-arch on top of that allows us to set currentArch to avoid extracting 'oc' for multiple architectures all into the same file (making it unlikely that you get the architecture you want ;). Updated the completions with: $ make build $ hack/update-generated-completions.sh [1]: https://bugzilla.redhat.com/show_bug.cgi?id=1774642 [2]: openshift#172 (comment)
1 parent 386b42b commit 88d9a93

File tree

4 files changed

+126
-83
lines changed

4 files changed

+126
-83
lines changed
 

‎contrib/completions/bash/oc

+3
Original file line numberDiff line numberDiff line change
@@ -5360,6 +5360,9 @@ _oc_adm_release_extract()
53605360
flags+=("--file=")
53615361
two_word_flags+=("--file")
53625362
local_nonpersistent_flags+=("--file=")
5363+
flags+=("--filter-by-os=")
5364+
two_word_flags+=("--filter-by-os")
5365+
local_nonpersistent_flags+=("--filter-by-os=")
53635366
flags+=("--from=")
53645367
two_word_flags+=("--from")
53655368
local_nonpersistent_flags+=("--from=")

‎contrib/completions/zsh/oc

+3
Original file line numberDiff line numberDiff line change
@@ -5502,6 +5502,9 @@ _oc_adm_release_extract()
55025502
flags+=("--file=")
55035503
two_word_flags+=("--file")
55045504
local_nonpersistent_flags+=("--file=")
5505+
flags+=("--filter-by-os=")
5506+
two_word_flags+=("--filter-by-os")
5507+
local_nonpersistent_flags+=("--filter-by-os=")
55055508
flags+=("--from=")
55065509
two_word_flags+=("--from")
55075510
local_nonpersistent_flags+=("--from=")

‎pkg/cli/admin/release/extract.go

+38-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package release
22

33
import (
44
"archive/tar"
5+
"errors"
56
"fmt"
67
"io"
78
"os"
@@ -41,13 +42,13 @@ func NewExtract(f kcmdutil.Factory, parentName string, streams genericclioptions
4142
must be installed on the cluster for a given version.
4243
4344
The --tools and --command flags allow you to extract the appropriate client binaries
44-
for your operating system to disk. --tools will create archive files containing the
45-
current OS tools (or, if --command-os is set to '*', all OS versions). Specifying
46-
--command for either 'oc' or 'openshift-install' will extract the binaries directly.
47-
You may pass a PGP private key file with --signing-key which will create an ASCII
48-
armored sha256sum.txt.asc file describing the content that was extracted that is
49-
signed by the key. For more advanced signing use the generated sha256sum.txt and an
50-
external tool like gpg.
45+
for your operating system to disk. --tools will create archive files containing the
46+
current OS/arch tools (or, if --filter-by-os is set to '*', all OS/arch versions).
47+
Specifying --command for either 'oc' or 'openshift-install' will extract the
48+
binaries directly. You may pass a PGP private key file with --signing-key which
49+
will create an ASCII armored sha256sum.txt.asc file describing the content that was
50+
extracted that is signed by the key. For more advanced signing use the generated
51+
sha256sum.txt and an external tool like gpg.
5152
5253
Instead of extracting the manifests, you can specify --git=DIR to perform a Git
5354
checkout of the source code that comprises the release. A warning will be printed
@@ -61,11 +62,13 @@ func NewExtract(f kcmdutil.Factory, parentName string, streams genericclioptions
6162
`),
6263
Run: func(cmd *cobra.Command, args []string) {
6364
kcmdutil.CheckErr(o.Complete(f, cmd, args))
65+
kcmdutil.CheckErr(o.Validate())
6466
kcmdutil.CheckErr(o.Run())
6567
},
6668
}
6769
flags := cmd.Flags()
6870
o.SecurityOptions.Bind(flags)
71+
o.FilterOptions.Bind(flags)
6972
o.ParallelOptions.Bind(flags)
7073

7174
flags.StringVar(&o.From, "from", o.From, "Image containing the release payload.")
@@ -77,7 +80,7 @@ func NewExtract(f kcmdutil.Factory, parentName string, streams genericclioptions
7780
flags.StringVar(&o.SigningKey, "signing-key", o.SigningKey, "Sign the sha256sum.txt generated by --tools with this GPG key. A sha256sum.txt.asc file signed by this key will be created. The key is assumed to be encrypted.")
7881

7982
flags.StringVar(&o.Command, "command", o.Command, "Specify 'oc' or 'openshift-install' to extract the client for your operating system.")
80-
flags.StringVar(&o.CommandOperatingSystem, "command-os", o.CommandOperatingSystem, "Override which operating system command is extracted (mac, windows, linux). You map specify '*' to extract all tool archives.")
83+
flags.StringVar(&o.CommandOperatingSystem, "command-os", o.CommandOperatingSystem, "Deprecated: use --filter-by-os instead. Override which operating system command is extracted (mac, windows, linux). You map specify '*' to extract all tool archives.")
8184
flags.StringVar(&o.FileDir, "dir", o.FileDir, "The directory on disk that file:// images will be copied under.")
8285
return cmd
8386
}
@@ -86,12 +89,14 @@ type ExtractOptions struct {
8689
genericclioptions.IOStreams
8790

8891
SecurityOptions imagemanifest.SecurityOptions
92+
FilterOptions imagemanifest.FilterOptions
8993
ParallelOptions imagemanifest.ParallelOptions
9094

9195
From string
9296

9397
Tools bool
9498
Command string
99+
// Deprecated: Use FilterOptions instead.
95100
CommandOperatingSystem string
96101
SigningKey string
97102

@@ -106,6 +111,27 @@ type ExtractOptions struct {
106111
}
107112

108113
func (o *ExtractOptions) Complete(f kcmdutil.Factory, cmd *cobra.Command, args []string) error {
114+
if len(o.CommandOperatingSystem) > 0 {
115+
if len(o.FilterOptions.FilterByOS) > 0 {
116+
return errors.New("--command-os is deprecated and may not be set when --filter-by-os is set")
117+
}
118+
119+
switch o.CommandOperatingSystem {
120+
case "mac":
121+
o.CommandOperatingSystem = "darwin"
122+
case "*":
123+
o.CommandOperatingSystem = ".*"
124+
default:
125+
}
126+
127+
o.FilterOptions.FilterByOS = fmt.Sprintf("^%s/", o.CommandOperatingSystem)
128+
klog.Warningf("--command-os is deprecated; use --filter-by-os=%q instead", o.FilterOptions.FilterByOS)
129+
}
130+
131+
if err := o.FilterOptions.Complete(cmd.Flags()); err != nil {
132+
return err
133+
}
134+
109135
switch {
110136
case len(args) == 1 && len(o.From) > 0, len(args) > 1:
111137
return fmt.Errorf("you may only specify a single image via --from or argument")
@@ -125,6 +151,10 @@ func (o *ExtractOptions) Complete(f kcmdutil.Factory, cmd *cobra.Command, args [
125151
return nil
126152
}
127153

154+
func (o *ExtractOptions) Validate() error {
155+
return o.FilterOptions.Validate()
156+
}
157+
128158
func (o *ExtractOptions) Run() error {
129159
sources := 0
130160
if o.Tools {

‎pkg/cli/admin/release/extract_tools.go

+82-75
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"io/ioutil"
1515
"os"
1616
"path/filepath"
17-
"runtime"
1817
"sort"
1918
"strings"
2019
"sync"
@@ -37,9 +36,10 @@ import (
3736

3837
// extractTarget describes how a file in the release image can be extracted to disk.
3938
type extractTarget struct {
40-
OS string
41-
Command string
42-
Optional bool
39+
OS string
40+
Architecture string
41+
Command string
42+
Optional bool
4343

4444
InjectReleaseImage bool
4545
InjectReleaseVersion bool
@@ -138,103 +138,114 @@ var (
138138
`)
139139
)
140140

141-
// extractTools extracts specific commands out of images referenced by the release image.
141+
// extractCommand extracts specific commands out of images referenced by the release image.
142142
// TODO: in the future the metadata this command contains might be loaded from the release
143143
// image, but we must maintain compatibility with older payloads if so
144144
func (o *ExtractOptions) extractCommand(command string) error {
145145
// Available targets is treated as a GA API and may not be changed without backwards
146146
// compatibility of at least N-2 releases.
147147
availableTargets := []extractTarget{
148148
{
149-
OS: "darwin",
150-
Command: "oc",
151-
Mapping: extract.Mapping{Image: "cli-artifacts", From: "usr/share/openshift/mac/oc"},
152-
153-
LinkTo: []string{"kubectl"},
154-
Readme: readmeCLIUnix,
155-
InjectReleaseVersion: true,
156-
ArchiveFormat: "openshift-client-mac-%s.tar.gz",
157-
},
158-
{
159-
OS: "linux",
160-
Command: "oc",
161-
Mapping: extract.Mapping{Image: "cli", From: "usr/bin/oc"},
162-
163-
LinkTo: []string{"kubectl"},
164-
Readme: readmeCLIUnix,
165-
InjectReleaseVersion: true,
166-
ArchiveFormat: "openshift-client-linux-%s.tar.gz",
167-
},
168-
{
169-
OS: "windows",
170-
Command: "oc",
171-
Mapping: extract.Mapping{Image: "cli-artifacts", From: "usr/share/openshift/windows/oc.exe"},
172-
173-
Readme: readmeCLIWindows,
174-
InjectReleaseVersion: true,
175-
ArchiveFormat: "openshift-client-windows-%s.zip",
176-
AsZip: true,
177-
},
178-
{
179-
OS: "darwin",
180-
Command: "openshift-install",
181-
Mapping: extract.Mapping{Image: "installer-artifacts", From: "usr/share/openshift/mac/openshift-install"},
149+
OS: "darwin",
150+
Architecture: "amd64",
151+
Command: "openshift-install",
152+
Mapping: extract.Mapping{Image: "installer-artifacts", From: "usr/share/openshift/mac/openshift-install"},
182153

183154
Readme: readmeInstallUnix,
184155
InjectReleaseImage: true,
185156
ArchiveFormat: "openshift-install-mac-%s.tar.gz",
186157
},
187158
{
188-
OS: "linux",
189-
Command: "openshift-install",
190-
Mapping: extract.Mapping{Image: "installer", From: "usr/bin/openshift-install"},
159+
OS: "linux",
160+
Architecture: "amd64",
161+
Command: "openshift-install",
162+
Mapping: extract.Mapping{Image: "installer", From: "usr/bin/openshift-install"},
191163

192164
Readme: readmeInstallUnix,
193165
InjectReleaseImage: true,
194166
ArchiveFormat: "openshift-install-linux-%s.tar.gz",
195167
},
196168
{
197-
OS: "linux",
198-
Command: "openshift-baremetal-install",
199-
Optional: true,
200-
Mapping: extract.Mapping{Image: "baremetal-installer", From: "usr/bin/openshift-install"},
169+
OS: "linux",
170+
Architecture: "amd64",
171+
Command: "openshift-baremetal-install",
172+
Optional: true,
173+
Mapping: extract.Mapping{Image: "baremetal-installer", From: "usr/bin/openshift-install"},
201174

202175
Readme: readmeInstallUnix,
203176
InjectReleaseImage: true,
204177
ArchiveFormat: "openshift-baremetal-install-linux-%s.tar.gz",
205178
},
206179
}
207180

208-
currentOS := runtime.GOOS
209-
if len(o.CommandOperatingSystem) > 0 {
210-
currentOS = o.CommandOperatingSystem
211-
}
212-
if currentOS == "mac" {
213-
currentOS = "darwin"
181+
for _, arch := range []string{"amd64", "arm64", "ppc64le", "s390x"} {
182+
for _, operating_system := range []string{"darwin", "linux", "windows"} {
183+
archive_format_os := operating_system
184+
basename := "oc"
185+
switch operating_system {
186+
case "darwin":
187+
archive_format_os = "mac"
188+
if arch != "amd64" {
189+
continue
190+
}
191+
case "windows":
192+
basename += ".exe"
193+
if arch != "amd64" {
194+
continue
195+
}
196+
default:
197+
}
198+
199+
archiveFormat := fmt.Sprintf("openshift-client-%s-%s-%%s.tar.gz", archive_format_os, arch)
200+
if arch == "amd64" { // backwards compat with single-arch names
201+
archiveFormat = fmt.Sprintf("openshift-client-%s-%%s.tar.gz", archive_format_os)
202+
}
203+
204+
source := filepath.Join("usr/share/openshift/", fmt.Sprintf("%s_%s", operating_system, arch), basename)
205+
if operating_system == "windows" && arch == "amd64" {
206+
// old single-arch name sorts before the new standard name, so the new name is a symlink, and we have to point at the old name
207+
source = filepath.Join("usr/share/openshift/", operating_system, basename)
208+
}
209+
210+
availableTargets = append(availableTargets, extractTarget{
211+
OS: operating_system,
212+
Architecture: arch,
213+
Command: "oc",
214+
Mapping: extract.Mapping{Image: "cli-artifacts", From: source},
215+
216+
LinkTo: []string{"kubectl"},
217+
Readme: readmeCLIUnix,
218+
InjectReleaseVersion: true,
219+
ArchiveFormat: archiveFormat,
220+
})
221+
}
214222
}
215223

216224
// Select the subset of targets based on command line input
217225
var willArchive bool
218226
var targets []extractTarget
219227

220-
// Filter by command, or gather all non-optional targets
221-
if len(command) > 0 {
222-
for _, target := range availableTargets {
223-
if target.Command == command {
224-
targets = append(targets, target)
225-
}
228+
commands := sets.NewString()
229+
for _, target := range availableTargets {
230+
commands.Insert(target.Command)
231+
if len(command) > 0 && target.Command != command {
232+
continue // filter by command
233+
} else if command == "" && target.Optional {
234+
continue // no command given, gather only non-optional targets
226235
}
227-
} else {
228-
for _, target := range availableTargets {
229-
if !target.Optional {
230-
targets = append(targets, target)
231-
}
236+
237+
os_arch := fmt.Sprintf("%s/%s", target.OS, target.Architecture)
238+
if !o.FilterOptions.OSFilter.MatchString(os_arch) {
239+
klog.V(2).Infof("Skipping %s, %q does not match --filter-by-os=%q", target.ArchiveFormat, os_arch, o.FilterOptions.FilterByOS)
240+
continue
232241
}
242+
243+
targets = append(targets, target)
233244
}
234245

235-
// If the user didn't specify a command, or the operating system is set
236-
// to '*', we'll produce an archive
237-
if len(command) == 0 || o.CommandOperatingSystem == "*" {
246+
// If user didn't specify a command, we matched multiple targets,
247+
// we'll produce archives
248+
if len(command) == 0 || len(targets) > 1 {
238249
for i := range targets {
239250
targets[i].AsArchive = true
240251
targets[i].AsZip = targets[i].OS == "windows"
@@ -243,12 +254,12 @@ func (o *ExtractOptions) extractCommand(command string) error {
243254

244255
if len(targets) == 0 {
245256
switch {
246-
case len(command) > 0 && currentOS != "*":
247-
return fmt.Errorf("command %q does not support the operating system %q", o.Command, currentOS)
248-
case len(command) > 0:
249-
return fmt.Errorf("the supported commands are 'oc' and 'openshift-install'")
257+
case command == "":
258+
return fmt.Errorf("no available commands for --filter-by-os=%q", o.FilterOptions.FilterByOS)
259+
case commands.Has(command):
260+
return fmt.Errorf("command %s does not support --filter-by-os=%s", command, o.FilterOptions.FilterByOS)
250261
default:
251-
return fmt.Errorf("no available commands")
262+
return fmt.Errorf("the supported commands are: %s", strings.Join(commands.List(), ", "))
252263
}
253264
}
254265

@@ -308,10 +319,6 @@ func (o *ExtractOptions) extractCommand(command string) error {
308319
missing := sets.NewString()
309320
var validTargets []extractTarget
310321
for _, target := range targets {
311-
if currentOS != "*" && target.OS != currentOS {
312-
klog.V(2).Infof("Skipping %s, does not match current OS %s", target.ArchiveFormat, target.OS)
313-
continue
314-
}
315322
spec, err := findImageSpec(release.References, target.Mapping.Image, o.From)
316323
if err != nil {
317324
missing.Insert(target.Mapping.Image)
@@ -330,7 +337,7 @@ func (o *ExtractOptions) extractCommand(command string) error {
330337
target.Mapping.To = filepath.Join(dir, target.Mapping.Name)
331338
} else {
332339
target.Mapping.To = filepath.Join(dir, target.Command)
333-
target.Mapping.Name = fmt.Sprintf("%s-%s", target.OS, target.Command)
340+
target.Mapping.Name = fmt.Sprintf("%s-%s-%s", target.OS, target.Architecture, target.Command)
334341
}
335342
validTargets = append(validTargets, target)
336343
}

0 commit comments

Comments
 (0)
Please sign in to comment.