Skip to content

Commit

Permalink
Merge pull request #9 from enoodle/multiple_dockercfg
Browse files Browse the repository at this point in the history
Multiple dockercfg
  • Loading branch information
simon3z committed Apr 28, 2016
2 parents d8f21d1 + 8d10a87 commit 2a1c4ea
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 24 deletions.
2 changes: 1 addition & 1 deletion cmd/image-inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func main() {
flag.StringVar(&inspectorOptions.DstPath, "path", inspectorOptions.DstPath, "Destination path for the image files")
flag.StringVar(&inspectorOptions.Serve, "serve", inspectorOptions.Serve, "Host and port where to serve the image with webdav")
flag.BoolVar(&inspectorOptions.Chroot, "chroot", inspectorOptions.Chroot, "Change root when serving the image with webdav")
flag.StringVar(&inspectorOptions.DockerCfg, "dockercfg", inspectorOptions.DockerCfg, "Location of the docker configuration file")
flag.Var(&inspectorOptions.DockerCfg, "dockercfg", "Location of the docker configuration files. May be specified more than once")
flag.StringVar(&inspectorOptions.Username, "username", inspectorOptions.Username, "username for authenticating with the docker registry")
flag.StringVar(&inspectorOptions.PasswordFile, "password-file", inspectorOptions.PasswordFile, "Location of a file that contains the password for authentication with the docker registry")
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))
Expand Down
22 changes: 18 additions & 4 deletions pkg/cmd/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ var (
ScanOptions = []string{"openscap"}
)

// MultiStringVar is implementing flag.Value
type MultiStringVar struct {
Values []string
}

func (sv *MultiStringVar) Set(s string) error {
sv.Values = append(sv.Values, s)
return nil
}

func (sv *MultiStringVar) String() string {
return fmt.Sprintf("%v", sv.Values)
}

