Merge pull request #212 from crawford/metadata
refactor: Refactor metadata and datasources to be more testable
This commit is contained in:
		| @@ -132,6 +132,17 @@ func main() { | ||||
| 		fmt.Printf("Failed to parse meta-data: %v\n", err) | ||||
| 		die() | ||||
| 	} | ||||
|  | ||||
| 	if ccm != nil { | ||||
| 		fmt.Printf("Fetching network config from datasource of type %q\n", ds.Type()) | ||||
| 		netconfBytes, err := ds.FetchNetworkConfig(ccm.NetworkConfigPath) | ||||
| 		if err != nil { | ||||
| 			fmt.Printf("Failed fetching network config from datasource: %v\n", err) | ||||
| 			die() | ||||
| 		} | ||||
| 		ccm.NetworkConfig = string(netconfBytes) | ||||
| 	} | ||||
|  | ||||
| 	if ud, err := initialize.ParseUserData(userdata); err != nil { | ||||
| 		fmt.Printf("Failed to parse user-data: %v\n", err) | ||||
| 		die() | ||||
|   | ||||
| @@ -41,6 +41,10 @@ func (cd *configDrive) FetchUserdata() ([]byte, error) { | ||||
| 	return cd.tryReadFile(path.Join(cd.openstackVersionRoot(), "user_data")) | ||||
| } | ||||
|  | ||||
| func (cd *configDrive) FetchNetworkConfig(filename string) ([]byte, error) { | ||||
| 	return cd.tryReadFile(path.Join(cd.openstackRoot(), filename)) | ||||
| } | ||||
|  | ||||
| func (cd *configDrive) Type() string { | ||||
| 	return "cloud-drive" | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,11 @@ | ||||
| package datasource | ||||
|  | ||||
| const ( | ||||
| 	Ec2ApiVersion       = "2009-04-04" | ||||
| 	OpenstackApiVersion = "2012-08-10" | ||||
| ) | ||||
|  | ||||
| type Datasource interface { | ||||
| 	IsAvailable() bool | ||||
| 	AvailabilityChanges() bool | ||||
| 	ConfigRoot() string | ||||
| 	FetchMetadata() ([]byte, error) | ||||
| 	FetchUserdata() ([]byte, error) | ||||
| 	FetchNetworkConfig(string) ([]byte, error) | ||||
| 	Type() string | ||||
| } | ||||
|   | ||||
| @@ -34,6 +34,10 @@ func (f *localFile) FetchUserdata() ([]byte, error) { | ||||
| 	return ioutil.ReadFile(f.path) | ||||
| } | ||||
|  | ||||
| func (f *localFile) FetchNetworkConfig(filename string) ([]byte, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func (f *localFile) Type() string { | ||||
| 	return "local-file" | ||||
| } | ||||
|   | ||||
| @@ -126,6 +126,10 @@ func (scs *serverContextService) FetchUserdata() ([]byte, error) { | ||||
| 	return []byte(userData), nil | ||||
| } | ||||
|  | ||||
| func (scs *serverContextService) FetchNetworkConfig(a string) ([]byte, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func isBase64Encoded(field string, userdata map[string]string) bool { | ||||
| 	base64Fields, ok := userdata["base64_fields"] | ||||
| 	if !ok { | ||||
|   | ||||
| @@ -7,44 +7,28 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/coreos/coreos-cloudinit/datasource/metadata" | ||||
| 	"github.com/coreos/coreos-cloudinit/pkg" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	DefaultAddress = "http://169.254.169.254/" | ||||
| 	apiVersion     = "2009-04-04" | ||||
| 	userdataUrl    = apiVersion + "/user-data" | ||||
| 	metadataUrl    = apiVersion + "/meta-data" | ||||
| 	userdataPath   = apiVersion + "/user-data" | ||||
| 	metadataPath   = apiVersion + "/meta-data" | ||||
| ) | ||||
|  | ||||
| type metadataService struct { | ||||
| 	root   string | ||||
| 	client pkg.Getter | ||||
| 	metadata.MetadataService | ||||
| } | ||||
|  | ||||
| func NewDatasource(root string) *metadataService { | ||||
| 	if !strings.HasSuffix(root, "/") { | ||||
| 		root += "/" | ||||
| 	} | ||||
| 	return &metadataService{root, pkg.NewHttpClient()} | ||||
| } | ||||
|  | ||||
| func (ms metadataService) IsAvailable() bool { | ||||
| 	_, err := ms.client.Get(ms.root + apiVersion) | ||||
| 	return (err == nil) | ||||
| } | ||||
|  | ||||
| func (ms metadataService) AvailabilityChanges() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (ms metadataService) ConfigRoot() string { | ||||
| 	return ms.root | ||||
| 	return &metadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath)} | ||||
| } | ||||
|  | ||||
| func (ms metadataService) FetchMetadata() ([]byte, error) { | ||||
| 	attrs := make(map[string]interface{}) | ||||
| 	if keynames, err := fetchAttributes(ms.client, fmt.Sprintf("%s/public-keys", ms.metadataUrl())); err == nil { | ||||
| 	if keynames, err := ms.fetchAttributes(fmt.Sprintf("%s/public-keys", ms.MetadataUrl())); err == nil { | ||||
| 		keyIDs := make(map[string]string) | ||||
| 		for _, keyname := range keynames { | ||||
| 			tokens := strings.SplitN(keyname, "=", 2) | ||||
| @@ -56,7 +40,7 @@ func (ms metadataService) FetchMetadata() ([]byte, error) { | ||||
|  | ||||
| 		keys := make(map[string]string) | ||||
| 		for name, id := range keyIDs { | ||||
| 			sshkey, err := fetchAttribute(ms.client, fmt.Sprintf("%s/public-keys/%s/openssh-key", ms.metadataUrl(), id)) | ||||
| 			sshkey, err := ms.fetchAttribute(fmt.Sprintf("%s/public-keys/%s/openssh-key", ms.MetadataUrl(), id)) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| @@ -68,25 +52,25 @@ func (ms metadataService) FetchMetadata() ([]byte, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if hostname, err := fetchAttribute(ms.client, fmt.Sprintf("%s/hostname", ms.metadataUrl())); err == nil { | ||||
| 	if hostname, err := ms.fetchAttribute(fmt.Sprintf("%s/hostname", ms.MetadataUrl())); err == nil { | ||||
| 		attrs["hostname"] = hostname | ||||
| 	} else if _, ok := err.(pkg.ErrNotFound); !ok { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if localAddr, err := fetchAttribute(ms.client, fmt.Sprintf("%s/local-ipv4", ms.metadataUrl())); err == nil { | ||||
| 	if localAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/local-ipv4", ms.MetadataUrl())); err == nil { | ||||
| 		attrs["local-ipv4"] = localAddr | ||||
| 	} else if _, ok := err.(pkg.ErrNotFound); !ok { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if publicAddr, err := fetchAttribute(ms.client, fmt.Sprintf("%s/public-ipv4", ms.metadataUrl())); err == nil { | ||||
| 	if publicAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/public-ipv4", ms.MetadataUrl())); err == nil { | ||||
| 		attrs["public-ipv4"] = publicAddr | ||||
| 	} else if _, ok := err.(pkg.ErrNotFound); !ok { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if content_path, err := fetchAttribute(ms.client, fmt.Sprintf("%s/network_config/content_path", ms.metadataUrl())); err == nil { | ||||
| 	if content_path, err := ms.fetchAttribute(fmt.Sprintf("%s/network_config/content_path", ms.MetadataUrl())); err == nil { | ||||
| 		attrs["network_config"] = map[string]string{ | ||||
| 			"content_path": content_path, | ||||
| 		} | ||||
| @@ -97,30 +81,12 @@ func (ms metadataService) FetchMetadata() ([]byte, error) { | ||||
| 	return json.Marshal(attrs) | ||||
| } | ||||
|  | ||||
| func (ms metadataService) FetchUserdata() ([]byte, error) { | ||||
| 	if data, err := ms.client.GetRetry(ms.userdataUrl()); err == nil { | ||||
| 		return data, err | ||||
| 	} else if _, ok := err.(pkg.ErrNotFound); ok { | ||||
| 		return []byte{}, nil | ||||
| 	} else { | ||||
| 		return data, err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (ms metadataService) Type() string { | ||||
| 	return "ec2-metadata-service" | ||||
| } | ||||
|  | ||||
| func (ms metadataService) metadataUrl() string { | ||||
| 	return (ms.root + metadataUrl) | ||||
| } | ||||
|  | ||||
| func (ms metadataService) userdataUrl() string { | ||||
| 	return (ms.root + userdataUrl) | ||||
| } | ||||
|  | ||||
| func fetchAttributes(client pkg.Getter, url string) ([]string, error) { | ||||
| 	resp, err := client.GetRetry(url) | ||||
| func (ms metadataService) fetchAttributes(url string) ([]string, error) { | ||||
| 	resp, err := ms.FetchData(url) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -132,8 +98,8 @@ func fetchAttributes(client pkg.Getter, url string) ([]string, error) { | ||||
| 	return data, scanner.Err() | ||||
| } | ||||
|  | ||||
| func fetchAttribute(client pkg.Getter, url string) (string, error) { | ||||
| 	if attrs, err := fetchAttributes(client, url); err == nil && len(attrs) > 0 { | ||||
| func (ms metadataService) fetchAttribute(url string) (string, error) { | ||||
| 	if attrs, err := ms.fetchAttributes(url); err == nil && len(attrs) > 0 { | ||||
| 		return attrs[0], nil | ||||
| 	} else { | ||||
| 		return "", err | ||||
|   | ||||
| @@ -6,36 +6,11 @@ import ( | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coreos/coreos-cloudinit/datasource/metadata" | ||||
| 	"github.com/coreos/coreos-cloudinit/datasource/metadata/test" | ||||
| 	"github.com/coreos/coreos-cloudinit/pkg" | ||||
| ) | ||||
|  | ||||
| type testHttpClient struct { | ||||
| 	resources map[string]string | ||||
| 	err       error | ||||
| } | ||||
|  | ||||
| func (t *testHttpClient) GetRetry(url string) ([]byte, error) { | ||||
| 	if t.err != nil { | ||||
| 		return nil, t.err | ||||
| 	} | ||||
| 	if val, ok := t.resources[url]; ok { | ||||
| 		return []byte(val), nil | ||||
| 	} else { | ||||
| 		return nil, pkg.ErrNotFound{fmt.Errorf("not found: %q", url)} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *testHttpClient) Get(url string) ([]byte, error) { | ||||
| 	return t.GetRetry(url) | ||||
| } | ||||
|  | ||||
| func TestAvailabilityChanges(t *testing.T) { | ||||
| 	want := true | ||||
| 	if ac := (metadataService{}).AvailabilityChanges(); ac != want { | ||||
| 		t.Fatalf("bad AvailabilityChanges: want %q, got %q", want, ac) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestType(t *testing.T) { | ||||
| 	want := "ec2-metadata-service" | ||||
| 	if kind := (metadataService{}).Type(); kind != want { | ||||
| @@ -43,102 +18,6 @@ func TestType(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestIsAvailable(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root      string | ||||
| 		resources map[string]string | ||||
| 		expect    bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root: "/", | ||||
| 			resources: map[string]string{ | ||||
| 				"/2009-04-04": "", | ||||
| 			}, | ||||
| 			expect: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:      "/", | ||||
| 			resources: map[string]string{}, | ||||
| 			expect:    false, | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := &metadataService{tt.root, &testHttpClient{tt.resources, nil}} | ||||
| 		if a := service.IsAvailable(); a != tt.expect { | ||||
| 			t.Fatalf("bad isAvailable (%q): want %q, got %q", tt.resources, tt.expect, a) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestFetchUserdata(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root      string | ||||
| 		resources map[string]string | ||||
| 		userdata  []byte | ||||
| 		clientErr error | ||||
| 		expectErr error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root: "/", | ||||
| 			resources: map[string]string{ | ||||
| 				"/2009-04-04/user-data": "hello", | ||||
| 			}, | ||||
| 			userdata: []byte("hello"), | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:      "/", | ||||
| 			clientErr: pkg.ErrNotFound{fmt.Errorf("test not found error")}, | ||||
| 			userdata:  []byte{}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:      "/", | ||||
| 			clientErr: pkg.ErrTimeout{fmt.Errorf("test timeout error")}, | ||||
| 			expectErr: pkg.ErrTimeout{fmt.Errorf("test timeout error")}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := &metadataService{tt.root, &testHttpClient{tt.resources, tt.clientErr}} | ||||
| 		data, err := service.FetchUserdata() | ||||
| 		if Error(err) != Error(tt.expectErr) { | ||||
| 			t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err) | ||||
| 		} | ||||
| 		if !bytes.Equal(data, tt.userdata) { | ||||
| 			t.Fatalf("bad userdata (%q): want %q, got %q", tt.resources, tt.userdata, data) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUrls(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root       string | ||||
| 		expectRoot string | ||||
| 		userdata   string | ||||
| 		metadata   string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root:       "/", | ||||
| 			expectRoot: "/", | ||||
| 			userdata:   "/2009-04-04/user-data", | ||||
| 			metadata:   "/2009-04-04/meta-data", | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:       "http://169.254.169.254/", | ||||
| 			expectRoot: "http://169.254.169.254/", | ||||
| 			userdata:   "http://169.254.169.254/2009-04-04/user-data", | ||||
| 			metadata:   "http://169.254.169.254/2009-04-04/meta-data", | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := &metadataService{tt.root, nil} | ||||
| 		if url := service.userdataUrl(); url != tt.userdata { | ||||
| 			t.Fatalf("bad url (%q): want %q, got %q", tt.root, tt.userdata, url) | ||||
| 		} | ||||
| 		if url := service.metadataUrl(); url != tt.metadata { | ||||
| 			t.Fatalf("bad url (%q): want %q, got %q", tt.root, tt.metadata, url) | ||||
| 		} | ||||
| 		if url := service.ConfigRoot(); url != tt.expectRoot { | ||||
| 			t.Fatalf("bad url (%q): want %q, got %q", tt.root, tt.expectRoot, url) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestFetchAttributes(t *testing.T) { | ||||
| 	for _, s := range []struct { | ||||
| 		resources map[string]string | ||||
| @@ -169,7 +48,7 @@ func TestFetchAttributes(t *testing.T) { | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			err: pkg.ErrNotFound{fmt.Errorf("test error")}, | ||||
| 			err: fmt.Errorf("test error"), | ||||
| 			tests: []struct { | ||||
| 				path string | ||||
| 				val  []string | ||||
| @@ -178,9 +57,11 @@ func TestFetchAttributes(t *testing.T) { | ||||
| 			}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		client := &testHttpClient{s.resources, s.err} | ||||
| 		service := metadataService{metadata.MetadataService{ | ||||
| 			Client: &test.HttpClient{s.resources, s.err}, | ||||
| 		}} | ||||
| 		for _, tt := range s.tests { | ||||
| 			attrs, err := fetchAttributes(client, tt.path) | ||||
| 			attrs, err := service.fetchAttributes(tt.path) | ||||
| 			if err != s.err { | ||||
| 				t.Fatalf("bad error for %q (%q): want %q, got %q", tt.path, s.resources, s.err, err) | ||||
| 			} | ||||
| @@ -221,7 +102,7 @@ func TestFetchAttribute(t *testing.T) { | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			err: pkg.ErrNotFound{fmt.Errorf("test error")}, | ||||
| 			err: fmt.Errorf("test error"), | ||||
| 			tests: []struct { | ||||
| 				path string | ||||
| 				val  string | ||||
| @@ -230,9 +111,11 @@ func TestFetchAttribute(t *testing.T) { | ||||
| 			}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		client := &testHttpClient{s.resources, s.err} | ||||
| 		service := metadataService{metadata.MetadataService{ | ||||
| 			Client: &test.HttpClient{s.resources, s.err}, | ||||
| 		}} | ||||
| 		for _, tt := range s.tests { | ||||
| 			attr, err := fetchAttribute(client, tt.path) | ||||
| 			attr, err := service.fetchAttribute(tt.path) | ||||
| 			if err != s.err { | ||||
| 				t.Fatalf("bad error for %q (%q): want %q, got %q", tt.path, s.resources, s.err, err) | ||||
| 			} | ||||
| @@ -245,21 +128,24 @@ func TestFetchAttribute(t *testing.T) { | ||||
|  | ||||
| func TestFetchMetadata(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root      string | ||||
| 		resources map[string]string | ||||
| 		expect    []byte | ||||
| 		clientErr error | ||||
| 		expectErr error | ||||
| 		root         string | ||||
| 		metadataPath string | ||||
| 		resources    map[string]string | ||||
| 		expect       []byte | ||||
| 		clientErr    error | ||||
| 		expectErr    error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root: "/", | ||||
| 			root:         "/", | ||||
| 			metadataPath: "2009-04-04/meta-data", | ||||
| 			resources: map[string]string{ | ||||
| 				"/2009-04-04/meta-data/public-keys": "bad\n", | ||||
| 			}, | ||||
| 			expectErr: fmt.Errorf("malformed public key: \"bad\""), | ||||
| 		}, | ||||
| 		{ | ||||
| 			root: "/", | ||||
| 			root:         "/", | ||||
| 			metadataPath: "2009-04-04/meta-data", | ||||
| 			resources: map[string]string{ | ||||
| 				"/2009-04-04/meta-data/hostname":                    "host", | ||||
| 				"/2009-04-04/meta-data/local-ipv4":                  "1.2.3.4", | ||||
| @@ -276,7 +162,11 @@ func TestFetchMetadata(t *testing.T) { | ||||
| 			expectErr: pkg.ErrTimeout{fmt.Errorf("test error")}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := &metadataService{tt.root, &testHttpClient{tt.resources, tt.clientErr}} | ||||
| 		service := &metadataService{metadata.MetadataService{ | ||||
| 			Root:         tt.root, | ||||
| 			Client:       &test.HttpClient{tt.resources, tt.clientErr}, | ||||
| 			MetadataPath: tt.metadataPath, | ||||
| 		}} | ||||
| 		metadata, err := service.FetchMetadata() | ||||
| 		if Error(err) != Error(tt.expectErr) { | ||||
| 			t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err) | ||||
| @@ -287,35 +177,6 @@ func TestFetchMetadata(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestNewDatasource(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root       string | ||||
| 		expectRoot string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root:       "", | ||||
| 			expectRoot: "/", | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:       "/", | ||||
| 			expectRoot: "/", | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:       "http://169.254.169.254", | ||||
| 			expectRoot: "http://169.254.169.254/", | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:       "http://169.254.169.254/", | ||||
| 			expectRoot: "http://169.254.169.254/", | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := NewDatasource(tt.root) | ||||
| 		if service.root != tt.expectRoot { | ||||
| 			t.Fatalf("bad root (%q): want %q, got %q", tt.root, tt.expectRoot, service.root) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Error(err error) string { | ||||
| 	if err != nil { | ||||
| 		return err.Error() | ||||
|   | ||||
							
								
								
									
										61
									
								
								datasource/metadata/metadata.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								datasource/metadata/metadata.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| package metadata | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/coreos/coreos-cloudinit/pkg" | ||||
| ) | ||||
|  | ||||
| type MetadataService struct { | ||||
| 	Root         string | ||||
| 	Client       pkg.Getter | ||||
| 	ApiVersion   string | ||||
| 	UserdataPath string | ||||
| 	MetadataPath string | ||||
| } | ||||
|  | ||||
| func NewDatasource(root, apiVersion, userdataPath, metadataPath string) MetadataService { | ||||
| 	if !strings.HasSuffix(root, "/") { | ||||
| 		root += "/" | ||||
| 	} | ||||
| 	return MetadataService{root, pkg.NewHttpClient(), apiVersion, userdataPath, metadataPath} | ||||
| } | ||||
|  | ||||
| func (ms MetadataService) IsAvailable() bool { | ||||
| 	_, err := ms.Client.Get(ms.Root + ms.ApiVersion) | ||||
| 	return (err == nil) | ||||
| } | ||||
|  | ||||
| func (ms MetadataService) AvailabilityChanges() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (ms MetadataService) ConfigRoot() string { | ||||
| 	return ms.Root | ||||
| } | ||||
|  | ||||
| func (ms MetadataService) FetchUserdata() ([]byte, error) { | ||||
| 	return ms.FetchData(ms.UserdataUrl()) | ||||
| } | ||||
|  | ||||
| func (ms MetadataService) FetchNetworkConfig(filename string) ([]byte, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func (ms MetadataService) FetchData(url string) ([]byte, error) { | ||||
| 	if data, err := ms.Client.GetRetry(url); err == nil { | ||||
| 		return data, err | ||||
| 	} else if _, ok := err.(pkg.ErrNotFound); ok { | ||||
| 		return []byte{}, nil | ||||
| 	} else { | ||||
| 		return data, err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (ms MetadataService) MetadataUrl() string { | ||||
| 	return (ms.Root + ms.MetadataPath) | ||||
| } | ||||
|  | ||||
| func (ms MetadataService) UserdataUrl() string { | ||||
| 	return (ms.Root + ms.UserdataPath) | ||||
| } | ||||
							
								
								
									
										171
									
								
								datasource/metadata/metadata_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								datasource/metadata/metadata_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| package metadata | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coreos/coreos-cloudinit/datasource/metadata/test" | ||||
| 	"github.com/coreos/coreos-cloudinit/pkg" | ||||
| ) | ||||
|  | ||||
| func TestAvailabilityChanges(t *testing.T) { | ||||
| 	want := true | ||||
| 	if ac := (MetadataService{}).AvailabilityChanges(); ac != want { | ||||
| 		t.Fatalf("bad AvailabilityChanges: want %q, got %q", want, ac) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestIsAvailable(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root       string | ||||
| 		apiVersion string | ||||
| 		resources  map[string]string | ||||
| 		expect     bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root:       "/", | ||||
| 			apiVersion: "2009-04-04", | ||||
| 			resources: map[string]string{ | ||||
| 				"/2009-04-04": "", | ||||
| 			}, | ||||
| 			expect: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:      "/", | ||||
| 			resources: map[string]string{}, | ||||
| 			expect:    false, | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := &MetadataService{ | ||||
| 			Root:       tt.root, | ||||
| 			Client:     &test.HttpClient{tt.resources, nil}, | ||||
| 			ApiVersion: tt.apiVersion, | ||||
| 		} | ||||
| 		if a := service.IsAvailable(); a != tt.expect { | ||||
| 			t.Fatalf("bad isAvailable (%q): want %q, got %q", tt.resources, tt.expect, a) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestFetchUserdata(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root         string | ||||
| 		userdataPath string | ||||
| 		resources    map[string]string | ||||
| 		userdata     []byte | ||||
| 		clientErr    error | ||||
| 		expectErr    error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root:         "/", | ||||
| 			userdataPath: "2009-04-04/user-data", | ||||
| 			resources: map[string]string{ | ||||
| 				"/2009-04-04/user-data": "hello", | ||||
| 			}, | ||||
| 			userdata: []byte("hello"), | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:      "/", | ||||
| 			clientErr: pkg.ErrNotFound{fmt.Errorf("test not found error")}, | ||||
| 			userdata:  []byte{}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:      "/", | ||||
| 			clientErr: pkg.ErrTimeout{fmt.Errorf("test timeout error")}, | ||||
| 			expectErr: pkg.ErrTimeout{fmt.Errorf("test timeout error")}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := &MetadataService{ | ||||
| 			Root:         tt.root, | ||||
| 			Client:       &test.HttpClient{tt.resources, tt.clientErr}, | ||||
| 			UserdataPath: tt.userdataPath, | ||||
| 		} | ||||
| 		data, err := service.FetchUserdata() | ||||
| 		if Error(err) != Error(tt.expectErr) { | ||||
| 			t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err) | ||||
| 		} | ||||
| 		if !bytes.Equal(data, tt.userdata) { | ||||
| 			t.Fatalf("bad userdata (%q): want %q, got %q", tt.resources, tt.userdata, data) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUrls(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root         string | ||||
| 		userdataPath string | ||||
| 		metadataPath string | ||||
| 		expectRoot   string | ||||
| 		userdata     string | ||||
| 		metadata     string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root:         "/", | ||||
| 			userdataPath: "2009-04-04/user-data", | ||||
| 			metadataPath: "2009-04-04/meta-data", | ||||
| 			expectRoot:   "/", | ||||
| 			userdata:     "/2009-04-04/user-data", | ||||
| 			metadata:     "/2009-04-04/meta-data", | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:         "http://169.254.169.254/", | ||||
| 			userdataPath: "2009-04-04/user-data", | ||||
| 			metadataPath: "2009-04-04/meta-data", | ||||
| 			expectRoot:   "http://169.254.169.254/", | ||||
| 			userdata:     "http://169.254.169.254/2009-04-04/user-data", | ||||
| 			metadata:     "http://169.254.169.254/2009-04-04/meta-data", | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := &MetadataService{ | ||||
| 			Root:         tt.root, | ||||
| 			UserdataPath: tt.userdataPath, | ||||
| 			MetadataPath: tt.metadataPath, | ||||
| 		} | ||||
| 		if url := service.UserdataUrl(); url != tt.userdata { | ||||
| 			t.Fatalf("bad url (%q): want %q, got %q", tt.root, tt.userdata, url) | ||||
| 		} | ||||
| 		if url := service.MetadataUrl(); url != tt.metadata { | ||||
| 			t.Fatalf("bad url (%q): want %q, got %q", tt.root, tt.metadata, url) | ||||
| 		} | ||||
| 		if url := service.ConfigRoot(); url != tt.expectRoot { | ||||
| 			t.Fatalf("bad url (%q): want %q, got %q", tt.root, tt.expectRoot, url) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestNewDatasource(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		root       string | ||||
| 		expectRoot string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			root:       "", | ||||
| 			expectRoot: "/", | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:       "/", | ||||
| 			expectRoot: "/", | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:       "http://169.254.169.254", | ||||
| 			expectRoot: "http://169.254.169.254/", | ||||
| 		}, | ||||
| 		{ | ||||
| 			root:       "http://169.254.169.254/", | ||||
| 			expectRoot: "http://169.254.169.254/", | ||||
| 		}, | ||||
| 	} { | ||||
| 		service := NewDatasource(tt.root, "", "", "") | ||||
| 		if service.Root != tt.expectRoot { | ||||
| 			t.Fatalf("bad root (%q): want %q, got %q", tt.root, tt.expectRoot, service.Root) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Error(err error) string { | ||||
| 	if err != nil { | ||||
| 		return err.Error() | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
							
								
								
									
										27
									
								
								datasource/metadata/test/test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								datasource/metadata/test/test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| package test | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/coreos/coreos-cloudinit/pkg" | ||||
| ) | ||||
|  | ||||
| type HttpClient struct { | ||||
| 	Resources map[string]string | ||||
| 	Err       error | ||||
| } | ||||
|  | ||||
| func (t *HttpClient) GetRetry(url string) ([]byte, error) { | ||||
| 	if t.Err != nil { | ||||
| 		return nil, t.Err | ||||
| 	} | ||||
| 	if val, ok := t.Resources[url]; ok { | ||||
| 		return []byte(val), nil | ||||
| 	} else { | ||||
| 		return nil, pkg.ErrNotFound{fmt.Errorf("not found: %q", url)} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *HttpClient) Get(url string) ([]byte, error) { | ||||
| 	return t.GetRetry(url) | ||||
| } | ||||
| @@ -66,6 +66,10 @@ func (c *procCmdline) FetchUserdata() ([]byte, error) { | ||||
| 	return cfg, nil | ||||
| } | ||||
|  | ||||
| func (c *procCmdline) FetchNetworkConfig(filename string) ([]byte, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func (c *procCmdline) Type() string { | ||||
| 	return "proc-cmdline" | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| package url | ||||
|  | ||||
| import "github.com/coreos/coreos-cloudinit/pkg" | ||||
| import ( | ||||
| 	"github.com/coreos/coreos-cloudinit/pkg" | ||||
| ) | ||||
|  | ||||
| type remoteFile struct { | ||||
| 	url string | ||||
| @@ -33,6 +35,10 @@ func (f *remoteFile) FetchUserdata() ([]byte, error) { | ||||
| 	return client.GetRetry(f.url) | ||||
| } | ||||
|  | ||||
| func (f *remoteFile) FetchNetworkConfig(filename string) ([]byte, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func (f *remoteFile) Type() string { | ||||
| 	return "url" | ||||
| } | ||||
|   | ||||
| @@ -3,7 +3,6 @@ package initialize | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"path" | ||||
|  | ||||
| @@ -42,6 +41,7 @@ type CloudConfig struct { | ||||
| 	Users             []system.User | ||||
| 	ManageEtcHosts    EtcHosts `yaml:"manage_etc_hosts"` | ||||
| 	NetworkConfigPath string | ||||
| 	NetworkConfig     string | ||||
| } | ||||
|  | ||||
| type warner func(format string, v ...interface{}) | ||||
| @@ -258,17 +258,11 @@ func Apply(cfg CloudConfig, env *Environment) error { | ||||
| 	} | ||||
|  | ||||
| 	if env.NetconfType() != "" { | ||||
| 		filename := path.Join(env.ConfigRoot(), cfg.NetworkConfigPath) | ||||
| 		log.Printf("Attempting to read config from %q\n", filename) | ||||
| 		netconfBytes, err := ioutil.ReadFile(filename) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		var interfaces []network.InterfaceGenerator | ||||
| 		var err error | ||||
| 		switch env.NetconfType() { | ||||
| 		case "debian": | ||||
| 			interfaces, err = network.ProcessDebianNetconf(string(netconfBytes)) | ||||
| 			interfaces, err = network.ProcessDebianNetconf(cfg.NetworkConfig) | ||||
| 		default: | ||||
| 			return fmt.Errorf("Unsupported network config format %q", env.NetconfType()) | ||||
| 		} | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package network | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| @@ -25,13 +26,25 @@ type networkInterface interface { | ||||
|  | ||||
| type logicalInterface struct { | ||||
| 	name        string | ||||
| 	hwaddr      net.HardwareAddr | ||||
| 	config      configMethod | ||||
| 	children    []networkInterface | ||||
| 	configDepth int | ||||
| } | ||||
|  | ||||
| func (i *logicalInterface) Name() string { | ||||
| 	return i.name | ||||
| } | ||||
|  | ||||
| func (i *logicalInterface) Network() string { | ||||
| 	config := fmt.Sprintf("[Match]\nName=%s\n\n[Network]\n", i.name) | ||||
| 	config := fmt.Sprintln("[Match]") | ||||
| 	if i.name != "" { | ||||
| 		config += fmt.Sprintf("Name=%s\n", i.name) | ||||
| 	} | ||||
| 	if i.hwaddr != nil { | ||||
| 		config += fmt.Sprintf("MACAddress=%s\n", i.hwaddr) | ||||
| 	} | ||||
| 	config += "\n[Network]\n" | ||||
|  | ||||
| 	for _, child := range i.children { | ||||
| 		switch iface := child.(type) { | ||||
| @@ -47,8 +60,8 @@ func (i *logicalInterface) Network() string { | ||||
| 		for _, nameserver := range conf.nameservers { | ||||
| 			config += fmt.Sprintf("DNS=%s\n", nameserver) | ||||
| 		} | ||||
| 		if conf.address.IP != nil { | ||||
| 			config += fmt.Sprintf("\n[Address]\nAddress=%s\n", conf.address.String()) | ||||
| 		for _, addr := range conf.addresses { | ||||
| 			config += fmt.Sprintf("\n[Address]\nAddress=%s\n", addr.String()) | ||||
| 		} | ||||
| 		for _, route := range conf.routes { | ||||
| 			config += fmt.Sprintf("\n[Route]\nDestination=%s\nGateway=%s\n", route.destination.String(), route.gateway) | ||||
| @@ -64,6 +77,10 @@ func (i *logicalInterface) Link() string { | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (i *logicalInterface) Netdev() string { | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (i *logicalInterface) Filename() string { | ||||
| 	return fmt.Sprintf("%02x-%s", i.configDepth, i.name) | ||||
| } | ||||
| @@ -84,14 +101,6 @@ type physicalInterface struct { | ||||
| 	logicalInterface | ||||
| } | ||||
|  | ||||
| func (p *physicalInterface) Name() string { | ||||
| 	return p.name | ||||
| } | ||||
|  | ||||
| func (p *physicalInterface) Netdev() string { | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (p *physicalInterface) Type() string { | ||||
| 	return "physical" | ||||
| } | ||||
| @@ -102,10 +111,6 @@ type bondInterface struct { | ||||
| 	options map[string]string | ||||
| } | ||||
|  | ||||
| func (b *bondInterface) Name() string { | ||||
| 	return b.name | ||||
| } | ||||
|  | ||||
| func (b *bondInterface) Netdev() string { | ||||
| 	return fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name) | ||||
| } | ||||
| @@ -129,10 +134,6 @@ type vlanInterface struct { | ||||
| 	rawDevice string | ||||
| } | ||||
|  | ||||
| func (v *vlanInterface) Name() string { | ||||
| 	return v.name | ||||
| } | ||||
|  | ||||
| func (v *vlanInterface) Netdev() string { | ||||
| 	config := fmt.Sprintf("[NetDev]\nKind=vlan\nName=%s\n", v.name) | ||||
| 	switch c := v.config.(type) { | ||||
|   | ||||
| @@ -6,241 +6,101 @@ import ( | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestPhysicalInterfaceName(t *testing.T) { | ||||
| 	p := physicalInterface{logicalInterface{name: "testname"}} | ||||
| 	if p.Name() != "testname" { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestPhysicalInterfaceNetdev(t *testing.T) { | ||||
| 	p := physicalInterface{} | ||||
| 	if p.Netdev() != "" { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestPhysicalInterfaceLink(t *testing.T) { | ||||
| 	p := physicalInterface{} | ||||
| 	if p.Link() != "" { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestPhysicalInterfaceNetwork(t *testing.T) { | ||||
| 	p := physicalInterface{logicalInterface{ | ||||
| 		name: "testname", | ||||
| 		children: []networkInterface{ | ||||
| 			&bondInterface{ | ||||
| 				logicalInterface{ | ||||
| 					name: "testbond1", | ||||
| 				}, | ||||
| 				nil, | ||||
| 				nil, | ||||
| 			}, | ||||
| 			&vlanInterface{ | ||||
| 				logicalInterface{ | ||||
| 					name: "testvlan1", | ||||
| 				}, | ||||
| 				1, | ||||
| 				"", | ||||
| 			}, | ||||
| 			&vlanInterface{ | ||||
| 				logicalInterface{ | ||||
| 					name: "testvlan2", | ||||
| 				}, | ||||
| 				1, | ||||
| 				"", | ||||
| 			}, | ||||
| 		}, | ||||
| 	}} | ||||
| 	network := `[Match] | ||||
| Name=testname | ||||
|  | ||||
| [Network] | ||||
| Bond=testbond1 | ||||
| VLAN=testvlan1 | ||||
| VLAN=testvlan2 | ||||
| ` | ||||
| 	if p.Network() != network { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestBondInterfaceName(t *testing.T) { | ||||
| 	b := bondInterface{logicalInterface{name: "testname"}, nil, nil} | ||||
| 	if b.Name() != "testname" { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestBondInterfaceNetdev(t *testing.T) { | ||||
| 	b := bondInterface{logicalInterface{name: "testname"}, nil, nil} | ||||
| 	netdev := `[NetDev] | ||||
| Kind=bond | ||||
| Name=testname | ||||
| ` | ||||
| 	if b.Netdev() != netdev { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestBondInterfaceLink(t *testing.T) { | ||||
| 	b := bondInterface{} | ||||
| 	if b.Link() != "" { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestBondInterfaceNetwork(t *testing.T) { | ||||
| 	b := bondInterface{ | ||||
| 		logicalInterface{ | ||||
| 			name:   "testname", | ||||
| 			config: configMethodDHCP{}, | ||||
| 			children: []networkInterface{ | ||||
| 				&bondInterface{ | ||||
| 					logicalInterface{ | ||||
| 						name: "testbond1", | ||||
| 					}, | ||||
| 					nil, | ||||
| 					nil, | ||||
| 				}, | ||||
| 				&vlanInterface{ | ||||
| 					logicalInterface{ | ||||
| 						name: "testvlan1", | ||||
| 					}, | ||||
| 					1, | ||||
| 					"", | ||||
| 				}, | ||||
| 				&vlanInterface{ | ||||
| 					logicalInterface{ | ||||
| 						name: "testvlan2", | ||||
| 					}, | ||||
| 					1, | ||||
| 					"", | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	} | ||||
| 	network := `[Match] | ||||
| Name=testname | ||||
|  | ||||
| [Network] | ||||
| Bond=testbond1 | ||||
| VLAN=testvlan1 | ||||
| VLAN=testvlan2 | ||||
| DHCP=true | ||||
| ` | ||||
| 	if b.Network() != network { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestVLANInterfaceName(t *testing.T) { | ||||
| 	v := vlanInterface{logicalInterface{name: "testname"}, 1, ""} | ||||
| 	if v.Name() != "testname" { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestVLANInterfaceNetdev(t *testing.T) { | ||||
| func TestInterfaceGenerators(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		i vlanInterface | ||||
| 		l string | ||||
| 		name    string | ||||
| 		netdev  string | ||||
| 		link    string | ||||
| 		network string | ||||
| 		kind    string | ||||
| 		iface   InterfaceGenerator | ||||
| 	}{ | ||||
| 		{ | ||||
| 			vlanInterface{logicalInterface{name: "testname"}, 1, ""}, | ||||
| 			"[NetDev]\nKind=vlan\nName=testname\n\n[VLAN]\nId=1\n", | ||||
| 			name:    "", | ||||
| 			network: "[Match]\nMACAddress=00:01:02:03:04:05\n\n[Network]\n", | ||||
| 			kind:    "physical", | ||||
| 			iface: &physicalInterface{logicalInterface{ | ||||
| 				hwaddr: net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5}), | ||||
| 			}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			vlanInterface{logicalInterface{name: "testname", config: configMethodStatic{hwaddress: net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5})}}, 1, ""}, | ||||
| 			"[NetDev]\nKind=vlan\nName=testname\nMACAddress=00:01:02:03:04:05\n\n[VLAN]\nId=1\n", | ||||
| 			name:    "testname", | ||||
| 			network: "[Match]\nName=testname\n\n[Network]\nBond=testbond1\nVLAN=testvlan1\nVLAN=testvlan2\n", | ||||
| 			kind:    "physical", | ||||
| 			iface: &physicalInterface{logicalInterface{ | ||||
| 				name: "testname", | ||||
| 				children: []networkInterface{ | ||||
| 					&bondInterface{logicalInterface: logicalInterface{name: "testbond1"}}, | ||||
| 					&vlanInterface{logicalInterface: logicalInterface{name: "testvlan1"}, id: 1}, | ||||
| 					&vlanInterface{logicalInterface: logicalInterface{name: "testvlan2"}, id: 1}, | ||||
| 				}, | ||||
| 			}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			vlanInterface{logicalInterface{name: "testname", config: configMethodDHCP{hwaddress: net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5})}}, 1, ""}, | ||||
| 			"[NetDev]\nKind=vlan\nName=testname\nMACAddress=00:01:02:03:04:05\n\n[VLAN]\nId=1\n", | ||||
| 			name:    "testname", | ||||
| 			netdev:  "[NetDev]\nKind=bond\nName=testname\n", | ||||
| 			network: "[Match]\nName=testname\n\n[Network]\nBond=testbond1\nVLAN=testvlan1\nVLAN=testvlan2\nDHCP=true\n", | ||||
| 			kind:    "bond", | ||||
| 			iface: &bondInterface{logicalInterface: logicalInterface{ | ||||
| 				name:   "testname", | ||||
| 				config: configMethodDHCP{}, | ||||
| 				children: []networkInterface{ | ||||
| 					&bondInterface{logicalInterface: logicalInterface{name: "testbond1"}}, | ||||
| 					&vlanInterface{logicalInterface: logicalInterface{name: "testvlan1"}, id: 1}, | ||||
| 					&vlanInterface{logicalInterface: logicalInterface{name: "testvlan2"}, id: 1}, | ||||
| 				}, | ||||
| 			}}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "testname", | ||||
| 			netdev:  "[NetDev]\nKind=vlan\nName=testname\n\n[VLAN]\nId=1\n", | ||||
| 			network: "[Match]\nName=testname\n\n[Network]\n", | ||||
| 			kind:    "vlan", | ||||
| 			iface:   &vlanInterface{logicalInterface{name: "testname"}, 1, ""}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "testname", | ||||
| 			netdev:  "[NetDev]\nKind=vlan\nName=testname\nMACAddress=00:01:02:03:04:05\n\n[VLAN]\nId=1\n", | ||||
| 			network: "[Match]\nName=testname\n\n[Network]\n", | ||||
| 			kind:    "vlan", | ||||
| 			iface:   &vlanInterface{logicalInterface{name: "testname", config: configMethodStatic{hwaddress: net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5})}}, 1, ""}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "testname", | ||||
| 			netdev:  "[NetDev]\nKind=vlan\nName=testname\nMACAddress=00:01:02:03:04:05\n\n[VLAN]\nId=1\n", | ||||
| 			network: "[Match]\nName=testname\n\n[Network]\nDHCP=true\n", | ||||
| 			kind:    "vlan", | ||||
| 			iface:   &vlanInterface{logicalInterface{name: "testname", config: configMethodDHCP{hwaddress: net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5})}}, 1, ""}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "testname", | ||||
| 			netdev:  "[NetDev]\nKind=vlan\nName=testname\n\n[VLAN]\nId=0\n", | ||||
| 			network: "[Match]\nName=testname\n\n[Network]\nDNS=8.8.8.8\n\n[Address]\nAddress=192.168.1.100/24\n\n[Route]\nDestination=0.0.0.0/0\nGateway=1.2.3.4\n", | ||||
| 			kind:    "vlan", | ||||
| 			iface: &vlanInterface{logicalInterface: logicalInterface{ | ||||
| 				name: "testname", | ||||
| 				config: configMethodStatic{ | ||||
| 					addresses:   []net.IPNet{{IP: []byte{192, 168, 1, 100}, Mask: []byte{255, 255, 255, 0}}}, | ||||
| 					nameservers: []net.IP{[]byte{8, 8, 8, 8}}, | ||||
| 					routes:      []route{route{destination: net.IPNet{IP: []byte{0, 0, 0, 0}, Mask: []byte{0, 0, 0, 0}}, gateway: []byte{1, 2, 3, 4}}}, | ||||
| 				}, | ||||
| 			}}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		if tt.i.Netdev() != tt.l { | ||||
| 			t.Fatalf("bad netdev config (%q): got %q, want %q", tt.i, tt.i.Netdev(), tt.l) | ||||
| 		if name := tt.iface.Name(); name != tt.name { | ||||
| 			t.Fatalf("bad name (%q): want %q, got %q", tt.iface, tt.name, name) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestVLANInterfaceLink(t *testing.T) { | ||||
| 	v := vlanInterface{} | ||||
| 	if v.Link() != "" { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestVLANInterfaceNetwork(t *testing.T) { | ||||
| 	v := vlanInterface{ | ||||
| 		logicalInterface{ | ||||
| 			name: "testname", | ||||
| 			config: configMethodStatic{ | ||||
| 				address: net.IPNet{ | ||||
| 					IP:   []byte{192, 168, 1, 100}, | ||||
| 					Mask: []byte{255, 255, 255, 0}, | ||||
| 				}, | ||||
| 				nameservers: []net.IP{ | ||||
| 					[]byte{8, 8, 8, 8}, | ||||
| 				}, | ||||
| 				routes: []route{ | ||||
| 					route{ | ||||
| 						destination: net.IPNet{ | ||||
| 							IP:   []byte{0, 0, 0, 0}, | ||||
| 							Mask: []byte{0, 0, 0, 0}, | ||||
| 						}, | ||||
| 						gateway: []byte{1, 2, 3, 4}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		0, | ||||
| 		"", | ||||
| 	} | ||||
| 	network := `[Match] | ||||
| Name=testname | ||||
|  | ||||
| [Network] | ||||
| DNS=8.8.8.8 | ||||
|  | ||||
| [Address] | ||||
| Address=192.168.1.100/24 | ||||
|  | ||||
| [Route] | ||||
| Destination=0.0.0.0/0 | ||||
| Gateway=1.2.3.4 | ||||
| ` | ||||
| 	if v.Network() != network { | ||||
| 		t.Log(v.Network()) | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestType(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		i InterfaceGenerator | ||||
| 		t string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			i: &physicalInterface{}, | ||||
| 			t: "physical", | ||||
| 		}, | ||||
| 		{ | ||||
| 			i: &vlanInterface{}, | ||||
| 			t: "vlan", | ||||
| 		}, | ||||
| 		{ | ||||
| 			i: &bondInterface{}, | ||||
| 			t: "bond", | ||||
| 		}, | ||||
| 	} { | ||||
| 		if tp := tt.i.Type(); tp != tt.t { | ||||
| 			t.Fatalf("bad type (%q): got %s, want %s", tt.i, tp, tt.t) | ||||
| 		if netdev := tt.iface.Netdev(); netdev != tt.netdev { | ||||
| 			t.Fatalf("bad netdev (%q): want %q, got %q", tt.iface, tt.netdev, netdev) | ||||
| 		} | ||||
| 		if link := tt.iface.Link(); link != tt.link { | ||||
| 			t.Fatalf("bad link (%q): want %q, got %q", tt.iface, tt.link, link) | ||||
| 		} | ||||
| 		if network := tt.iface.Network(); network != tt.network { | ||||
| 			t.Fatalf("bad network (%q): want %q, got %q", tt.iface, tt.network, network) | ||||
| 		} | ||||
| 		if kind := tt.iface.Type(); kind != tt.kind { | ||||
| 			t.Fatalf("bad type (%q): want %q, got %q", tt.iface, tt.kind, kind) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -37,7 +37,7 @@ type route struct { | ||||
| type configMethod interface{} | ||||
|  | ||||
| type configMethodStatic struct { | ||||
| 	address     net.IPNet | ||||
| 	addresses   []net.IPNet | ||||
| 	nameservers []net.IP | ||||
| 	routes      []route | ||||
| 	hwaddress   net.HardwareAddr | ||||
| @@ -193,20 +193,21 @@ func parseInterfaceStanza(attributes []string, options []string) (*stanzaInterfa | ||||
| 	switch confMethod { | ||||
| 	case "static": | ||||
| 		config := configMethodStatic{ | ||||
| 			addresses:   make([]net.IPNet, 1), | ||||
| 			routes:      make([]route, 0), | ||||
| 			nameservers: make([]net.IP, 0), | ||||
| 		} | ||||
| 		if addresses, ok := optionMap["address"]; ok { | ||||
| 			if len(addresses) == 1 { | ||||
| 				config.address.IP = net.ParseIP(addresses[0]) | ||||
| 				config.addresses[0].IP = net.ParseIP(addresses[0]) | ||||
| 			} | ||||
| 		} | ||||
| 		if netmasks, ok := optionMap["netmask"]; ok { | ||||
| 			if len(netmasks) == 1 { | ||||
| 				config.address.Mask = net.IPMask(net.ParseIP(netmasks[0]).To4()) | ||||
| 				config.addresses[0].Mask = net.IPMask(net.ParseIP(netmasks[0]).To4()) | ||||
| 			} | ||||
| 		} | ||||
| 		if config.address.IP == nil || config.address.Mask == nil { | ||||
| 		if config.addresses[0].IP == nil || config.addresses[0].Mask == nil { | ||||
| 			return nil, fmt.Errorf("malformed static network config for %q", iface) | ||||
| 		} | ||||
| 		if gateways, ok := optionMap["gateway"]; ok { | ||||
|   | ||||
| @@ -194,9 +194,11 @@ func TestParseVLANStanzas(t *testing.T) { | ||||
|  | ||||
| func TestParseInterfaceStanzaStaticAddress(t *testing.T) { | ||||
| 	options := []string{"address 192.168.1.100", "netmask 255.255.255.0"} | ||||
| 	expect := net.IPNet{ | ||||
| 		IP:   net.IPv4(192, 168, 1, 100), | ||||
| 		Mask: net.IPv4Mask(255, 255, 255, 0), | ||||
| 	expect := []net.IPNet{ | ||||
| 		{ | ||||
| 			IP:   net.IPv4(192, 168, 1, 100), | ||||
| 			Mask: net.IPv4Mask(255, 255, 255, 0), | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	iface, err := parseInterfaceStanza([]string{"eth", "inet", "static"}, options) | ||||
| @@ -207,7 +209,7 @@ func TestParseInterfaceStanzaStaticAddress(t *testing.T) { | ||||
| 	if !ok { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| 	if !reflect.DeepEqual(static.address, expect) { | ||||
| 	if !reflect.DeepEqual(static.addresses, expect) { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -74,15 +74,14 @@ func maybeProbe8012q(interfaces []network.InterfaceGenerator) error { | ||||
| } | ||||
|  | ||||
| func maybeProbeBonding(interfaces []network.InterfaceGenerator) error { | ||||
| 	args := []string{"bonding"} | ||||
| 	for _, iface := range interfaces { | ||||
| 		if iface.Type() == "bond" { | ||||
| 			args = append(args, strings.Split(iface.ModprobeParams(), " ")...) | ||||
| 			break | ||||
| 			args := append([]string{"bonding"}, strings.Split(iface.ModprobeParams(), " ")...) | ||||
| 			log.Printf("Probing LKM %q (%q)\n", "bonding", args) | ||||
| 			return exec.Command("modprobe", args...).Run() | ||||
| 		} | ||||
| 	} | ||||
| 	log.Printf("Probing LKM %q (%q)\n", "bonding", args) | ||||
| 	return exec.Command("modprobe", args...).Run() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func restartNetworkd() error { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user