diff --git a/datasource/configdrive.go b/datasource/configdrive.go index 3b008f4..4841999 100644 --- a/datasource/configdrive.go +++ b/datasource/configdrive.go @@ -7,11 +7,12 @@ import ( ) type configDrive struct { - root string + root string + readFile func(filename string) ([]byte, error) } func NewConfigDrive(root string) *configDrive { - return &configDrive{path.Join(root, "openstack")} + return &configDrive{root, ioutil.ReadFile} } func (cd *configDrive) IsAvailable() bool { @@ -24,23 +25,38 @@ func (cd *configDrive) AvailabilityChanges() bool { } func (cd *configDrive) ConfigRoot() string { - return cd.root + return cd.openstackRoot() } +// FetchMetadata attempts to retrieve metadata from ec2/2009-04-04/meta_data.json. func (cd *configDrive) FetchMetadata() ([]byte, error) { - return cd.readFile("meta_data.json") + return cd.tryReadFile(path.Join(cd.ec2Root(), "meta_data.json")) } +// FetchUserdata attempts to retrieve the userdata from ec2/2009-04-04/user_data. +// If no data is found, it will attempt to read from openstack/latest/user_data. func (cd *configDrive) FetchUserdata() ([]byte, error) { - return cd.readFile("user_data") + bytes, err := cd.tryReadFile(path.Join(cd.ec2Root(), "user_data")) + if bytes == nil && err == nil { + bytes, err = cd.tryReadFile(path.Join(cd.openstackRoot(), "user_data")) + } + return bytes, err } func (cd *configDrive) Type() string { return "cloud-drive" } -func (cd *configDrive) readFile(filename string) ([]byte, error) { - data, err := ioutil.ReadFile(path.Join(cd.root, "latest", filename)) +func (cd *configDrive) ec2Root() string { + return path.Join(cd.root, "ec2", Ec2ApiVersion) +} + +func (cd *configDrive) openstackRoot() string { + return path.Join(cd.root, "openstack", "latest") +} + +func (cd *configDrive) tryReadFile(filename string) ([]byte, error) { + data, err := cd.readFile(filename) if os.IsNotExist(err) { err = nil } diff --git a/datasource/configdrive_test.go b/datasource/configdrive_test.go new file mode 100644 index 0000000..a000d4a --- /dev/null +++ b/datasource/configdrive_test.go @@ -0,0 +1,114 @@ +package datasource + +import ( + "os" + "testing" +) + +type mockFilesystem []string + +func (m mockFilesystem) readFile(filename string) ([]byte, error) { + for _, file := range m { + if file == filename { + return []byte(filename), nil + } + } + return nil, os.ErrNotExist +} + +func TestCDFetchMetadata(t *testing.T) { + for _, tt := range []struct { + root string + filename string + files mockFilesystem + }{ + { + "/", + "", + mockFilesystem{}, + }, + { + "/", + "/ec2/2009-04-04/meta_data.json", + mockFilesystem([]string{"/ec2/2009-04-04/meta_data.json"}), + }, + { + "/media/configdrive", + "/media/configdrive/ec2/2009-04-04/meta_data.json", + mockFilesystem([]string{"/media/configdrive/ec2/2009-04-04/meta_data.json"}), + }, + } { + cd := configDrive{tt.root, tt.files.readFile} + filename, err := cd.FetchMetadata() + if err != nil { + t.Fatalf("bad error for %q: want %q, got %q", tt, nil, err) + } + if string(filename) != tt.filename { + t.Fatalf("bad path for %q: want %q, got %q", tt, tt.filename, filename) + } + } +} + +func TestCDFetchUserdata(t *testing.T) { + for _, tt := range []struct { + root string + filename string + files mockFilesystem + }{ + { + "/", + "", + mockFilesystem{}, + }, + { + "/", + "/ec2/2009-04-04/user_data", + mockFilesystem([]string{"/ec2/2009-04-04/user_data"}), + }, + { + "/", + "/openstack/latest/user_data", + mockFilesystem([]string{"/openstack/latest/user_data"}), + }, + { + "/", + "/ec2/2009-04-04/user_data", + mockFilesystem([]string{"/openstack/latest/user_data", "/ec2/2009-04-04/user_data"}), + }, + { + "/media/configdrive", + "/media/configdrive/ec2/2009-04-04/user_data", + mockFilesystem([]string{"/media/configdrive/ec2/2009-04-04/user_data"}), + }, + } { + cd := configDrive{tt.root, tt.files.readFile} + filename, err := cd.FetchUserdata() + if err != nil { + t.Fatalf("bad error for %q: want %q, got %q", tt, nil, err) + } + if string(filename) != tt.filename { + t.Fatalf("bad path for %q: want %q, got %q", tt, tt.filename, filename) + } + } +} + +func TestCDConfigRoot(t *testing.T) { + for _, tt := range []struct { + root string + configRoot string + }{ + { + "/", + "/openstack/latest", + }, + { + "/media/configdrive", + "/media/configdrive/openstack/latest", + }, + } { + cd := configDrive{tt.root, nil} + if configRoot := cd.ConfigRoot(); configRoot != tt.configRoot { + t.Fatalf("bad config root for %q: want %q, got %q", tt, tt.configRoot, configRoot) + } + } +} diff --git a/datasource/datasource.go b/datasource/datasource.go index 7146925..7de7f23 100644 --- a/datasource/datasource.go +++ b/datasource/datasource.go @@ -1,5 +1,10 @@ package datasource +const ( + Ec2ApiVersion = "2009-04-04" + OpenstackApiVersion = "2012-08-10" +) + type Datasource interface { IsAvailable() bool AvailabilityChanges() bool diff --git a/datasource/metadata_service.go b/datasource/metadata_service.go index 341f743..571b1a2 100644 --- a/datasource/metadata_service.go +++ b/datasource/metadata_service.go @@ -22,11 +22,9 @@ import ( const ( BaseUrl = "http://169.254.169.254/" - Ec2ApiVersion = "2009-04-04" Ec2UserdataUrl = BaseUrl + Ec2ApiVersion + "/user-data" Ec2MetadataUrl = BaseUrl + Ec2ApiVersion + "/meta-data" - OpenstackApiVersion = "openstack/2012-08-10" - OpenstackUserdataUrl = BaseUrl + OpenstackApiVersion + "/user_data" + OpenstackUserdataUrl = BaseUrl + "openstack/" + OpenstackApiVersion + "/user_data" ) type metadataService struct{} diff --git a/datasource/metadata_service_test.go b/datasource/metadata_service_test.go index 3d18533..5867d42 100644 --- a/datasource/metadata_service_test.go +++ b/datasource/metadata_service_test.go @@ -25,7 +25,7 @@ func (t *TestHttpClient) GetRetry(url string) ([]byte, error) { } } -func TestFetchAttributes(t *testing.T) { +func TestMSFetchAttributes(t *testing.T) { for _, s := range []struct { metadata map[string]string err error @@ -77,7 +77,7 @@ func TestFetchAttributes(t *testing.T) { } } -func TestFetchAttribute(t *testing.T) { +func TestMSFetchAttribute(t *testing.T) { for _, s := range []struct { metadata map[string]string err error @@ -129,7 +129,7 @@ func TestFetchAttribute(t *testing.T) { } } -func TestFetchMetadata(t *testing.T) { +func TestMSFetchMetadata(t *testing.T) { for _, tt := range []struct { metadata map[string]string err error