Skip to content

Commit 8d10a87

Browse files
author
Erez Freiberger
committed
Adding support for multiple dockercfg files
1 parent d8f21d1 commit 8d10a87

9 files changed

+123
-24
lines changed

cmd/image-inspector.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func main() {
1717
flag.StringVar(&inspectorOptions.DstPath, "path", inspectorOptions.DstPath, "Destination path for the image files")
1818
flag.StringVar(&inspectorOptions.Serve, "serve", inspectorOptions.Serve, "Host and port where to serve the image with webdav")
1919
flag.BoolVar(&inspectorOptions.Chroot, "chroot", inspectorOptions.Chroot, "Change root when serving the image with webdav")
20-
flag.StringVar(&inspectorOptions.DockerCfg, "dockercfg", inspectorOptions.DockerCfg, "Location of the docker configuration file")
20+
flag.Var(&inspectorOptions.DockerCfg, "dockercfg", "Location of the docker configuration files. May be specified more than once")
2121
flag.StringVar(&inspectorOptions.Username, "username", inspectorOptions.Username, "username for authenticating with the docker registry")
2222
flag.StringVar(&inspectorOptions.PasswordFile, "password-file", inspectorOptions.PasswordFile, "Location of a file that contains the password for authentication with the docker registry")
2323
flag.StringVar(&inspectorOptions.ScanType, "scan-type", inspectorOptions.ScanType, fmt.Sprintf("The type of the scan to be done on the inspected image. Available scan types are: %v", iicmd.ScanOptions))

pkg/cmd/types.go

+18-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ var (
99
ScanOptions = []string{"openscap"}
1010
)
1111

12+
// MultiStringVar is implementing flag.Value
13+
type MultiStringVar struct {
14+
Values []string
15+
}
16+
17+
func (sv *MultiStringVar) Set(s string) error {
18+
sv.Values = append(sv.Values, s)
19+
return nil
20+
}
21+
22+
func (sv *MultiStringVar) String() string {
23+
return fmt.Sprintf("%v", sv.Values)
24+
}
25+
1226
// ImageInspectorOptions is the main inspector implementation and holds the configuration
1327
// for an image inspector.
1428
type ImageInspectorOptions struct {
@@ -23,7 +37,7 @@ type ImageInspectorOptions struct {
2337
// Chroot controls whether or not a chroot is excuted when serving the image with webdav.
2438
Chroot bool
2539
// DockerCfg is the location of the docker config file.
26-
DockerCfg string
40+
DockerCfg MultiStringVar
2741
// Username is the username for authenticating to the docker registry.
2842
Username string
2943
// PasswordFile is the location of the file containing the password for authentication to the
@@ -43,7 +57,7 @@ func NewDefaultImageInspectorOptions() *ImageInspectorOptions {
4357
DstPath: "",
4458
Serve: "",
4559
Chroot: false,
46-
DockerCfg: "",
60+
DockerCfg: MultiStringVar{[]string{}},
4761
Username: "",
4862
PasswordFile: "",
4963
ScanType: "",
@@ -59,7 +73,7 @@ func (i *ImageInspectorOptions) Validate() error {
5973
if len(i.Image) == 0 {
6074
return fmt.Errorf("Docker image to inspect must be specified")
6175
}
62-
if len(i.DockerCfg) > 0 && len(i.Username) > 0 {
76+
if len(i.DockerCfg.Values) > 0 && len(i.Username) > 0 {
6377
return fmt.Errorf("Only specify dockercfg file or username/password pair for authentication")
6478
}
6579
if len(i.Username) > 0 && len(i.PasswordFile) == 0 {
@@ -77,7 +91,7 @@ func (i *ImageInspectorOptions) Validate() error {
7791
return fmt.Errorf("%s is not a directory", i.ScanResultsDir)
7892
}
7993
}
80-
for _, fl := range []string{i.DockerCfg, i.PasswordFile} {
94+
for _, fl := range append(i.DockerCfg.Values, i.PasswordFile) {
8195
if len(fl) > 0 {
8296
if _, err := os.Stat(fl); os.IsNotExist(err) {
8397
return fmt.Errorf("%s does not exists", fl)

pkg/cmd/types_test.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"strings"
45
"testing"
56
)
67

@@ -10,7 +11,7 @@ func TestValidate(t *testing.T) {
1011

1112
dockerCfgAndUsername := NewDefaultImageInspectorOptions()
1213
dockerCfgAndUsername.Image = "image"
13-
dockerCfgAndUsername.DockerCfg = "foo"
14+
dockerCfgAndUsername.DockerCfg.Set("foo")
1415
dockerCfgAndUsername.Username = "bar"
1516

1617
usernameNoPasswordFile := NewDefaultImageInspectorOptions()
@@ -28,7 +29,7 @@ func TestValidate(t *testing.T) {
2829

2930
goodConfigWithDockerCfg := NewDefaultImageInspectorOptions()
3031
goodConfigWithDockerCfg.Image = "image"
31-
goodConfigWithDockerCfg.DockerCfg = "types.go"
32+
goodConfigWithDockerCfg.DockerCfg.Set("types.go")
3233

3334
noScanTypeAndDir := NewDefaultImageInspectorOptions()
3435
noScanTypeAndDir.Image = "image"
@@ -51,7 +52,7 @@ func TestValidate(t *testing.T) {
5152

5253
noSuchFileDockercfg := NewDefaultImageInspectorOptions()
5354
noSuchFileDockercfg.Image = "image"
54-
noSuchFileDockercfg.DockerCfg = "nosuchfile"
55+
noSuchFileDockercfg.DockerCfg.Set("nosuchfile")
5556

5657
tests := map[string]struct {
5758
inspector *ImageInspectorOptions
@@ -81,4 +82,14 @@ func TestValidate(t *testing.T) {
8182
t.Errorf("%s expected to be invalid but received no error", k)
8283
}
8384
}
85+
86+
// for 100% coverage we need to test MultiStringVar::String
87+
goodConfigWithDockerCfg.DockerCfg.Set("types_test.go")
88+
if len(goodConfigWithDockerCfg.DockerCfg.Values) != 2 {
89+
t.Errorf("MultiStringVar Set didn't add to the lenght of Values")
90+
}
91+
st := goodConfigWithDockerCfg.DockerCfg.String()
92+
if !strings.Contains(st, "types.go") || !strings.Contains(st, "types_test.go") {
93+
t.Errorf("MultiStringVar Set didn't add to the right values or Strings didn't return them")
94+
}
8495
}

pkg/inspector/image-inspector.go

+31-16
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (i *defaultImageInspector) Inspect() error {
7575

7676
var imagePullAuths *docker.AuthConfigurations
7777
var authCfgErr error
78-
if imagePullAuths, authCfgErr = getAuthConfigs(i.opts.DockerCfg, i.opts.Username, i.opts.PasswordFile); authCfgErr != nil {
78+
if imagePullAuths, authCfgErr = i.getAuthConfigs(); authCfgErr != nil {
7979
return authCfgErr
8080
}
8181

@@ -330,28 +330,43 @@ func generateRandomName() (string, error) {
330330
return fmt.Sprintf("image-inspector-%016x", n), nil
331331
}
332332

333-
func getAuthConfigs(dockercfg, username, password_file string) (*docker.AuthConfigurations, error) {
333+
func appendDockerCfgConfigs(dockercfg string, cfgs *docker.AuthConfigurations) error {
334+
var imagePullAuths *docker.AuthConfigurations
335+
reader, err := os.Open(dockercfg)
336+
if err != nil {
337+
return fmt.Errorf("Unable to open docker config file: %v\n", err)
338+
}
339+
if imagePullAuths, err = docker.NewAuthConfigurations(reader); err != nil {
340+
return fmt.Errorf("Unable to parse docker config file: %v\n", err)
341+
}
342+
if len(imagePullAuths.Configs) == 0 {
343+
return fmt.Errorf("No auths were found in the given dockercfg file\n")
344+
}
345+
for name, ac := range imagePullAuths.Configs {
346+
cfgs.Configs[fmt.Sprintf("%s/%s", dockercfg, name)] = ac
347+
}
348+
reader.Close()
349+
return nil
350+
}
351+
352+
func (i *defaultImageInspector) getAuthConfigs() (*docker.AuthConfigurations, error) {
334353
imagePullAuths := &docker.AuthConfigurations{
335-
map[string]docker.AuthConfiguration{"": {}}}
336-
if dockercfg != "" {
337-
reader, err := os.Open(dockercfg)
338-
if err != nil {
339-
return nil, fmt.Errorf("Unable to open docker config file: %v\n", err)
340-
}
341-
if imagePullAuths, err = docker.NewAuthConfigurations(reader); err != nil {
342-
return nil, fmt.Errorf("Unable to parse docker config file: %v\n", err)
343-
}
344-
if len(imagePullAuths.Configs) == 0 {
345-
return nil, fmt.Errorf("No auths were found in the given dockercfg file\n")
354+
map[string]docker.AuthConfiguration{}}
355+
if len(i.opts.DockerCfg.Values) > 0 {
356+
for _, dcfgFile := range i.opts.DockerCfg.Values {
357+
if err := appendDockerCfgConfigs(dcfgFile, imagePullAuths); err != nil {
358+
return nil, err
359+
}
346360
}
347361
}
348-
if username != "" {
349-
token, err := ioutil.ReadFile(password_file)
362+
363+
if i.opts.Username != "" {
364+
token, err := ioutil.ReadFile(i.opts.PasswordFile)
350365
if err != nil {
351366
return nil, fmt.Errorf("Unable to read password file: %v\n", err)
352367
}
353368
imagePullAuths = &docker.AuthConfigurations{
354-
map[string]docker.AuthConfiguration{"": {Username: username, Password: string(token)}}}
369+
map[string]docker.AuthConfiguration{"": {Username: i.opts.Username, Password: string(token)}}}
355370
}
356371

357372
return imagePullAuths, nil

pkg/inspector/image-inspector_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package inspector
33
import (
44
"fmt"
55
docker "github.com/fsouza/go-dockerclient"
6+
iicmd "github.com/openshift/image-inspector/pkg/cmd"
67
"github.com/openshift/image-inspector/pkg/openscap"
78
"io/ioutil"
89
"os"
@@ -61,7 +62,61 @@ func TestScanImage(t *testing.T) {
6162
}
6263
}
6364
}
65+
}
66+
}
67+
68+
func TestGetAuthConfigs(t *testing.T) {
69+
goodTwoDockerCfg := iicmd.NewDefaultImageInspectorOptions()
70+
goodTwoDockerCfg.DockerCfg.Values = []string{"test/dockercfg1", "test/dockercfg2"}
71+
72+
goodUserAndPass := iicmd.NewDefaultImageInspectorOptions()
73+
goodUserAndPass.Username = "erez"
74+
goodUserAndPass.PasswordFile = "test/passwordFile1"
75+
76+
badDockerCfgMissing := iicmd.NewDefaultImageInspectorOptions()
77+
badDockerCfgMissing.DockerCfg.Values = []string{"test/dockercfg1", "test/nosuchfile"}
78+
79+
badDockerCfgWrong := iicmd.NewDefaultImageInspectorOptions()
80+
badDockerCfgWrong.DockerCfg.Values = []string{"test/dockercfg1", "test/passwordFile1"}
81+
82+
badDockerCfgNoAuth := iicmd.NewDefaultImageInspectorOptions()
83+
badDockerCfgNoAuth.DockerCfg.Values = []string{"test/dockercfg1", "test/dockercfg3"}
84+
85+
tests := map[string]struct {
86+
opts *iicmd.ImageInspectorOptions
87+
shouldFail bool
88+
}{
89+
"two dockercfg": {opts: goodTwoDockerCfg, shouldFail: false},
90+
"username and passwordFile": {opts: goodUserAndPass, shouldFail: false},
91+
"two dockercfg, one missing": {opts: badDockerCfgMissing, shouldFail: true},
92+
"two dockercfg, one wrong": {opts: badDockerCfgWrong, shouldFail: true},
93+
"two dockercfg, no auth": {opts: badDockerCfgNoAuth, shouldFail: true},
94+
}
6495

96+
for k, v := range tests {
97+
ii := &defaultImageInspector{*v.opts, InspectorMetadata{}}
98+
auths, err := ii.getAuthConfigs()
99+
if !v.shouldFail {
100+
var expectedLength int = len(v.opts.DockerCfg.Values)
101+
if len(v.opts.Username) > 0 {
102+
expectedLength = expectedLength + 1
103+
}
104+
if err != nil {
105+
t.Errorf("%s expected to validate but received %v", k, err)
106+
}
107+
var authsLen int = 0
108+
if auths != nil {
109+
authsLen = len(auths.Configs)
110+
}
111+
if auths == nil || expectedLength != authsLen {
112+
t.Errorf("%s expected len to be %d but got %d from %v",
113+
k, expectedLength, authsLen, auths)
114+
}
115+
} else {
116+
if err == nil {
117+
t.Errorf("%s should have failed be it didn't", k)
118+
}
119+
}
65120
}
66121
}
67122

pkg/inspector/test/dockercfg1

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"172.30.203.184:5000":{"username":"serviceaccount","password":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlcGxveWVyLXRva2VuLXYwY2kwIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlcGxveWVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNzJjMDJjMDQtZDY5Yy0xMWU1LWFkZTMtMDAxYTRhMjMxMzBlIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6ZGVwbG95ZXIifQ.UGfWMzha4RGsua9nJQdNajbQsEYcEuonCRHGByZ7Ibjsn1okk-KGLlEDNeWHvY_SVMVOIfeMpsFa3-4d_Mg3HPFC07T0Kiiqw5o5hEN4FEM8abegG_UADxBFBSSe9sGq93xz42t4Ib2FjcpLC1kpDr28SWHgymart37Mou6xL_b7HcUzkfQooY0NWcUM_19Vlc9kWzO0injJcm-Gn_vGMSd8sRX1fBOWzuz0u2K-fLPLtWzoE65E08VoWOExckHLOAyYQ0SDZkqQEpHZ2T5e-57C50qrenrBZ7mh9rezDq_ncayAerjaJ9UJ-BcqX7zWxW2vBKDz8zC7vc_vwZUvMw","email":"serviceaccount@example.org","auth":"c2VydmljZWFjY291bnQ6ZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUprWldaaGRXeDBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbVJsY0d4dmVXVnlMWFJ2YTJWdUxYWXdZMmt3SWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXpaWEoyYVdObExXRmpZMjkxYm5RdWJtRnRaU0k2SW1SbGNHeHZlV1Z5SWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXpaWEoyYVdObExXRmpZMjkxYm5RdWRXbGtJam9pTnpKak1ESmpNRFF0WkRZNVl5MHhNV1UxTFdGa1pUTXRNREF4WVRSaE1qTXhNekJsSWl3aWMzVmlJam9pYzNsemRHVnRPbk5sY25acFkyVmhZMk52ZFc1ME9tUmxabUYxYkhRNlpHVndiRzk1WlhJaWZRLlVHZldNemhhNFJHc3VhOW5KUWROYWpiUXNFWWNFdW9uQ1JIR0J5WjdJYmpzbjFva2stS0dMbEVETmVXSHZZX1NWTVZPSWZlTXBzRmEzLTRkX01nM0hQRkMwN1QwS2lpcXc1bzVoRU40RkVNOGFiZWdHX1VBRHhCRkJTU2U5c0dxOTN4ejQydDRJYjJGamNwTEMxa3BEcjI4U1dIZ3ltYXJ0MzdNb3U2eExfYjdIY1V6a2ZRb29ZME5XY1VNXzE5VmxjOWtXek8waW5qSmNtLUduX3ZHTVNkOHNSWDFmQk9XenV6MHUySy1mTFBMdFd6b0U2NUUwOFZvV09FeGNrSExPQXlZUTBTRFprcVFFcEhaMlQ1ZS01N0M1MHFyZW5yQlo3bWg5cmV6RHFfbmNheUFlcmphSjlVSi1CY3FYN3pXeFcydkJLRHo4ekM3dmNfdndaVXZNdw=="}}

pkg/inspector/test/dockercfg2

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"172.30.203.184:5000":{"username":"serviceaccount","password":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJtYW5hZ2VtZW50LWluZnJhIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Imluc3BlY3Rvci1hZG1pbi10b2tlbi1senphZyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJpbnNwZWN0b3ItYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJmNzRmODBjZS1mYmQzLTExZTUtOGJmZC0wMDFhNGEyMzEzMGUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6bWFuYWdlbWVudC1pbmZyYTppbnNwZWN0b3ItYWRtaW4ifQ.KjciIQ6AClvFsmbY66It_y7ULGESJWcbu0V7SzRjrKcUNTNL0yCf_0or_MP55bMcKA_y-IfhpCH8MMcAWRLLVfrrkmHotMJyhxbrC00Ud7-zvRRzd9e-FwEEvgXRgbYBIjsfj4aJd2G_CgA9fWSJWTSIvAPox-mRu1Ivxw2Vhd5WoRCL0AsakRaCoXzHkw64QJdDFm2jYcjIvFVTXZXWADPdzl8eP_rd9cZ8id2PQgW7LpGYItJq51-H-QhfclcKhhbKYxkEhfPjwlT2FmKj__l8fH8uDjJlkDSXsbWTtfNKkHKXQzRM6nTPZNUepjOV5HS8J9ZE_g-M1bOrBYRjoA","email":"serviceaccount@example.org","auth":"c2VydmljZWFjY291bnQ6ZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUp0WVc1aFoyVnRaVzUwTFdsdVpuSmhJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbWx1YzNCbFkzUnZjaTFoWkcxcGJpMTBiMnRsYmkxc2VucGhaeUlzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG01aGJXVWlPaUpwYm5Od1pXTjBiM0l0WVdSdGFXNGlMQ0pyZFdKbGNtNWxkR1Z6TG1sdkwzTmxjblpwWTJWaFkyTnZkVzUwTDNObGNuWnBZMlV0WVdOamIzVnVkQzUxYVdRaU9pSm1OelJtT0RCalpTMW1ZbVF6TFRFeFpUVXRPR0ptWkMwd01ERmhOR0V5TXpFek1HVWlMQ0p6ZFdJaU9pSnplWE4wWlcwNmMyVnlkbWxqWldGalkyOTFiblE2YldGdVlXZGxiV1Z1ZEMxcGJtWnlZVHBwYm5Od1pXTjBiM0l0WVdSdGFXNGlmUS5LamNpSVE2QUNsdkZzbWJZNjZJdF95N1VMR0VTSldjYnUwVjdTelJqcktjVU5UTkwweUNmXzBvcl9NUDU1Yk1jS0FfeS1JZmhwQ0g4TU1jQVdSTExWZnJya21Ib3RNSnloeGJyQzAwVWQ3LXp2UlJ6ZDllLUZ3RUV2Z1hSZ2JZQklqc2ZqNGFKZDJHX0NnQTlmV1NKV1RTSXZBUG94LW1SdTFJdnh3MlZoZDVXb1JDTDBBc2FrUmFDb1h6SGt3NjRRSmRERm0yalljakl2RlZUWFpYV0FEUGR6bDhlUF9yZDljWjhpZDJQUWdXN0xwR1lJdEpxNTEtSC1RaGZjbGNLaGhiS1l4a0VoZlBqd2xUMkZtS2pfX2w4Zkg4dURqSmxrRFNYc2JXVHRmTktrSEtYUXpSTTZuVFBaTlVlcGpPVjVIUzhKOVpFX2ctTTFiT3JCWVJqb0E="}}

pkg/inspector/test/dockercfg3

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

pkg/inspector/test/passwordFile1

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
some_password

0 commit comments

Comments
 (0)