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  }