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  }