github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/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.Header() 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 } 90 91 func TestDockerAuthConfigFormat(t *testing.T) { 92 tests := []struct { 93 contents string 94 expected map[string]BasicCredentials 95 fail bool 96 }{ 97 {"bogus contents", nil, true}, 98 {`{"bogus": {"foo": "bar"}}`, nil, true}, 99 {`{"rktKind": "foo"}`, nil, true}, 100 {`{"rktKind": "dockerAuth", "rktVersion": "foo"}`, nil, true}, 101 {`{"rktKind": "dockerAuth", "rktVersion": "v1"}`, nil, true}, 102 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": "foo"}`, nil, true}, 103 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": []}`, nil, true}, 104 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"]}`, nil, true}, 105 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {}}`, nil, true}, 106 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {"user": ""}}`, nil, true}, 107 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {"user": "bar"}}`, nil, true}, 108 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {"user": "bar", "password": ""}}`, nil, true}, 109 {`{"rktKind": "dockerAuth", "rktVersion": "v1", "registries": ["coreos.com"], "credentials": {"user": "bar", "password": "baz"}}`, map[string]BasicCredentials{"coreos.com": BasicCredentials{User: "bar", Password: "baz"}}, false}, 110 } 111 for _, tt := range tests { 112 cfg, err := getConfigFromContents(tt.contents, "dockerAuth") 113 if vErr := verifyFailure(tt.fail, tt.contents, err); vErr != nil { 114 t.Errorf("%v", vErr) 115 } else if !tt.fail { 116 result := cfg.DockerCredentialsPerRegistry 117 if !reflect.DeepEqual(result, tt.expected) { 118 t.Error("Got unexpected results\nResult:\n", result, "\n\nExpected:\n", tt.expected) 119 } 120 } 121 } 122 } 123 124 func TestPathsConfigFormat(t *testing.T) { 125 tests := []struct { 126 contents string 127 expected ConfigurablePaths 128 fail bool 129 }{ 130 {"bogus contents", ConfigurablePaths{}, true}, 131 {`{"bogus": {"foo": "bar"}}`, ConfigurablePaths{}, true}, 132 {`{"rktKind": "foo"}`, ConfigurablePaths{}, true}, 133 {`{"rktKind": "paths", "rktVersion": "foo"}`, ConfigurablePaths{}, true}, 134 {`{"rktKind": "paths", "rktVersion": "v1"}`, ConfigurablePaths{}, false}, 135 {`{"rktKind": "paths", "rktVersion": "v1", "data": "/dir1"}`, ConfigurablePaths{DataDir: "/dir1"}, false}, 136 {`{"rktKind": "paths", "rktVersion": "v1", "data": "/dir1", "stage1-images": "/dir2"}`, ConfigurablePaths{DataDir: "/dir1", Stage1ImagesDir: "/dir2"}, false}, 137 } 138 for _, tt := range tests { 139 cfg, err := getConfigFromContents(tt.contents, "paths") 140 if vErr := verifyFailure(tt.fail, tt.contents, err); vErr != nil { 141 t.Errorf("%v", vErr) 142 } else if !tt.fail { 143 result := cfg.Paths 144 if !reflect.DeepEqual(result, tt.expected) { 145 t.Errorf("Got unexpected results\nResult:\n%#v\n\nExpected:\n%#v", result, tt.expected) 146 } 147 } 148 } 149 } 150 151 func TestStage1ConfigFormat(t *testing.T) { 152 tests := []struct { 153 contents string 154 expected Stage1Data 155 fail bool 156 }{ 157 {"bogus contents", Stage1Data{}, true}, 158 {`{"bogus": {"foo": "bar"}}`, Stage1Data{}, true}, 159 {`{"rktKind": "foo"}`, Stage1Data{}, true}, 160 {`{"rktKind": "stage1", "rktVersion": "foo"}`, Stage1Data{}, true}, 161 {`{"rktKind": "stage1", "rktVersion": "v1"}`, Stage1Data{}, false}, 162 {`{"rktKind": "stage1", "rktVersion": "v1", "name": "example.com/stage1"}`, Stage1Data{}, true}, 163 {`{"rktKind": "stage1", "rktVersion": "v1", "version": "1.2.3"}`, Stage1Data{}, true}, 164 {`{"rktKind": "stage1", "rktVersion": "v1", "name": "example.com/stage1", "version": "1.2.3"}`, Stage1Data{Name: "example.com/stage1", Version: "1.2.3"}, false}, 165 {`{"rktKind": "stage1", "rktVersion": "v1", "location": "/image.aci"}`, Stage1Data{Location: "/image.aci"}, false}, 166 {`{"rktKind": "stage1", "rktVersion": "v1", "name": "example.com/stage1", "location": "/image.aci"}`, Stage1Data{}, true}, 167 {`{"rktKind": "stage1", "rktVersion": "v1", "version": "1.2.3", "location": "/image.aci"}`, Stage1Data{}, true}, 168 {`{"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}, 169 } 170 for _, tt := range tests { 171 cfg, err := getConfigFromContents(tt.contents, "stage1") 172 if vErr := verifyFailure(tt.fail, tt.contents, err); vErr != nil { 173 t.Errorf("%v", vErr) 174 } else if !tt.fail { 175 result := cfg.Stage1 176 if !reflect.DeepEqual(result, tt.expected) { 177 t.Errorf("Got unexpected results\nResult:\n%#v\n\nExpected:\n%#v", result, tt.expected) 178 } 179 } 180 } 181 } 182 183 func verifyFailure(shouldFail bool, contents string, err error) error { 184 var vErr error = nil 185 if err != nil { 186 if !shouldFail { 187 vErr = fmt.Errorf("Expected test to succeed, failed unexpectedly (contents: `%s`): %v", contents, err) 188 } 189 } else if shouldFail { 190 vErr = fmt.Errorf("Expected test to fail, succeeded unexpectedly (contents: `%s`)", contents) 191 } 192 return vErr 193 } 194 195 func getConfigFromContents(contents, kind string) (*Config, error) { 196 f, err := tmpConfigFile(tstprefix) 197 if err != nil { 198 panic(fmt.Sprintf("Failed to create tmp config file: %v", err)) 199 } 200 // First remove the file, then close it (last deferred item is 201 // executed first). 202 defer f.Close() 203 defer os.Remove(f.Name()) 204 if _, err := f.Write([]byte(contents)); err != nil { 205 panic(fmt.Sprintf("Writing config to file failed: %v", err)) 206 } 207 fi, err := f.Stat() 208 if err != nil { 209 panic(fmt.Sprintf("Stating a tmp config file failed: %v", err)) 210 } 211 cfg := newConfig() 212 return cfg, readFile(cfg, fi, f.Name(), []string{kind}) 213 } 214 215 func TestConfigLoading(t *testing.T) { 216 dir, err := ioutil.TempDir("", tstprefix) 217 if err != nil { 218 panic(fmt.Sprintf("Failed to create temporary directory: %v", err)) 219 } 220 defer os.RemoveAll(dir) 221 systemAuth := filepath.Join("system", "auth.d") 222 systemIgnored := filepath.Join(systemAuth, "ignoreddir") 223 localAuth := filepath.Join("local", "auth.d") 224 localIgnored := filepath.Join(localAuth, "ignoreddir") 225 dirs := []string{ 226 "system", 227 systemAuth, 228 systemIgnored, 229 "local", 230 localAuth, 231 localIgnored, 232 } 233 for _, d := range dirs { 234 cd := filepath.Join(dir, d) 235 if err := os.Mkdir(cd, 0700); err != nil { 236 panic(fmt.Sprintf("Failed to create configuration directory %q: %v", cd, err)) 237 } 238 } 239 files := []struct { 240 path string 241 domain string 242 user string 243 pass string 244 }{ 245 {filepath.Join(dir, systemAuth, "endocode.json"), "endocode.com", "system_user1", "system_password1"}, 246 {filepath.Join(dir, systemAuth, "coreos.json"), "coreos.com", "system_user2", "system_password2"}, 247 {filepath.Join(dir, systemAuth, "ignoredfile"), "example1.com", "ignored_user1", "ignored_password1"}, 248 {filepath.Join(dir, systemIgnored, "ignoredfile"), "example2.com", "ignored_user2", "ignored_password2"}, 249 {filepath.Join(dir, systemIgnored, "ignoredanyway.json"), "example3.com", "ignored_user3", "ignored_password3"}, 250 {filepath.Join(dir, localAuth, "endocode.json"), "endocode.com", "local_user1", "local_password1"}, 251 {filepath.Join(dir, localAuth, "tectonic.json"), "tectonic.com", "local_user2", "local_password2"}, 252 {filepath.Join(dir, localAuth, "ignoredfile"), "example4.com", "ignored_user4", "ignored_password4"}, 253 {filepath.Join(dir, localIgnored, "ignoredfile"), "example5.com", "ignored_user5", "ignored_password5"}, 254 {filepath.Join(dir, localIgnored, "ignoredanyway.json"), "example6.com", "ignored_user6", "ignored_password6"}, 255 } 256 for _, f := range files { 257 if err := writeBasicConfig(f.path, f.domain, f.user, f.pass); err != nil { 258 panic(fmt.Sprintf("Failed to write configuration file: %v", err)) 259 } 260 } 261 cfg, err := GetConfigFrom(filepath.Join(dir, "system"), filepath.Join(dir, "local")) 262 if err != nil { 263 panic(fmt.Sprintf("Failed to get configuration: %v", err)) 264 } 265 result := make(map[string]http.Header) 266 for d, h := range cfg.AuthPerHost { 267 result[d] = h.Header() 268 } 269 expected := map[string]http.Header{ 270 "endocode.com": http.Header{ 271 // local_user1:local_password1 272 authHeader: []string{"Basic bG9jYWxfdXNlcjE6bG9jYWxfcGFzc3dvcmQx"}, 273 }, 274 "coreos.com": http.Header{ 275 // system_user2:system_password2 276 authHeader: []string{"Basic c3lzdGVtX3VzZXIyOnN5c3RlbV9wYXNzd29yZDI="}, 277 }, 278 "tectonic.com": http.Header{ 279 // local_user2:local_password2 280 authHeader: []string{"Basic bG9jYWxfdXNlcjI6bG9jYWxfcGFzc3dvcmQy"}, 281 }, 282 } 283 if !reflect.DeepEqual(result, expected) { 284 t.Error("Got unexpected results\nResult:\n", result, "\n\nExpected:\n", expected) 285 } 286 } 287 288 func writeBasicConfig(path, domain, user, pass string) error { 289 type basicv1creds struct { 290 User string `json:"user"` 291 Password string `json:"password"` 292 } 293 type basicv1 struct { 294 RktVersion string `json:"rktVersion"` 295 RktKind string `json:"rktKind"` 296 Domains []string `json:"domains"` 297 Type string `json:"type"` 298 Credentials basicv1creds `json:"credentials"` 299 } 300 config := &basicv1{ 301 RktVersion: "v1", 302 RktKind: "auth", 303 Domains: []string{domain}, 304 Type: "basic", 305 Credentials: basicv1creds{ 306 User: user, 307 Password: pass, 308 }, 309 } 310 raw, err := json.Marshal(config) 311 if err != nil { 312 return err 313 } 314 return ioutil.WriteFile(path, raw, 0600) 315 }