// ImageInspectorOptions is the main inspector implementation and holds the configuration
// for an image inspector.
type ImageInspectorOptions struct {
Expand All @@ -23,7 +37,7 @@ type ImageInspectorOptions struct {
// Chroot controls whether or not a chroot is excuted when serving the image with webdav.
Chroot bool
// DockerCfg is the location of the docker config file.
DockerCfg string
DockerCfg MultiStringVar
// Username is the username for authenticating to the docker registry.
Username string
// PasswordFile is the location of the file containing the password for authentication to the
Expand All @@ -43,7 +57,7 @@ func NewDefaultImageInspectorOptions() *ImageInspectorOptions {
DstPath: "",
Serve: "",
Chroot: false,
DockerCfg: "",
DockerCfg: MultiStringVar{[]string{}},
Username: "",
PasswordFile: "",
ScanType: "",
Expand All @@ -59,7 +73,7 @@ func (i *ImageInspectorOptions) Validate() error {
if len(i.Image) == 0 {
return fmt.Errorf("Docker image to inspect must be specified")
}
if len(i.DockerCfg) > 0 && len(i.Username) > 0 {
if len(i.DockerCfg.Values) > 0 && len(i.Username) > 0 {
return fmt.Errorf("Only specify dockercfg file or username/password pair for authentication")
}
if len(i.Username) > 0 && len(i.PasswordFile) == 0 {
Expand All @@ -77,7 +91,7 @@ func (i *ImageInspectorOptions) Validate() error {
return fmt.Errorf("%s is not a directory", i.ScanResultsDir)
}
}
for _, fl := range []string{i.DockerCfg, i.PasswordFile} {
for _, fl := range append(i.DockerCfg.Values, i.PasswordFile) {
if len(fl) > 0 {
if _, err := os.Stat(fl); os.IsNotExist(err) {
return fmt.Errorf("%s does not exists", fl)
Expand Down
17 changes: 14 additions & 3 deletions pkg/cmd/types_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"strings"
"testing"
)

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

dockerCfgAndUsername := NewDefaultImageInspectorOptions()
dockerCfgAndUsername.Image = "image"
dockerCfgAndUsername.DockerCfg = "foo"
dockerCfgAndUsername.DockerCfg.Set("foo")
dockerCfgAndUsername.Username = "bar"

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

goodConfigWithDockerCfg := NewDefaultImageInspectorOptions()
goodConfigWithDockerCfg.Image = "image"
goodConfigWithDockerCfg.DockerCfg = "types.go"
goodConfigWithDockerCfg.DockerCfg.Set("types.go")

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

noSuchFileDockercfg := NewDefaultImageInspectorOptions()
noSuchFileDockercfg.Image = "image"
noSuchFileDockercfg.DockerCfg = "nosuchfile"
noSuchFileDockercfg.DockerCfg.Set("nosuchfile")

tests := map[string]struct {
inspector *ImageInspectorOptions
Expand Down Expand Up @@ -81,4 +82,14 @@ func TestValidate(t *testing.T) {
t.Errorf("%s expected to be invalid but received no error", k)
}
}

// for 100% coverage we need to test MultiStringVar::String
goodConfigWithDockerCfg.DockerCfg.Set("types_test.go")
if len(goodConfigWithDockerCfg.DockerCfg.Values) != 2 {
t.Errorf("MultiStringVar Set didn't add to the lenght of Values")
}
st := goodConfigWithDockerCfg.DockerCfg.String()
if !strings.Contains(st, "types.go") || !strings.Contains(st, "types_test.go") {
t.Errorf("MultiStringVar Set didn't add to the right values or Strings didn't return them")
}
}
47 changes: 31 additions & 16 deletions pkg/inspector/image-inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (i *defaultImageInspector) Inspect() error {

var imagePullAuths *docker.AuthConfigurations
var authCfgErr error
if imagePullAuths, authCfgErr = getAuthConfigs(i.opts.DockerCfg, i.opts.Username, i.opts.PasswordFile); authCfgErr != nil {
if imagePullAuths, authCfgErr = i.getAuthConfigs(); authCfgErr != nil {
return authCfgErr
}

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

func getAuthConfigs(dockercfg, username, password_file string) (*docker.AuthConfigurations, error) {
func appendDockerCfgConfigs(dockercfg string, cfgs *docker.AuthConfigurations) error {
var imagePullAuths *docker.AuthConfigurations
reader, err := os.Open(dockercfg)
if err != nil {
return fmt.Errorf("Unable to open docker config file: %v\n", err)
}
if imagePullAuths, err = docker.NewAuthConfigurations(reader); err != nil {
return fmt.Errorf("Unable to parse docker config file: %v\n", err)
}
if len(imagePullAuths.Configs) == 0 {
return fmt.Errorf("No auths were found in the given dockercfg file\n")
}
for name, ac := range imagePullAuths.Configs {
cfgs.Configs[fmt.Sprintf("%s/%s", dockercfg, name)] = ac
}
reader.Close()
return nil
}

func (i *defaultImageInspector) getAuthConfigs() (*docker.AuthConfigurations, error) {
imagePullAuths := &docker.AuthConfigurations{
map[string]docker.AuthConfiguration{"": {}}}
if dockercfg != "" {
reader, err := os.Open(dockercfg)
if err != nil {
return nil, fmt.Errorf("Unable to open docker config file: %v\n", err)
}
if imagePullAuths, err = docker.NewAuthConfigurations(reader); err != nil {
return nil, fmt.Errorf("Unable to parse docker config file: %v\n", err)
}
if len(imagePullAuths.Configs) == 0 {
return nil, fmt.Errorf("No auths were found in the given dockercfg file\n")
map[string]docker.AuthConfiguration{}}
if len(i.opts.DockerCfg.Values) > 0 {
for _, dcfgFile := range i.opts.DockerCfg.Values {
if err := appendDockerCfgConfigs(dcfgFile, imagePullAuths); err != nil {
return nil, err
}
}
}
if username != "" {
token, err := ioutil.ReadFile(password_file)

if i.opts.Username != "" {
token, err := ioutil.ReadFile(i.opts.PasswordFile)
if err != nil {
return nil, fmt.Errorf("Unable to read password file: %v\n", err)
}
imagePullAuths = &docker.AuthConfigurations{
map[string]docker.AuthConfiguration{"": {Username: username, Password: string(token)}}}
map[string]docker.AuthConfiguration{"": {Username: i.opts.Username, Password: string(token)}}}
}

return imagePullAuths, nil
Expand Down
55 changes: 55 additions & 0 deletions pkg/inspector/image-inspector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package inspector
import (
"fmt"
docker "github.com/fsouza/go-dockerclient"
iicmd "github.com/openshift/image-inspector/pkg/cmd"
"github.com/openshift/image-inspector/pkg/openscap"
"io/ioutil"
"os"
Expand Down Expand Up @@ -61,7 +62,61 @@ func TestScanImage(t *testing.T) {
}
}
}
}
}

func TestGetAuthConfigs(t *testing.T) {
goodTwoDockerCfg := iicmd.NewDefaultImageInspectorOptions()
goodTwoDockerCfg.DockerCfg.Values = []string{"test/dockercfg1", "test/dockercfg2"}

goodUserAndPass := iicmd.NewDefaultImageInspectorOptions()
goodUserAndPass.Username = "erez"
goodUserAndPass.PasswordFile = "test/passwordFile1"

badDockerCfgMissing := iicmd.NewDefaultImageInspectorOptions()
badDockerCfgMissing.DockerCfg.Values = []string{"test/dockercfg1", "test/nosuchfile"}

badDockerCfgWrong := iicmd.NewDefaultImageInspectorOptions()
badDockerCfgWrong.DockerCfg.Values = []string{"test/dockercfg1", "test/passwordFile1"}

badDockerCfgNoAuth := iicmd.NewDefaultImageInspectorOptions()
badDockerCfgNoAuth.DockerCfg.Values = []string{"test/dockercfg1", "test/dockercfg3"}

tests := map[string]struct {
opts *iicmd.ImageInspectorOptions
shouldFail bool
}{
"two dockercfg": {opts: goodTwoDockerCfg, shouldFail: false},
"username and passwordFile": {opts: goodUserAndPass, shouldFail: false},
"two dockercfg, one missing": {opts: badDockerCfgMissing, shouldFail: true},
"two dockercfg, one wrong": {opts: badDockerCfgWrong, shouldFail: true},
"two dockercfg, no auth": {opts: badDockerCfgNoAuth, shouldFail: true},
}

for k, v := range tests {
ii := &defaultImageInspector{*v.opts, InspectorMetadata{}}
auths, err := ii.getAuthConfigs()
if !v.shouldFail {
var expectedLength int = len(v.opts.DockerCfg.Values)
if len(v.opts.Username) > 0 {
expectedLength = expectedLength + 1
}
if err != nil {
t.Errorf("%s expected to validate but received %v", k, err)
}
var authsLen int = 0
if auths != nil {
authsLen = len(auths.Configs)
}
if auths == nil || expectedLength != authsLen {
t.Errorf("%s expected len to be %d but got %d from %v",
k, expectedLength, authsLen, auths)
}
} else {
if err == nil {
t.Errorf("%s should have failed be it didn't", k)
}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/inspector/test/dockercfg1
Original file line number Diff line number Diff line change
@@ -0,0 +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":"[email protected]","auth":"c2VydmljZWFjY291bnQ6ZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUprWldaaGRXeDBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbVJsY0d4dmVXVnlMWFJ2YTJWdUxYWXdZMmt3SWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXpaWEoyYVdObExXRmpZMjkxYm5RdWJtRnRaU0k2SW1SbGNHeHZlV1Z5SWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXpaWEoyYVdObExXRmpZMjkxYm5RdWRXbGtJam9pTnpKak1ESmpNRFF0WkRZNVl5MHhNV1UxTFdGa1pUTXRNREF4WVRSaE1qTXhNekJsSWl3aWMzVmlJam9pYzNsemRHVnRPbk5sY25acFkyVmhZMk52ZFc1ME9tUmxabUYxYkhRNlpHVndiRzk1WlhJaWZRLlVHZldNemhhNFJHc3VhOW5KUWROYWpiUXNFWWNFdW9uQ1JIR0J5WjdJYmpzbjFva2stS0dMbEVETmVXSHZZX1NWTVZPSWZlTXBzRmEzLTRkX01nM0hQRkMwN1QwS2lpcXc1bzVoRU40RkVNOGFiZWdHX1VBRHhCRkJTU2U5c0dxOTN4ejQydDRJYjJGamNwTEMxa3BEcjI4U1dIZ3ltYXJ0MzdNb3U2eExfYjdIY1V6a2ZRb29ZME5XY1VNXzE5VmxjOWtXek8waW5qSmNtLUduX3ZHTVNkOHNSWDFmQk9XenV6MHUySy1mTFBMdFd6b0U2NUUwOFZvV09FeGNrSExPQXlZUTBTRFprcVFFcEhaMlQ1ZS01N0M1MHFyZW5yQlo3bWg5cmV6RHFfbmNheUFlcmphSjlVSi1CY3FYN3pXeFcydkJLRHo4ekM3dmNfdndaVXZNdw=="}}
1 change: 1 addition & 0 deletions pkg/inspector/test/dockercfg2
Original file line number Diff line number Diff line change
@@ -0,0 +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":"[email protected]","auth":"c2VydmljZWFjY291bnQ6ZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUp0WVc1aFoyVnRaVzUwTFdsdVpuSmhJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbWx1YzNCbFkzUnZjaTFoWkcxcGJpMTBiMnRsYmkxc2VucGhaeUlzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG01aGJXVWlPaUpwYm5Od1pXTjBiM0l0WVdSdGFXNGlMQ0pyZFdKbGNtNWxkR1Z6TG1sdkwzTmxjblpwWTJWaFkyTnZkVzUwTDNObGNuWnBZMlV0WVdOamIzVnVkQzUxYVdRaU9pSm1OelJtT0RCalpTMW1ZbVF6TFRFeFpUVXRPR0ptWkMwd01ERmhOR0V5TXpFek1HVWlMQ0p6ZFdJaU9pSnplWE4wWlcwNmMyVnlkbWxqWldGalkyOTFiblE2YldGdVlXZGxiV1Z1ZEMxcGJtWnlZVHBwYm5Od1pXTjBiM0l0WVdSdGFXNGlmUS5LamNpSVE2QUNsdkZzbWJZNjZJdF95N1VMR0VTSldjYnUwVjdTelJqcktjVU5UTkwweUNmXzBvcl9NUDU1Yk1jS0FfeS1JZmhwQ0g4TU1jQVdSTExWZnJya21Ib3RNSnloeGJyQzAwVWQ3LXp2UlJ6ZDllLUZ3RUV2Z1hSZ2JZQklqc2ZqNGFKZDJHX0NnQTlmV1NKV1RTSXZBUG94LW1SdTFJdnh3MlZoZDVXb1JDTDBBc2FrUmFDb1h6SGt3NjRRSmRERm0yalljakl2RlZUWFpYV0FEUGR6bDhlUF9yZDljWjhpZDJQUWdXN0xwR1lJdEpxNTEtSC1RaGZjbGNLaGhiS1l4a0VoZlBqd2xUMkZtS2pfX2w4Zkg4dURqSmxrRFNYc2JXVHRmTktrSEtYUXpSTTZuVFBaTlVlcGpPVjVIUzhKOVpFX2ctTTFiT3JCWVJqb0E="}}
1 change: 1 addition & 0 deletions pkg/inspector/test/dockercfg3
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions pkg/inspector/test/passwordFile1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
some_password

0 comments on commit 2a1c4ea

Please sign in to comment.