github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/config/config_test.go (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package config 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "os" 23 "path/filepath" 24 "reflect" 25 "testing" 26 ) 27 28 const tstprefix = "config-test" 29 30 // tmpConfigFile is based on ioutil.Tempfile. The differences are that 31 // this function is simpler (no reseeding and whatnot) and, most 32 // importantly, it returns a file with ".json" extension. 33 func tmpConfigFile(prefix string) (*os.File, error) { 34 dir := os.TempDir() 35 idx := 0 36 tries := 10000 37 for i := 0; i < tries; i++ { 38 name := filepath.Join(dir, fmt.Sprintf("%s%d.json", prefix, idx)) 39 f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) 40 if os.IsExist(err) { 41 idx++ 42 continue 43 } 44 return f, err 45 } 46 return nil, fmt.Errorf("Failed to get tmpfile after %d tries", tries) 47 } 48 49 func TestAuthConfigFormat(t *testing.T) { 50 tests := []struct { 51 contents string 52 expected map[string]http.Header 53 fail bool 54 }{ 55 {"bogus contents", nil, true}, 56 {`{"bogus": {"foo": "bar"}}`, nil, true}, 57 {`{"rktKind": "foo"}`, nil, true}, 58 {`{"rktKind": "auth", "rktVersion": "foo"}`, nil, true}, 59 {`{"rktKind": "auth", "rktVersion": "v1"}`, nil, true}, 60 {`{"rktKind": "auth", "rktVersion": "v1", "domains": "foo"}`, nil, true}, 61 {`{"rktKind": "auth", "rktVersion": "v1", "domains": []}`, nil, true}, 62 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"]}`, nil, true}, 63 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "foo"}`, nil, true}, 64 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "basic"}`, nil, true}, 65 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "basic", "credentials": {}}`, nil, true}, 66 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "basic", "credentials": {"user": ""}}`, nil, true}, 67 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "basic", "credentials": {"user": "bar"}}`, nil, true}, 68 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "basic", "credentials": {"user": "bar", "password": ""}}`, nil, true}, 69 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "basic", "credentials": {"user": "bar", "password": "baz"}}`, map[string]http.Header{"coreos.com": {"Authorization": []string{"Basic YmFyOmJheg=="}}}, false}, 70 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "oauth"}`, nil, true}, 71 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "oauth", "credentials": {}}`, nil, true}, 72 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "oauth", "credentials": {"token": ""}}`, nil, true}, 73 {`{"rktKind": "auth", "rktVersion": "v1", "domains": ["coreos.com"], "type": "oauth", "credentials": {"token": "sometoken"}}`, map[string]http.Header{"coreos.com": {"Authorization": []string{"Bearer sometoken"}}}, false}, 74 } 75 for _, tt := range tests { 76 cfg, err := getConfigFromContents(tt.contents, "auth") 77 if vErr := verifyFailure(tt.fail, tt.contents, err); vErr != nil { 78 t.Errorf("%v", vErr) 79 } else if !tt.fail { 80 result := make(map[string]http.Header) 81 for k, v := range cfg.AuthPerHost { 82 result[k] = v.GetHeader() 83 } 84 if !reflect.DeepEqual(result, tt.expected) { 85 t.Error("Got unexpected results\nResult:\n", result, "\n\nExpected:\n", tt.expected) 86 } 87 } 88 89 if _, err := json.Marshal(cfg); err != nil { 90 t.Errorf("error marshaling config %v", err) 91 } 92 } 93 } 94 95 func TestDockerAuthConfigFormat(t *testing.T) { 96 tests := []struct { 97 contents string 98 expected map[string]BasicCredentials 99 fail bool 100 }{ 101 {"bogus contents", nil, true}, 102 {`{"bogus": {"foo": "bar"}}`, nil, true}, 103 {`{"rktKind": "foo"}`, nil, true}, 104 {`{"rktKind": "dockerAuth", "rktVersion": "foo"}`, nil, true}, 105 {`{"rktKind": "dockerAuth", "rktVersion": "v1"}`, nil, true}, 106 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": "foo"}`, nil, true}, 107 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": []}`, nil, true}, 108 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"]}`, nil, true}, 109 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {}}`, nil, true}, 110 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {"user": ""}}`, nil, true}, 111 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {"user": "bar"}}`, nil, true}, 112 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {"user": "bar", "password": ""}}`, nil, true}, 113 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {"user": "bar", "password": "baz"}}`, map[string]BasicCredentials{"coreos.com": {User: "bar", Password: "baz"}}, false}, 114 } 115 for _, tt := range tests { 116 cfg, err := getConfigFromContents(tt.contents, "dockerAuth") 117 if vErr := verifyFailure(tt.fail, tt.contents, err); vErr != nil { 118 t.Errorf("%v", vErr) 119 } else if !tt.fail { 120 result := cfg.DockerCredentialsPerRegistry 121 if !reflect.DeepEqual(result, tt.expected) { 122 t.Error("Got unexpected results\nResult:\n", result, "\n\nExpected:\n", tt.expected) 123 } 124 } 125 126 if _, err := json.Marshal(cfg); err != nil { 127 t.Errorf("error marshaling config %v", err) 128 } 129 } 130 } 131 132 func TestPathsConfigFormat(t *testing.T) { 133 tests := []struct { 134 contents string 135 expected ConfigurablePaths 136 fail bool 137 }{ 138 {"bogus contents", ConfigurablePaths{}, true}, 139 {`{"bogus": {"foo": "bar"}}`, ConfigurablePaths{}, true}, 140 {`{"rktKind": "foo"}`, ConfigurablePaths{}, true}, 141 {`{"rktKind": "paths", "rktVersion": "foo"}`, ConfigurablePaths{}, true}, 142 {`{"rktKind": "paths", "rktVersion": "v1"}`, ConfigurablePaths{}, false}, 143 {`{"rktKind": "paths", "rktVersion": "v1", "data": "/dir1"}`, ConfigurablePaths{DataDir: "/dir1"}, false}, 144 {`{"rktKind": "paths", "rktVersion": "v1", "data": "/dir1", "stage1-images": "/dir2"}`, ConfigurablePaths{DataDir: "/dir1", Stage1ImagesDir: "/dir2"}, false}, 145 } 146 for _, tt := range tests { 147 cfg, err := getConfigFromContents(tt.contents, "paths") 148 if vErr := verifyFailure(tt.fail, tt.contents, err); vErr != nil { 149 t.Errorf("%v", vErr) 150 } else if !tt.fail { 151 result := cfg.Paths 152 if !reflect.DeepEqual(result, tt.expected) { 153 t.Errorf("Got unexpected results\nResult:\n%#v\n\nExpected:\n%#v", result, tt.expected) 154 } 155 } 156 } 157 } 158 159 func TestStage1ConfigFormat(t *testing.T) { 160 tests := []struct { 161 contents string 162 expected Stage1Data 163 fail bool 164 }{ 165 {"bogus contents", Stage1Data{}, true}, 166 {`{"bogus": {"foo": "bar"}}`, Stage1Data{}, true}, 167 {`{"rktKind": "foo"}`, Stage1Data{}, true}, 168 {`{"rktKind": "stage1", "rktVersion": "foo"}`, Stage1Data{}, true}, 169 {`{"rktKind": "stage1", "rktVersion": "v1"}`, Stage1Data{}, false}, 170 {`{"rktKind": "stage1", "rktVersion": "v1", "name": "example.com/stage1"}`, Stage1Data{}, true}, 171 {`{"rktKind": "stage1", "rktVersion": "v1", "version": "1.2.3"}`, Stage1Data{}, true}, 172 {`{"rktKind": "stage1", "rktVersion": "v1", "name": "example.com/stage1", "version": "1.2.3"}`, Stage1Data{Name: "example.com/stage1", Version: "1.2.3"}, false}, 173 {`{"rktKind": "stage1", "rktVersion": "v1", "location": "/image.aci"}`, Stage1Data{Location: "/image.aci"}, false}, 174 {`{"rktKind": "stage1", "rktVersion": "v1", "name": "example.com/stage1", "location": "/image.aci"}`, Stage1Data{}, true}, 175 {`{"rktKind": "stage1", "rktVersion": "v1", "version": "1.2.3", "location": "/image.aci"}`, Stage1Data{}, true}, 176 {`{"rktKind": "stage1", "rktVersion": "v1", "name": "example.com/stage1", "version": "1.2.3", "location": "/image.aci"}`, Stage1Data{Name: "example.com/stage1", Version: "1.2.3", Location: "/image.aci"}, false}, 177 } 178 for _, tt := range tests { 179 cfg, err := getConfigFromContents(tt.contents, "stage1") 180 if vErr := verifyFailure(tt.fail, tt.contents, err); vErr != nil { 181 t.Errorf("%v", vErr) 182 } else if !tt.fail { 183 result := cfg.Stage1 184 if !reflect.DeepEqual(result, tt.expected) { 185 t.Errorf("Got unexpected results\nResult:\n%#v\n\nExpected:\n%#v", result, tt.expected) 186 } 187 } 188 } 189 } 190 191 func verifyFailure(shouldFail bool, contents string, err error) error { 192 var vErr error = nil 193 if err != nil { 194 if !shouldFail { 195 vErr = fmt.Errorf("Expected test to succeed, failed unexpectedly (contents: `%s`): %v", contents, err) 196 } 197 } else if shouldFail { 198 vErr = fmt.Errorf("Expected test to fail, succeeded unexpectedly (contents: `%s`)", contents) 199 } 200 return vErr 201 } 202 203 func getConfigFromContents(contents, kind string) (*Config, error) { 204 f, err := tmpConfigFile(tstprefix) 205 if err != nil { 206 panic(fmt.Sprintf("Failed to create tmp config file: %v", err)) 207 } 208 // First remove the file, then close it (last deferred item is 209 // executed first). 210 defer f.Close() 211 defer os.Remove(f.Name()) 212 if _, err := f.Write([]byte(contents)); err != nil { 213 panic(fmt.Sprintf("Writing config to file failed: %v", err)) 214 } 215 fi, err := f.Stat() 216 if err != nil { 217 panic(fmt.Sprintf("Stating a tmp config file failed: %v", err)) 218 } 219 cfg := newConfig() 220 return cfg, readFile(cfg, fi, f.Name(), []string{kind}) 221 } 222 223 func TestConfigLoading(t *testing.T) { 224 dir, err := ioutil.TempDir("", tstprefix) 225 if err != nil { 226 panic(fmt.Sprintf("Failed to create temporary directory: %v", err)) 227 } 228 defer os.RemoveAll(dir) 229 systemAuth := filepath.Join("system", "auth.d") 230 systemIgnored := filepath.Join(systemAuth, "ignoreddir") 231 localAuth := filepath.Join("local", "auth.d") 232 localIgnored := filepath.Join(localAuth, "ignoreddir") 233 dirs := []string{ 234 "system", 235 systemAuth, 236 systemIgnored, 237 "local", 238 localAuth, 239 localIgnored, 240 } 241 for _, d := range dirs { 242 cd := filepath.Join(dir, d) 243 if err := os.Mkdir(cd, 0700); err != nil { 244 panic(fmt.Sprintf("Failed to create configuration directory %q: %v", cd, err)) 245 } 246 } 247 files := []struct { 248 path string 249 domain string 250 user string 251 pass string 252 }{ 253 {filepath.Join(dir, systemAuth, "endocode.json"), "endocode.com", "system_user1", "system_password1"}, 254 {filepath.Join(dir, systemAuth, "coreos.json"), "coreos.com", "system_user2", "system_password2"}, 255 {filepath.Join(dir, systemAuth, "ignoredfile"), "example1.com", "ignored_user1", "ignored_password1"}, 256 {filepath.Join(dir, systemIgnored, "ignoredfile"), "example2.com", "ignored_user2", "ignored_password2"}, 257 {filepath.Join(dir, systemIgnored, "ignoredanyway.json"), "example3.com", "ignored_user3", "ignored_password3"}, 258 {filepath.Join(dir, localAuth, "endocode.json"), "endocode.com", "local_user1", "local_password1"}, 259 {filepath.Join(dir, localAuth, "tectonic.json"), "tectonic.com", "local_user2", "local_password2"}, 260 {filepath.Join(dir, localAuth, "ignoredfile"), "example4.com", "ignored_user4", "ignored_password4"}, 261 {filepath.Join(dir, localIgnored, "ignoredfile"), "example5.com", "ignored_user5", "ignored_password5"}, 262 {filepath.Join(dir, localIgnored, "ignoredanyway.json"), "example6.com", "ignored_user6", "ignored_password6"}, 263 } 264 for _, f := range files { 265 if err := writeBasicConfig(f.path, f.domain, f.user, f.pass); err != nil { 266 panic(fmt.Sprintf("Failed to write configuration file: %v", err)) 267 } 268 } 269 cfg, err := GetConfigFrom(filepath.Join(dir, "system"), filepath.Join(dir, "local")) 270 if err != nil { 271 panic(fmt.Sprintf("Failed to get configuration: %v", err)) 272 } 273 result := make(map[string]http.Header) 274 for d, h := range cfg.AuthPerHost { 275 result[d] = h.GetHeader() 276 } 277 expected := map[string]http.Header{ 278 "endocode.com": { 279 // local_user1:local_password1 280 authHeader: []string{"Basic bG9jYWxfdXNlcjE6bG9jYWxfcGFzc3dvcmQx"}, 281 }, 282 "coreos.com": { 283 // system_user2:system_password2 284 authHeader: []string{"Basic c3lzdGVtX3VzZXIyOnN5c3RlbV9wYXNzd29yZDI="}, 285 }, 286 "tectonic.com": { 287 // local_user2:local_password2 288 authHeader: []string{"Basic bG9jYWxfdXNlcjI6bG9jYWxfcGFzc3dvcmQy"}, 289 }, 290 } 291 if !reflect.DeepEqual(result, expected) { 292 t.Error("Got unexpected results\nResult:\n", result, "\n\nExpected:\n", expected) 293 } 294 } 295 296 func writeBasicConfig(path, domain, user, pass string) error { 297 type basicv1creds struct { 298 User string `json:"user"` 299 Password string `json:"password"` 300 } 301 type basicv1 struct { 302 RktVersion string `json:"rktVersion"` 303 RktKind string `json:"rktKind"` 304 Domains []string `json:"domains"` 305 Type string `json:"type"` 306 Credentials basicv1creds `json:"credentials"` 307 } 308 config := &basicv1{ 309 RktVersion: "v1", 310 RktKind: "auth", 311 Domains: []string{domain}, 312 Type: "basic", 313 Credentials: basicv1creds{ 314 User: user, 315 Password: pass, 316 }, 317 } 318 raw, err := json.Marshal(config) 319 if err != nil { 320 return err 321 } 322 return ioutil.WriteFile(path, raw, 0600) 323 }