From a923161f4acfd4091994ff0b4765e7fe6647ab60 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 15 Aug 2014 18:13:37 -0700 Subject: [PATCH] metadata: Refactor common parts out of ec2 --- datasource/datasource.go | 5 - datasource/metadata/ec2/metadata.go | 64 ++------ datasource/metadata/ec2/metadata_test.go | 193 ++++------------------- datasource/metadata/metadata.go | 57 +++++++ datasource/metadata/metadata_test.go | 171 ++++++++++++++++++++ datasource/metadata/test/test.go | 27 ++++ test | 1 + 7 files changed, 298 insertions(+), 220 deletions(-) create mode 100644 datasource/metadata/metadata.go create mode 100644 datasource/metadata/metadata_test.go create mode 100644 datasource/metadata/test/test.go diff --git a/datasource/datasource.go b/datasource/datasource.go index 7de7f23..7146925 100644 --- a/datasource/datasource.go +++ b/datasource/datasource.go @@ -1,10 +1,5 @@ package datasource -const ( - Ec2ApiVersion = "2009-04-04" - OpenstackApiVersion = "2012-08-10" -) - type Datasource interface { IsAvailable() bool AvailabilityChanges() bool diff --git a/datasource/metadata/ec2/metadata.go b/datasource/metadata/ec2/metadata.go index 9079543..9e284e6 100644 --- a/datasource/metadata/ec2/metadata.go +++ b/datasource/metadata/ec2/metadata.go @@ -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 diff --git a/datasource/metadata/ec2/metadata_test.go b/datasource/metadata/ec2/metadata_test.go index 6a1cc10..993ec19 100644 --- a/datasource/metadata/ec2/metadata_test.go +++ b/datasource/metadata/ec2/metadata_test.go @@ -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() diff --git a/datasource/metadata/metadata.go b/datasource/metadata/metadata.go new file mode 100644 index 0000000..9f58bdb --- /dev/null +++ b/datasource/metadata/metadata.go @@ -0,0 +1,57 @@ +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) 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) +} diff --git a/datasource/metadata/metadata_test.go b/datasource/metadata/metadata_test.go new file mode 100644 index 0000000..55291d3 --- /dev/null +++ b/datasource/metadata/metadata_test.go @@ -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 "" +} diff --git a/datasource/metadata/test/test.go b/datasource/metadata/test/test.go new file mode 100644 index 0000000..2fc408d --- /dev/null +++ b/datasource/metadata/test/test.go @@ -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) +} diff --git a/test b/test index 0d0a7a9..74812c4 100755 --- a/test +++ b/test @@ -18,6 +18,7 @@ declare -a TESTPKGS=(initialize datasource datasource/configdrive datasource/file + datasource/metadata datasource/metadata/cloudsigma datasource/metadata/ec2 datasource/proc_cmdline