From d68ae84b371dbf74fa5290d1e49d9b1d7374adca Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Wed, 30 Jul 2014 14:23:10 -0700 Subject: [PATCH] metadata: Refactor metadata service into ec2 metadata Added more testing. --- coreos-cloudinit.go | 23 +- datasource/metadata/ec2/metadata.go | 141 ++++++++ datasource/metadata/ec2/metadata_test.go | 324 +++++++++++++++++++ datasource/metadata/metadata_service.go | 151 --------- datasource/metadata/metadata_service_test.go | 159 --------- pkg/http_client.go | 1 + test | 2 +- 7 files changed, 481 insertions(+), 320 deletions(-) create mode 100644 datasource/metadata/ec2/metadata.go create mode 100644 datasource/metadata/ec2/metadata_test.go delete mode 100644 datasource/metadata/metadata_service.go delete mode 100644 datasource/metadata/metadata_service_test.go diff --git a/coreos-cloudinit.go b/coreos-cloudinit.go index 9e2ee66..2e514b5 100644 --- a/coreos-cloudinit.go +++ b/coreos-cloudinit.go @@ -10,7 +10,7 @@ import ( "github.com/coreos/coreos-cloudinit/datasource" "github.com/coreos/coreos-cloudinit/datasource/configdrive" "github.com/coreos/coreos-cloudinit/datasource/file" - "github.com/coreos/coreos-cloudinit/datasource/metadata" + "github.com/coreos/coreos-cloudinit/datasource/metadata/ec2" "github.com/coreos/coreos-cloudinit/datasource/proc_cmdline" "github.com/coreos/coreos-cloudinit/datasource/url" "github.com/coreos/coreos-cloudinit/initialize" @@ -29,11 +29,12 @@ var ( printVersion bool ignoreFailure bool sources struct { - file string - configDrive string - metadataService bool - url string - procCmdLine bool + file string + configDrive string + metadataService bool + ec2MetadataService string + url string + procCmdLine bool } convertNetconf string workspace string @@ -45,7 +46,8 @@ func init() { flag.BoolVar(&ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data") flag.StringVar(&sources.file, "from-file", "", "Read user-data from provided file") flag.StringVar(&sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory") - flag.BoolVar(&sources.metadataService, "from-metadata-service", false, "Download data from metadata service") + flag.BoolVar(&sources.metadataService, "from-metadata-service", false, "[DEPRECATED - Use -from-ec2-metadata] Download data from metadata service") + flag.StringVar(&sources.ec2MetadataService, "from-ec2-metadata", "", "Download data from the provided metadata service") flag.StringVar(&sources.url, "from-url", "", "Download user-data from provided url") flag.BoolVar(&sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=', using the cloud-config served by an HTTP GET to ", proc_cmdline.ProcCmdlineLocation, proc_cmdline.ProcCmdlineCloudConfigFlag)) flag.StringVar(&convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files (requires the -from-configdrive flag)") @@ -83,7 +85,7 @@ func main() { dss := getDatasources() if len(dss) == 0 { - fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-metadata-service, --from-url or --from-proc-cmdline") + fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-url or --from-proc-cmdline") os.Exit(1) } @@ -210,7 +212,10 @@ func getDatasources() []datasource.Datasource { dss = append(dss, configdrive.NewDatasource(sources.configDrive)) } if sources.metadataService { - dss = append(dss, metadata.NewDatasource()) + dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress)) + } + if sources.ec2MetadataService != "" { + dss = append(dss, ec2.NewDatasource(sources.ec2MetadataService)) } if sources.procCmdLine { dss = append(dss, proc_cmdline.NewDatasource()) diff --git a/datasource/metadata/ec2/metadata.go b/datasource/metadata/ec2/metadata.go new file mode 100644 index 0000000..9079543 --- /dev/null +++ b/datasource/metadata/ec2/metadata.go @@ -0,0 +1,141 @@ +package ec2 + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "strings" + + "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" +) + +type metadataService struct { + root string + client pkg.Getter +} + +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 +} + +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 { + keyIDs := make(map[string]string) + for _, keyname := range keynames { + tokens := strings.SplitN(keyname, "=", 2) + if len(tokens) != 2 { + return nil, fmt.Errorf("malformed public key: %q", keyname) + } + keyIDs[tokens[1]] = tokens[0] + } + + 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)) + if err != nil { + return nil, err + } + keys[name] = sshkey + fmt.Printf("Found SSH key for %q\n", name) + } + attrs["public_keys"] = keys + } else if _, ok := err.(pkg.ErrNotFound); !ok { + return nil, err + } + + if hostname, err := fetchAttribute(ms.client, 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 { + 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 { + 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 { + attrs["network_config"] = map[string]string{ + "content_path": content_path, + } + } else if _, ok := err.(pkg.ErrNotFound); !ok { + return nil, err + } + + 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) + if err != nil { + return nil, err + } + scanner := bufio.NewScanner(bytes.NewBuffer(resp)) + data := make([]string, 0) + for scanner.Scan() { + data = append(data, scanner.Text()) + } + return data, scanner.Err() +} + +func fetchAttribute(client pkg.Getter, url string) (string, error) { + if attrs, err := fetchAttributes(client, 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 new file mode 100644 index 0000000..6a1cc10 --- /dev/null +++ b/datasource/metadata/ec2/metadata_test.go @@ -0,0 +1,324 @@ +package ec2 + +import ( + "bytes" + "fmt" + "reflect" + "testing" + + "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 { + t.Fatalf("bad type: want %q, got %q", want, kind) + } +} + +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 + err error + tests []struct { + path string + val []string + } + }{ + { + resources: map[string]string{ + "/": "a\nb\nc/", + "/c/": "d\ne/", + "/c/e/": "f", + "/a": "1", + "/b": "2", + "/c/d": "3", + "/c/e/f": "4", + }, + tests: []struct { + path string + val []string + }{ + {"/", []string{"a", "b", "c/"}}, + {"/b", []string{"2"}}, + {"/c/d", []string{"3"}}, + {"/c/e/", []string{"f"}}, + }, + }, + { + err: pkg.ErrNotFound{fmt.Errorf("test error")}, + tests: []struct { + path string + val []string + }{ + {"", nil}, + }, + }, + } { + client := &testHttpClient{s.resources, s.err} + for _, tt := range s.tests { + attrs, err := fetchAttributes(client, tt.path) + if err != s.err { + t.Fatalf("bad error for %q (%q): want %q, got %q", tt.path, s.resources, s.err, err) + } + if !reflect.DeepEqual(attrs, tt.val) { + t.Fatalf("bad fetch for %q (%q): want %q, got %q", tt.path, s.resources, tt.val, attrs) + } + } + } +} + +func TestFetchAttribute(t *testing.T) { + for _, s := range []struct { + resources map[string]string + err error + tests []struct { + path string + val string + } + }{ + { + resources: map[string]string{ + "/": "a\nb\nc/", + "/c/": "d\ne/", + "/c/e/": "f", + "/a": "1", + "/b": "2", + "/c/d": "3", + "/c/e/f": "4", + }, + tests: []struct { + path string + val string + }{ + {"/a", "1"}, + {"/b", "2"}, + {"/c/d", "3"}, + {"/c/e/f", "4"}, + }, + }, + { + err: pkg.ErrNotFound{fmt.Errorf("test error")}, + tests: []struct { + path string + val string + }{ + {"", ""}, + }, + }, + } { + client := &testHttpClient{s.resources, s.err} + for _, tt := range s.tests { + attr, err := fetchAttribute(client, tt.path) + if err != s.err { + t.Fatalf("bad error for %q (%q): want %q, got %q", tt.path, s.resources, s.err, err) + } + if attr != tt.val { + t.Fatalf("bad fetch for %q (%q): want %q, got %q", tt.path, s.resources, tt.val, attr) + } + } + } +} + +func TestFetchMetadata(t *testing.T) { + for _, tt := range []struct { + root string + resources map[string]string + expect []byte + clientErr error + expectErr error + }{ + { + root: "/", + resources: map[string]string{ + "/2009-04-04/meta-data/public-keys": "bad\n", + }, + expectErr: fmt.Errorf("malformed public key: \"bad\""), + }, + { + root: "/", + resources: map[string]string{ + "/2009-04-04/meta-data/hostname": "host", + "/2009-04-04/meta-data/local-ipv4": "1.2.3.4", + "/2009-04-04/meta-data/public-ipv4": "5.6.7.8", + "/2009-04-04/meta-data/public-keys": "0=test1\n", + "/2009-04-04/meta-data/public-keys/0": "openssh-key", + "/2009-04-04/meta-data/public-keys/0/openssh-key": "key", + "/2009-04-04/meta-data/network_config/content_path": "path", + }, + expect: []byte(`{"hostname":"host","local-ipv4":"1.2.3.4","network_config":{"content_path":"path"},"public-ipv4":"5.6.7.8","public_keys":{"test1":"key"}}`), + }, + { + clientErr: pkg.ErrTimeout{fmt.Errorf("test error")}, + expectErr: pkg.ErrTimeout{fmt.Errorf("test error")}, + }, + } { + service := &metadataService{tt.root, &testHttpClient{tt.resources, tt.clientErr}} + metadata, err := service.FetchMetadata() + if Error(err) != Error(tt.expectErr) { + t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err) + } + if !bytes.Equal(metadata, tt.expect) { + t.Fatalf("bad fetch (%q): want %q, got %q", tt.resources, tt.expect, metadata) + } + } +} + +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/metadata_service.go b/datasource/metadata/metadata_service.go deleted file mode 100644 index 7f08a85..0000000 --- a/datasource/metadata/metadata_service.go +++ /dev/null @@ -1,151 +0,0 @@ -package metadata - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "strings" - - "github.com/coreos/coreos-cloudinit/pkg" -) - -// metadataService retrieves metadata from either an OpenStack[1] (2012-08-10) -// or EC2[2] (2009-04-04) compatible endpoint. It will first attempt to -// directly retrieve a JSON blob from the OpenStack endpoint. If that fails -// with a 404, it then attempts to retrieve metadata bit-by-bit from the EC2 -// endpoint, and populates that into an equivalent JSON blob. metadataService -// also checks for userdata from EC2 and, if that fails with a 404, OpenStack. -// -// [1] http://docs.openstack.org/grizzly/openstack-compute/admin/content/metadata-service.html -// [2] http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html#instancedata-data-categories - -const ( - Ec2ApiVersion = "2009-04-04" - OpenstackApiVersion = "2012-08-10" - BaseUrl = "http://169.254.169.254/" - Ec2UserdataUrl = BaseUrl + Ec2ApiVersion + "/user-data" - Ec2MetadataUrl = BaseUrl + Ec2ApiVersion + "/meta-data" - OpenstackUserdataUrl = BaseUrl + "openstack/" + OpenstackApiVersion + "/user_data" -) - -type metadataService struct{} - -func NewDatasource() *metadataService { - return &metadataService{} -} - -func (ms *metadataService) IsAvailable() bool { - client := pkg.NewHttpClient() - _, err := client.Get(BaseUrl) - return (err == nil) -} - -func (ms *metadataService) AvailabilityChanges() bool { - return true -} - -func (ms *metadataService) ConfigRoot() string { - return "" -} - -func (ms *metadataService) FetchMetadata() ([]byte, error) { - return fetchMetadata(pkg.NewHttpClient()) -} - -func (ms *metadataService) FetchUserdata() ([]byte, error) { - client := pkg.NewHttpClient() - if data, err := client.GetRetry(Ec2UserdataUrl); err == nil { - return data, err - } else if _, ok := err.(pkg.ErrTimeout); ok { - return data, err - } - - if data, err := client.GetRetry(OpenstackUserdataUrl); 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 "metadata-service" -} - -func fetchMetadata(client pkg.Getter) ([]byte, error) { - attrs := make(map[string]interface{}) - if keynames, err := fetchAttributes(client, fmt.Sprintf("%s/public-keys", Ec2MetadataUrl)); err == nil { - keyIDs := make(map[string]string) - for _, keyname := range keynames { - tokens := strings.SplitN(keyname, "=", 2) - if len(tokens) != 2 { - return nil, fmt.Errorf("malformed public key: %q\n", keyname) - } - keyIDs[tokens[1]] = tokens[0] - } - - keys := make(map[string]string) - for name, id := range keyIDs { - sshkey, err := fetchAttribute(client, fmt.Sprintf("%s/public-keys/%s/openssh-key", Ec2MetadataUrl, id)) - if err != nil { - return nil, err - } - keys[name] = sshkey - fmt.Printf("Found SSH key for %q\n", name) - } - attrs["public_keys"] = keys - } else if _, ok := err.(pkg.ErrNotFound); !ok { - return nil, err - } - - if hostname, err := fetchAttribute(client, fmt.Sprintf("%s/hostname", Ec2MetadataUrl)); err == nil { - attrs["hostname"] = hostname - } else if _, ok := err.(pkg.ErrNotFound); !ok { - return nil, err - } - - if localAddr, err := fetchAttribute(client, fmt.Sprintf("%s/local-ipv4", Ec2MetadataUrl)); err == nil { - attrs["local-ipv4"] = localAddr - } else if _, ok := err.(pkg.ErrNotFound); !ok { - return nil, err - } - - if publicAddr, err := fetchAttribute(client, fmt.Sprintf("%s/public-ipv4", Ec2MetadataUrl)); err == nil { - attrs["public-ipv4"] = publicAddr - } else if _, ok := err.(pkg.ErrNotFound); !ok { - return nil, err - } - - if content_path, err := fetchAttribute(client, fmt.Sprintf("%s/network_config/content_path", Ec2MetadataUrl)); err == nil { - attrs["network_config"] = map[string]string{ - "content_path": content_path, - } - } else if _, ok := err.(pkg.ErrNotFound); !ok { - return nil, err - } - - return json.Marshal(attrs) -} - -func fetchAttributes(client pkg.Getter, url string) ([]string, error) { - resp, err := client.GetRetry(url) - if err != nil { - return nil, err - } - scanner := bufio.NewScanner(bytes.NewBuffer(resp)) - data := make([]string, 0) - for scanner.Scan() { - data = append(data, scanner.Text()) - } - return data, scanner.Err() -} - -func fetchAttribute(client pkg.Getter, url string) (string, error) { - if attrs, err := fetchAttributes(client, url); err == nil && len(attrs) > 0 { - return attrs[0], nil - } else { - return "", err - } -} diff --git a/datasource/metadata/metadata_service_test.go b/datasource/metadata/metadata_service_test.go deleted file mode 100644 index e1d8f96..0000000 --- a/datasource/metadata/metadata_service_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package metadata - -import ( - "bytes" - "fmt" - "reflect" - "testing" - - "github.com/coreos/coreos-cloudinit/pkg" -) - -type TestHttpClient struct { - metadata 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.metadata[url]; ok { - return []byte(val), nil - } else { - return nil, pkg.ErrNotFound{fmt.Errorf("not found: %q", url)} - } -} - -func TestMSFetchAttributes(t *testing.T) { - for _, s := range []struct { - metadata map[string]string - err error - tests []struct { - path string - val []string - } - }{ - { - metadata: map[string]string{ - "/": "a\nb\nc/", - "/c/": "d\ne/", - "/c/e/": "f", - "/a": "1", - "/b": "2", - "/c/d": "3", - "/c/e/f": "4", - }, - tests: []struct { - path string - val []string - }{ - {"/", []string{"a", "b", "c/"}}, - {"/b", []string{"2"}}, - {"/c/d", []string{"3"}}, - {"/c/e/", []string{"f"}}, - }, - }, - { - err: pkg.ErrNotFound{fmt.Errorf("test error")}, - tests: []struct { - path string - val []string - }{ - {"", nil}, - }, - }, - } { - client := &TestHttpClient{s.metadata, s.err} - for _, tt := range s.tests { - attrs, err := fetchAttributes(client, tt.path) - if err != s.err { - t.Fatalf("bad error for %q (%q): want %q, got %q", tt.path, s.metadata, s.err, err) - } - if !reflect.DeepEqual(attrs, tt.val) { - t.Fatalf("bad fetch for %q (%q): want %q, got %q", tt.path, s.metadata, tt.val, attrs) - } - } - } -} - -func TestMSFetchAttribute(t *testing.T) { - for _, s := range []struct { - metadata map[string]string - err error - tests []struct { - path string - val string - } - }{ - { - metadata: map[string]string{ - "/": "a\nb\nc/", - "/c/": "d\ne/", - "/c/e/": "f", - "/a": "1", - "/b": "2", - "/c/d": "3", - "/c/e/f": "4", - }, - tests: []struct { - path string - val string - }{ - {"/a", "1"}, - {"/b", "2"}, - {"/c/d", "3"}, - {"/c/e/f", "4"}, - }, - }, - { - err: pkg.ErrNotFound{fmt.Errorf("test error")}, - tests: []struct { - path string - val string - }{ - {"", ""}, - }, - }, - } { - client := &TestHttpClient{s.metadata, s.err} - for _, tt := range s.tests { - attr, err := fetchAttribute(client, tt.path) - if err != s.err { - t.Fatalf("bad error for %q (%q): want %q, got %q", tt.path, s.metadata, s.err, err) - } - if attr != tt.val { - t.Fatalf("bad fetch for %q (%q): want %q, got %q", tt.path, s.metadata, tt.val, attr) - } - } - } -} - -func TestMSFetchMetadata(t *testing.T) { - for _, tt := range []struct { - metadata map[string]string - err error - expect []byte - }{ - { - metadata: map[string]string{ - "http://169.254.169.254/2009-04-04/meta-data/hostname": "host", - "http://169.254.169.254/2009-04-04/meta-data/public-keys": "0=test1\n", - "http://169.254.169.254/2009-04-04/meta-data/public-keys/0": "openssh-key", - "http://169.254.169.254/2009-04-04/meta-data/public-keys/0/openssh-key": "key", - "http://169.254.169.254/2009-04-04/meta-data/network_config/content_path": "path", - }, - expect: []byte(`{"hostname":"host","network_config":{"content_path":"path"},"public_keys":{"test1":"key"}}`), - }, - {err: pkg.ErrTimeout{fmt.Errorf("test error")}}, - } { - client := &TestHttpClient{tt.metadata, tt.err} - metadata, err := fetchMetadata(client) - if err != tt.err { - t.Fatalf("bad error (%q): want %q, got %q", tt.metadata, tt.err, err) - } - if !bytes.Equal(metadata, tt.expect) { - t.Fatalf("bad fetch (%q): want %q, got %q", tt.metadata, tt.expect, metadata) - } - } -} diff --git a/pkg/http_client.go b/pkg/http_client.go index 7135798..679a536 100644 --- a/pkg/http_client.go +++ b/pkg/http_client.go @@ -58,6 +58,7 @@ type HttpClient struct { } type Getter interface { + Get(string) ([]byte, error) GetRetry(string) ([]byte, error) } diff --git a/test b/test index 77469ce..8f7c353 100755 --- a/test +++ b/test @@ -18,7 +18,7 @@ declare -a TESTPKGS=(initialize datasource datasource/configdrive datasource/file - datasource/metadata + datasource/metadata/ec2 datasource/proc_cmdline datasource/url pkg