From e77cb421713a652ff5936e05469814d408b010b4 Mon Sep 17 00:00:00 2001 From: sophia Date: Thu, 11 Nov 2021 17:56:26 -0600 Subject: [PATCH] Add tests --- internal/core/box.go | 15 +- internal/core/box_collection.go | 84 ++++++++-- internal/core/box_collection_test.go | 155 +++++++++++++++++++ internal/core/box_test.go | 21 +-- internal/server/singleprocess/service_box.go | 4 +- 5 files changed, 247 insertions(+), 32 deletions(-) create mode 100644 internal/core/box_collection_test.go diff --git a/internal/core/box.go b/internal/core/box.go index 8b7e856d0..bc26657d0 100644 --- a/internal/core/box.go +++ b/internal/core/box.go @@ -3,6 +3,7 @@ package core import ( "archive/tar" "context" + "encoding/json" "errors" "io" "io/ioutil" @@ -62,13 +63,12 @@ func NewBox(opts ...BoxOption) (b *Box, err error) { if err != nil { return nil, err } - metadata, err := LoadBoxMetadata(data) - if err != nil { + if err := json.Unmarshal(data, &b.box.Metadata); err != nil { return nil, err } - mapstructure.Decode(metadata, &b.box.Metadata) + // The metadata should have provider info + b.box.Provider = b.box.Metadata["provider"] b.box.Id = b.box.Name + "-" + b.box.Version + "-" + b.box.Provider - b.Save() return } @@ -198,7 +198,10 @@ func (b *Box) AutomaticUpdateCheckAllowed() (allowed bool, err error) { // This deletes the box. This is NOT undoable. func (b *Box) Destroy() (err error) { - return os.RemoveAll(b.box.Directory) + if _, err := os.Stat(b.box.Directory); err != nil { + return os.RemoveAll(b.box.Directory) + } + return } func (b *Box) Directory() (path string, err error) { @@ -297,6 +300,7 @@ func (b *Box) Repackage(outputPath string) (err error) { if err != nil { return err } + header.Name = filepath.ToSlash(path) if err := tw.WriteHeader(header); err != nil { return err } @@ -305,6 +309,7 @@ func (b *Box) Repackage(outputPath string) (err error) { if err != nil { return err } + defer data.Close() if _, err := io.Copy(tw, data); err != nil { return err } diff --git a/internal/core/box_collection.go b/internal/core/box_collection.go index 8bc775a69..8eb15a3ea 100644 --- a/internal/core/box_collection.go +++ b/internal/core/box_collection.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" "strings" - "sync" "github.com/hashicorp/go-hclog" "github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk" @@ -24,7 +23,6 @@ const ( type BoxCollection struct { basis *Basis directory string - m sync.Mutex logger hclog.Logger } @@ -46,8 +44,10 @@ func (b *BoxCollection) Add(path, name, version string, force bool, providers .. if exists != nil && !force { return nil, fmt.Errorf("Box already exits, can't add %s v%s", name, version) } else { - // If the box already exists but force is enabled, then delete the box - exists.Destroy() + if exists != nil { + // If the box already exists but force is enabled, then delete the box + exists.Destroy() + } } tempDir := b.basis.dir.TempDir().String() @@ -61,9 +61,15 @@ func (b *BoxCollection) Add(path, name, version string, force bool, providers .. reader := tar.NewReader(boxFile) for { header, err := reader.Next() + if err == io.EOF { + break + } if err != nil { return nil, err } + if header == nil { + continue + } dest := filepath.Join(tempDir, header.Name) switch header.Typeflag { case tar.TypeDir: @@ -75,6 +81,12 @@ func (b *BoxCollection) Add(path, name, version string, force bool, providers .. } } case tar.TypeReg: + if _, err := os.Stat(filepath.Dir(dest)); err != nil { + err = os.MkdirAll(filepath.Dir(dest), 0755) + if err != nil { + return nil, err + } + } // copy the file f, err := os.OpenFile(dest, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) if err != nil { @@ -95,25 +107,65 @@ func (b *BoxCollection) Add(path, name, version string, force bool, providers .. Directory: tempDir, }), ) - newBox.Save() if err != nil { return nil, err } provider := newBox.box.Provider - foundProvider := false - for _, p := range providers { - if p == provider { - foundProvider = true - break + + if providers != nil { + foundProvider := false + for _, p := range providers { + if p == provider { + foundProvider = true + break + } + } + if !foundProvider { + return nil, fmt.Errorf("could not add box %s, provider '%s' does not match the expected providers %s", path, provider, providers) } } - if !foundProvider { - return nil, fmt.Errorf("could not add box %s, provider '%s' does not match the expected providers %s", path, provider, providers) - } - destDir := b.generateDirectoryName(filepath.Join(b.directory, name, version, provider)) + destDir := filepath.Join(b.directory, b.generateDirectoryName(name), version, provider) b.logger.Debug("Box directory: %s", destDir) - os.Rename(tempDir, destDir) + os.MkdirAll(destDir, 0755) + // Copy the contents of the tempdir to the final dir + err = filepath.Walk(tempDir, func(path string, info os.FileInfo, erro error) (err error) { + // TODO: only copy in the files that were extracted into the tempdir + destPath := filepath.Join(destDir, info.Name()) + if info.IsDir() { + err = os.MkdirAll(destPath, info.Mode()) + return err + } else { + data, err := os.Open(path) + if err != nil { + return err + } + defer data.Close() + dest, err := os.Create(destPath) + if err != nil { + return err + } + defer dest.Close() + if err != nil { + return err + } + if _, err := io.Copy(dest, data); err != nil { + return err + } + } + return + }) + + newBox, err = NewBox( + BoxWithBasis(b.basis), + BoxWithBox(&vagrant_server.Box{ + Name: name, + Version: version, + Directory: destDir, + Provider: provider, + }), + ) + newBox.Save() return newBox, nil } @@ -156,7 +208,7 @@ func (b *BoxCollection) Find(name, version string, providers ...string) (box *Bo if err != nil { return nil, err } - if resp != nil { + if resp.Box != nil { // Return the first box that is found return NewBox( BoxWithBasis(b.basis), diff --git a/internal/core/box_collection_test.go b/internal/core/box_collection_test.go new file mode 100644 index 000000000..4b34e0787 --- /dev/null +++ b/internal/core/box_collection_test.go @@ -0,0 +1,155 @@ +package core + +import ( + "archive/tar" + "context" + "io" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vagrant/internal/plugin" + "github.com/stretchr/testify/require" +) + +func seedDB(t *testing.T, basis *Basis) { + box1 := newFullBox(t, hashicorpBionicBoxData(), basis) + box1.Save() + box2 := newFullBox(t, testboxBoxData(), basis) + box2.Save() +} + +func newBoxCollection(t *testing.T) *BoxCollection { + pluginManager := plugin.NewManager( + context.Background(), + hclog.New(&hclog.LoggerOptions{}), + ) + basis := TestBasis(t, WithPluginManager(pluginManager)) + seedDB(t, basis) + td, err := ioutil.TempDir(basis.dir.DataDir().String(), "boxes") + t.Cleanup(func() { os.RemoveAll(td) }) + require.NoError(t, err) + return &BoxCollection{ + basis: basis, + directory: td, + logger: hclog.New(&hclog.LoggerOptions{}), + } +} + +func generateTestBox(t *testing.T, path string, basis *Basis) string { + metafile := filepath.Join(path, "box", "metadata.json") + os.Mkdir(filepath.Dir(metafile), 0755) + data := []byte("{\"provider\":\"virtualbox\"}") + err := os.WriteFile(metafile, data, 0644) + require.NoError(t, err) + outputPath := filepath.Join(path, "output", "box") + os.Mkdir(filepath.Dir(outputPath), 0755) + + tarFile, err := os.Create(outputPath) + require.NoError(t, err) + defer tarFile.Close() + tw := tar.NewWriter(tarFile) + defer tw.Close() + file, err := os.Open(metafile) + require.NoError(t, err) + defer file.Close() + info, err := file.Stat() + require.NoError(t, err) + header, err := tar.FileInfoHeader(info, info.Name()) + require.NoError(t, err) + err = tw.WriteHeader(header) + require.NoError(t, err) + _, err = io.Copy(tw, file) + require.NoError(t, err) + + return outputPath +} + +func TestAddErrors(t *testing.T) { + bc := newBoxCollection(t) + + td, err := ioutil.TempDir("/tmp", "box") + require.NoError(t, err) + t.Cleanup(func() { os.RemoveAll(td) }) + + _, err = bc.Add("/path/that/doesntexist", "test", "1.2.3", true) + require.Error(t, err) + + _, err = bc.Add(td, "test/box", "1.2.3", false) + require.Error(t, err) +} + +func TestAddNoProviders(t *testing.T) { + bc := newBoxCollection(t) + + td, err := ioutil.TempDir("/tmp", "box") + require.NoError(t, err) + t.Cleanup(func() { os.RemoveAll(td) }) + + testBoxPath := generateTestBox(t, td, bc.basis) + box, err := bc.Add(testBoxPath, "test/box", "1.2.3", true) + require.NoError(t, err) + require.NotNil(t, box) +} + +func TestAddWithProviders(t *testing.T) { + bc := newBoxCollection(t) + + td, err := ioutil.TempDir("/tmp", "box") + require.NoError(t, err) + t.Cleanup(func() { os.RemoveAll(td) }) + + testBoxPath := generateTestBox(t, td, bc.basis) + box, err := bc.Add(testBoxPath, "test/box", "1.2.3", true, "virtualbox", "vmware") + require.NoError(t, err) + require.NotNil(t, box) +} + +func TestAddBadProviders(t *testing.T) { + bc := newBoxCollection(t) + + td, err := ioutil.TempDir("/tmp", "box") + require.NoError(t, err) + t.Cleanup(func() { os.RemoveAll(td) }) + + testBoxPath := generateTestBox(t, td, bc.basis) + _, err = bc.Add(testBoxPath, "test/box", "1.2.4", true, "vmware") + require.Error(t, err) +} + +func TestAll(t *testing.T) { + bc := newBoxCollection(t) + boxes, err := bc.All() + require.NoError(t, err) + require.Equal(t, len(boxes), 2) +} + +func TestFind(t *testing.T) { + bc := newBoxCollection(t) + + boxes, err := bc.Find("test/box", "1.2.3") + require.NoError(t, err) + require.NotNil(t, boxes) + + boxes, err = bc.Find("test/box", "1.2.3", "virtualbox") + require.NoError(t, err) + require.NotNil(t, boxes) + + boxes, err = bc.Find("test/box", "1.2.3", "idontexist") + require.NoError(t, err) + require.Nil(t, boxes) + + boxes, err = bc.Find("test/box", "9.9.9", "virtualbox") + require.NoError(t, err) + require.Nil(t, boxes) + + boxes, err = bc.Find("test/box", "9.9.9") + require.NoError(t, err) + require.Nil(t, boxes) + + boxes, err = bc.Find("test/box", "1.2.3", "vmware", "virtualbox") + require.NoError(t, err) + require.NotNil(t, boxes) +} diff --git a/internal/core/box_test.go b/internal/core/box_test.go index a5ffe6e03..c779726eb 100644 --- a/internal/core/box_test.go +++ b/internal/core/box_test.go @@ -52,15 +52,18 @@ func hashicorpBionicTestBox() *Box { } } -func newFullBox(t *testing.T, boxData *vagrant_server.Box) *Box { - pluginManager := plugin.NewManager( - context.Background(), - hclog.New(&hclog.LoggerOptions{}), - ) - basis := TestBasis(t, WithPluginManager(pluginManager)) +func newFullBox(t *testing.T, boxData *vagrant_server.Box, testBasis *Basis) *Box { + basis := testBasis + if basis == nil { + pluginManager := plugin.NewManager( + context.Background(), + hclog.New(&hclog.LoggerOptions{}), + ) + basis = TestBasis(t, WithPluginManager(pluginManager)) + } td, err := ioutil.TempDir("", "box-metadata") require.NoError(t, err) - data := []byte("{}") + data := []byte("{\"provider\":\"virtualbox\"}") err = os.WriteFile(filepath.Join(td, "metadata.json"), data, 0644) require.NoError(t, err) t.Cleanup(func() { os.RemoveAll(td) }) @@ -75,13 +78,13 @@ func newFullBox(t *testing.T, boxData *vagrant_server.Box) *Box { } func TestNewBox(t *testing.T) { - box := newFullBox(t, testboxBoxData()) + box := newFullBox(t, testboxBoxData(), nil) require.NotNil(t, box) require.Equal(t, "test/box", box.box.Name) } func TestBoxAutomaticUpdateCheckAllowed(t *testing.T) { - testBox := newFullBox(t, testboxBoxData()) + testBox := newFullBox(t, testboxBoxData(), nil) // just did automated check testBox.box.LastUpdate = timestamppb.Now() allowed1, err := testBox.AutomaticUpdateCheckAllowed() diff --git a/internal/server/singleprocess/service_box.go b/internal/server/singleprocess/service_box.go index ae78d0675..d85693e55 100644 --- a/internal/server/singleprocess/service_box.go +++ b/internal/server/singleprocess/service_box.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/vagrant/internal/server/proto/vagrant_server" ) -func (s *service) ListBox( +func (s *service) ListBoxes( ctx context.Context, req *empty.Empty, ) (*vagrant_server.ListBoxesResponse, error) { @@ -55,7 +55,7 @@ func (s *service) FindBox( ctx context.Context, req *vagrant_server.FindBoxRequest, ) (*vagrant_server.FindBoxResponse, error) { - result, err:= s.state.BoxFind(req.Box) + result, err := s.state.BoxFind(req.Box) if err != nil { return nil, err }