From 47ac4f69315969cdf0fee81da81d6c6928e86886 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 13 Feb 2015 21:01:44 -0800 Subject: [PATCH 1/2] test: add directory support to MockFilesystem --- datasource/configdrive/configdrive_test.go | 27 +++-- datasource/test/filesystem.go | 57 ++++++++++ datasource/test/filesystem_test.go | 115 +++++++++++++++++++++ datasource/test/test.go | 28 ----- datasource/waagent/waagent_test.go | 22 ++-- test | 1 + 6 files changed, 197 insertions(+), 53 deletions(-) create mode 100644 datasource/test/filesystem.go create mode 100644 datasource/test/filesystem_test.go delete mode 100644 datasource/test/test.go diff --git a/datasource/configdrive/configdrive_test.go b/datasource/configdrive/configdrive_test.go index d384890..e40a8fd 100644 --- a/datasource/configdrive/configdrive_test.go +++ b/datasource/configdrive/configdrive_test.go @@ -31,23 +31,22 @@ func TestFetchMetadata(t *testing.T) { }{ { root: "/", - files: test.MockFilesystem{"/openstack/latest/meta_data.json": ""}, + files: test.NewMockFilesystem(test.File{Path: "/openstack/latest/meta_data.json", Contents: ""}), }, { root: "/", - files: test.MockFilesystem{"/openstack/latest/meta_data.json": `{"ignore": "me"}`}, + files: test.NewMockFilesystem(test.File{Path: "/openstack/latest/meta_data.json", Contents: `{"ignore": "me"}`}), }, { root: "/", - files: test.MockFilesystem{"/openstack/latest/meta_data.json": `{"hostname": "host"}`}, + files: test.NewMockFilesystem(test.File{Path: "/openstack/latest/meta_data.json", Contents: `{"hostname": "host"}`}), metadata: datasource.Metadata{Hostname: "host"}, }, { root: "/media/configdrive", - files: test.MockFilesystem{ - "/media/configdrive/openstack/latest/meta_data.json": `{"hostname": "host", "network_config": {"content_path": "config_file.json"}, "public_keys":{"1": "key1", "2": "key2"}}`, - "/media/configdrive/openstack/config_file.json": "make it work", - }, + files: test.NewMockFilesystem(test.File{Path: "/media/configdrive/openstack/latest/meta_data.json", Contents: `{"hostname": "host", "network_config": {"content_path": "config_file.json"}, "public_keys":{"1": "key1", "2": "key2"}}`}, + test.File{Path: "/media/configdrive/openstack/config_file.json", Contents: "make it work"}, + ), metadata: datasource.Metadata{ Hostname: "host", NetworkConfig: []byte("make it work"), @@ -61,10 +60,10 @@ func TestFetchMetadata(t *testing.T) { cd := configDrive{tt.root, tt.files.ReadFile} metadata, err := cd.FetchMetadata() if err != nil { - t.Fatalf("bad error for %q: want %v, got %q", tt, nil, err) + t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) } if !reflect.DeepEqual(tt.metadata, metadata) { - t.Fatalf("bad metadata for %q: want %#v, got %#v", tt, tt.metadata, metadata) + t.Fatalf("bad metadata for %+v: want %#v, got %#v", tt, tt.metadata, metadata) } } } @@ -78,27 +77,27 @@ func TestFetchUserdata(t *testing.T) { }{ { "/", - test.MockFilesystem{}, + test.NewMockFilesystem(), "", }, { "/", - test.MockFilesystem{"/openstack/latest/user_data": "userdata"}, + test.NewMockFilesystem(test.File{Path: "/openstack/latest/user_data", Contents: "userdata"}), "userdata", }, { "/media/configdrive", - test.MockFilesystem{"/media/configdrive/openstack/latest/user_data": "userdata"}, + test.NewMockFilesystem(test.File{Path: "/media/configdrive/openstack/latest/user_data", Contents: "userdata"}), "userdata", }, } { cd := configDrive{tt.root, tt.files.ReadFile} userdata, err := cd.FetchUserdata() if err != nil { - t.Fatalf("bad error for %q: want %v, got %q", tt, nil, err) + t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) } if string(userdata) != tt.userdata { - t.Fatalf("bad userdata for %q: want %q, got %q", tt, tt.userdata, userdata) + t.Fatalf("bad userdata for %+v: want %q, got %q", tt, tt.userdata, userdata) } } } diff --git a/datasource/test/filesystem.go b/datasource/test/filesystem.go new file mode 100644 index 0000000..95d6fb2 --- /dev/null +++ b/datasource/test/filesystem.go @@ -0,0 +1,57 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "fmt" + "os" + "path" +) + +type MockFilesystem map[string]File + +type File struct { + Path string + Contents string + Directory bool +} + +func (m MockFilesystem) ReadFile(filename string) ([]byte, error) { + if f, ok := m[path.Clean(filename)]; ok { + if f.Directory { + return nil, fmt.Errorf("read %s: is a directory", filename) + } + return []byte(f.Contents), nil + } + return nil, os.ErrNotExist +} + +func NewMockFilesystem(files ...File) MockFilesystem { + fs := MockFilesystem{} + for _, file := range files { + fs[file.Path] = file + + // Create the directories leading up to the file + p := path.Dir(file.Path) + for p != "/" && p != "." { + if f, ok := fs[p]; ok && !f.Directory { + panic(fmt.Sprintf("%q already exists and is not a directory (%#v)", p, f)) + } + fs[p] = File{Path: p, Directory: true} + p = path.Dir(p) + } + } + return fs +} diff --git a/datasource/test/filesystem_test.go b/datasource/test/filesystem_test.go new file mode 100644 index 0000000..547c51f --- /dev/null +++ b/datasource/test/filesystem_test.go @@ -0,0 +1,115 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "errors" + "os" + "reflect" + "testing" +) + +func TestReadFile(t *testing.T) { + tests := []struct { + filesystem MockFilesystem + + filename string + contents string + err error + }{ + { + filename: "dne", + err: os.ErrNotExist, + }, + { + filesystem: MockFilesystem{ + "exists": File{Contents: "hi"}, + }, + filename: "exists", + contents: "hi", + }, + { + filesystem: MockFilesystem{ + "dir": File{Directory: true}, + }, + filename: "dir", + err: errors.New("read dir: is a directory"), + }, + } + + for i, tt := range tests { + contents, err := tt.filesystem.ReadFile(tt.filename) + if tt.contents != string(contents) { + t.Errorf("bad contents (test %d): want %q, got %q", i, tt.contents, string(contents)) + } + if !reflect.DeepEqual(tt.err, err) { + t.Errorf("bad error (test %d): want %v, got %v", i, tt.err, err) + } + } +} + +func TestNewMockFilesystem(t *testing.T) { + tests := []struct { + files []File + + filesystem MockFilesystem + }{ + { + filesystem: MockFilesystem{}, + }, + { + files: []File{File{Path: "file"}}, + filesystem: MockFilesystem{ + "file": File{Path: "file"}, + }, + }, + { + files: []File{File{Path: "/file"}}, + filesystem: MockFilesystem{ + "/file": File{Path: "/file"}, + }, + }, + { + files: []File{File{Path: "/dir/file"}}, + filesystem: MockFilesystem{ + "/dir": File{Path: "/dir", Directory: true}, + "/dir/file": File{Path: "/dir/file"}, + }, + }, + { + files: []File{File{Path: "/dir/dir/file"}}, + filesystem: MockFilesystem{ + "/dir": File{Path: "/dir", Directory: true}, + "/dir/dir": File{Path: "/dir/dir", Directory: true}, + "/dir/dir/file": File{Path: "/dir/dir/file"}, + }, + }, + { + files: []File{File{Path: "/dir/dir/dir", Directory: true}}, + filesystem: MockFilesystem{ + "/dir": File{Path: "/dir", Directory: true}, + "/dir/dir": File{Path: "/dir/dir", Directory: true}, + "/dir/dir/dir": File{Path: "/dir/dir/dir", Directory: true}, + }, + }, + } + + for i, tt := range tests { + filesystem := NewMockFilesystem(tt.files...) + if !reflect.DeepEqual(tt.filesystem, filesystem) { + t.Errorf("bad filesystem (test %d): want %#v, got %#v", i, tt.filesystem, filesystem) + } + } +} diff --git a/datasource/test/test.go b/datasource/test/test.go deleted file mode 100644 index 84696e2..0000000 --- a/datasource/test/test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "os" -) - -type MockFilesystem map[string]string - -func (m MockFilesystem) ReadFile(filename string) ([]byte, error) { - if contents, ok := m[filename]; ok { - return []byte(contents), nil - } - return nil, os.ErrNotExist -} diff --git a/datasource/waagent/waagent_test.go b/datasource/waagent/waagent_test.go index 1036b03..f60a4c2 100644 --- a/datasource/waagent/waagent_test.go +++ b/datasource/waagent/waagent_test.go @@ -31,19 +31,19 @@ func TestFetchMetadata(t *testing.T) { }{ { root: "/", - files: test.MockFilesystem{}, + files: test.NewMockFilesystem(), }, { root: "/", - files: test.MockFilesystem{"/SharedConfig.xml": ""}, + files: test.NewMockFilesystem(test.File{Path: "/SharedConfig.xml", Contents: ""}), }, { root: "/var/lib/waagent", - files: test.MockFilesystem{"/var/lib/waagent/SharedConfig.xml": ""}, + files: test.NewMockFilesystem(test.File{Path: "/var/lib/waagent/SharedConfig.xml", Contents: ""}), }, { root: "/var/lib/waagent", - files: test.MockFilesystem{"/var/lib/waagent/SharedConfig.xml": ` + files: test.NewMockFilesystem(test.File{Path: "/var/lib/waagent/SharedConfig.xml", Contents: ` @@ -79,7 +79,7 @@ func TestFetchMetadata(t *testing.T) { -`}, +`}), metadata: datasource.Metadata{ PrivateIPv4: net.ParseIP("100.73.202.64"), PublicIPv4: net.ParseIP("191.239.39.77"), @@ -89,10 +89,10 @@ func TestFetchMetadata(t *testing.T) { a := waagent{tt.root, tt.files.ReadFile} metadata, err := a.FetchMetadata() if err != nil { - t.Fatalf("bad error for %q: want %v, got %q", tt, nil, err) + t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) } if !reflect.DeepEqual(tt.metadata, metadata) { - t.Fatalf("bad metadata for %q: want %#v, got %#v", tt, tt.metadata, metadata) + t.Fatalf("bad metadata for %+v: want %#v, got %#v", tt, tt.metadata, metadata) } } } @@ -104,21 +104,21 @@ func TestFetchUserdata(t *testing.T) { }{ { "/", - test.MockFilesystem{}, + test.NewMockFilesystem(), }, { "/", - test.MockFilesystem{"/CustomData": ""}, + test.NewMockFilesystem(test.File{Path: "/CustomData", Contents: ""}), }, { "/var/lib/waagent/", - test.MockFilesystem{"/var/lib/waagent/CustomData": ""}, + test.NewMockFilesystem(test.File{Path: "/var/lib/waagent/CustomData", Contents: ""}), }, } { a := waagent{tt.root, tt.files.ReadFile} _, err := a.FetchUserdata() if err != nil { - t.Fatalf("bad error for %q: want %v, got %q", tt, nil, err) + t.Fatalf("bad error for %+v: want %v, got %q", tt, nil, err) } } } diff --git a/test b/test index b9de140..a24bb31 100755 --- a/test +++ b/test @@ -24,6 +24,7 @@ declare -a TESTPKGS=( datasource/metadata/digitalocean datasource/metadata/ec2 datasource/proc_cmdline + datasource/test datasource/url datasource/waagent initialize From 987aa218839d557b27675953b46b1dcc387b04b0 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 13 Feb 2015 22:28:09 -0800 Subject: [PATCH 2/2] configdrive: check the network config path Check to make sure that a network config path has been specified before trying to read from it. Otherwise, it will end up trying to read a directory. --- datasource/configdrive/configdrive.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datasource/configdrive/configdrive.go b/datasource/configdrive/configdrive.go index bbadf64..ed4c7c7 100644 --- a/datasource/configdrive/configdrive.go +++ b/datasource/configdrive/configdrive.go @@ -69,7 +69,9 @@ func (cd *configDrive) FetchMetadata() (metadata datasource.Metadata, err error) metadata.SSHPublicKeys = m.SSHAuthorizedKeyMap metadata.Hostname = m.Hostname - metadata.NetworkConfig, err = cd.tryReadFile(path.Join(cd.openstackRoot(), m.NetworkConfig.ContentPath)) + if m.NetworkConfig.ContentPath != "" { + metadata.NetworkConfig, err = cd.tryReadFile(path.Join(cd.openstackRoot(), m.NetworkConfig.ContentPath)) + } return }