Merge pull request #188 from crawford/configdrive

configdrive: Use the EC2 metadata over OpenStack
This commit is contained in:
Alex Crawford 2014-08-04 11:12:06 -07:00
commit 38b3e1213a
5 changed files with 146 additions and 13 deletions

View File

@ -7,11 +7,12 @@ import (
) )
type configDrive struct { type configDrive struct {
root string root string
readFile func(filename string) ([]byte, error)
} }
func NewConfigDrive(root string) *configDrive { func NewConfigDrive(root string) *configDrive {
return &configDrive{path.Join(root, "openstack")} return &configDrive{root, ioutil.ReadFile}
} }
func (cd *configDrive) IsAvailable() bool { func (cd *configDrive) IsAvailable() bool {
@ -24,23 +25,38 @@ func (cd *configDrive) AvailabilityChanges() bool {
} }
func (cd *configDrive) ConfigRoot() string { 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) { 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) { 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 { func (cd *configDrive) Type() string {
return "cloud-drive" return "cloud-drive"
} }
func (cd *configDrive) readFile(filename string) ([]byte, error) { func (cd *configDrive) ec2Root() string {
data, err := ioutil.ReadFile(path.Join(cd.root, "latest", filename)) 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) { if os.IsNotExist(err) {
err = nil err = nil
} }

View File

@ -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)
}
}
}

View File

@ -1,5 +1,10 @@
package datasource package datasource
const (
Ec2ApiVersion = "2009-04-04"
OpenstackApiVersion = "2012-08-10"
)
type Datasource interface { type Datasource interface {
IsAvailable() bool IsAvailable() bool
AvailabilityChanges() bool AvailabilityChanges() bool

View File

@ -22,11 +22,9 @@ import (
const ( const (
BaseUrl = "http://169.254.169.254/" BaseUrl = "http://169.254.169.254/"
Ec2ApiVersion = "2009-04-04"
Ec2UserdataUrl = BaseUrl + Ec2ApiVersion + "/user-data" Ec2UserdataUrl = BaseUrl + Ec2ApiVersion + "/user-data"
Ec2MetadataUrl = BaseUrl + Ec2ApiVersion + "/meta-data" Ec2MetadataUrl = BaseUrl + Ec2ApiVersion + "/meta-data"
OpenstackApiVersion = "openstack/2012-08-10" OpenstackUserdataUrl = BaseUrl + "openstack/" + OpenstackApiVersion + "/user_data"
OpenstackUserdataUrl = BaseUrl + OpenstackApiVersion + "/user_data"
) )
type metadataService struct{} type metadataService struct{}

View File

@ -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 { for _, s := range []struct {
metadata map[string]string metadata map[string]string
err error 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 { for _, s := range []struct {
metadata map[string]string metadata map[string]string
err error 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 { for _, tt := range []struct {
metadata map[string]string metadata map[string]string
err error err error