vaguerent/internal/core/machine_test.go
2023-06-05 14:18:53 -07:00

394 lines
10 KiB
Go

package core
import (
"testing"
"github.com/hashicorp/vagrant-plugin-sdk/component"
"github.com/hashicorp/vagrant-plugin-sdk/core"
"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
"github.com/hashicorp/vagrant/internal/plugin"
"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestMachineSetValidId(t *testing.T) {
tm := TestMinimalMachine(t)
// Set valid id
tm.SetID("something")
newId, err := tm.ID()
if err != nil {
t.Errorf("Failed to get id")
}
require.Equal(t, newId, "something")
// Ensure new id is save to db
dbTarget, err := tm.Client().GetTarget(tm.ctx,
&vagrant_server.GetTargetRequest{
Target: tm.Ref().(*vagrant_plugin_sdk.Ref_Target),
},
)
if err != nil {
t.Errorf("Failed to get target")
}
require.Equal(t, dbTarget.Target.Uuid, "something")
}
func TestMachineSetEmptyId(t *testing.T) {
tm := TestMinimalMachine(t)
oldId := tm.target.ResourceId
// Set empty id
tm.SetID("")
newId, err := tm.ID()
if err != nil {
t.Errorf("Failed to get id")
}
require.Equal(t, newId, "")
// Machine won't be deleted from db until project is closed, so close project first
err = tm.project.Close()
require.NoError(t, err)
// Ensure machine is deleted from the db by checking for the old id
dbTarget, err := tm.Client().GetTarget(tm.ctx,
&vagrant_server.GetTargetRequest{
Target: &vagrant_plugin_sdk.Ref_Target{
ResourceId: oldId,
Project: tm.target.Project,
Name: tm.target.Name,
},
},
)
require.Nil(t, dbTarget)
require.Error(t, err)
// Verify the DataDir still exists (see below test for more detail on why)
dir, err := tm.DataDir()
require.NoError(t, err)
require.DirExists(t, dir.DataDir().String())
// Also check new id
dbTarget, err = tm.Client().GetTarget(tm.ctx,
&vagrant_server.GetTargetRequest{
Target: &vagrant_plugin_sdk.Ref_Target{
ResourceId: "",
Project: tm.target.Project,
Name: tm.target.Name,
},
},
)
require.Nil(t, dbTarget)
require.Error(t, err)
}
func TestMachineSetIdBlankThenSomethingPreservesDataDir(t *testing.T) {
tm := TestMinimalMachine(t)
// Set empty id, followed by a temp id. This is the same thing that happens
// in the Docker provider's InitState action
require.NoError(t, tm.SetID(""))
require.NoError(t, tm.SetID("preparing"))
// The DataDir should still exist; the Docker provider relies on this
// behavior in order for its provisioning sentinel file handling to work
// properly.
dir, err := tm.DataDir()
require.NoError(t, err)
require.DirExists(t, dir.DataDir().String())
}
func TestMachineGetNonExistentBox(t *testing.T) {
tp := TestMinimalProject(t)
tm := TestMachine(t, tp,
WithTestTargetConfig(testBoxConfig("somebox")),
WithTestTargetProvider("testprovider"),
)
box, err := tm.Box()
require.NoError(t, err)
name, err := box.Name()
require.NoError(t, err)
require.Equal(t, name, "somebox")
provider, err := box.Provider()
require.NoError(t, err)
require.Equal(t, provider, "testprovider")
metaurl, err := box.MetadataURL()
require.NoError(t, err)
require.Empty(t, metaurl)
}
func TestMachineGetExistentBox(t *testing.T) {
tp := TestMinimalProject(t)
tm := TestMachine(t, tp,
WithTestTargetConfig(testBoxConfig("test/box")),
WithTestTargetProvider("virtualbox"),
)
testBox := newFullBox(t, testboxBoxData(), tp.basis)
testBox.Save()
box, err := tm.Box()
require.NoError(t, err)
name, err := box.Name()
require.NoError(t, err)
require.Equal(t, name, "test/box")
provider, err := box.Provider()
require.NoError(t, err)
require.NotEmpty(t, provider)
metaurl, err := box.MetadataURL()
require.NoError(t, err)
require.NotEmpty(t, metaurl)
}
func TestMachineConfigedGuest(t *testing.T) {
commMock := BuildTestCommunicatorPlugin("ssh")
commMock.On("Ready", mock.AnythingOfType("*core.Machine")).Return(true, nil)
commPlugin := plugin.TestPlugin(t,
commMock,
plugin.WithPluginName("ssh"),
plugin.WithPluginTypes(component.CommunicatorType),
)
type test struct {
config *component.ConfigData
errors bool
}
tests := []test{
{config: testGuestConfig("myguest"), errors: false},
{config: testGuestConfig("idontexist"), errors: true},
}
guestMock := BuildTestGuestPlugin("myguest", "")
guestMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(true, nil)
guestMock.On("Parent").Return("", nil)
pluginManager := plugin.TestManager(t,
plugin.TestPlugin(t,
guestMock,
plugin.WithPluginName("myguest"),
plugin.WithPluginTypes(component.GuestType),
),
commPlugin,
)
for _, tc := range tests {
tp := TestProject(t, WithPluginManager(pluginManager))
tm := TestMachine(t, tp,
WithTestTargetConfig(tc.config),
)
guest, err := tm.Guest()
if tc.errors {
require.Error(t, err)
require.Nil(t, guest)
require.Nil(t, tm.cache.Get("guest"))
} else {
require.NoError(t, err)
require.NotNil(t, guest)
require.NotNil(t, tm.cache.Get("guest"))
}
}
}
func TestMachineNoConfigGuest(t *testing.T) {
commMock := BuildTestCommunicatorPlugin("ssh")
commMock.On("Ready", mock.AnythingOfType("*core.Machine")).Return(true, nil)
commPlugin := plugin.TestPlugin(t,
commMock,
plugin.WithPluginName("ssh"),
plugin.WithPluginTypes(component.CommunicatorType),
)
guestMock := BuildTestGuestPlugin("myguest", "")
guestMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(true, nil)
guestMock.On("Parent").Return("", nil)
detectingPlugin := plugin.TestPlugin(t,
guestMock,
plugin.WithPluginName("myguest"),
plugin.WithPluginTypes(component.GuestType),
)
notGuestMock := BuildTestGuestPlugin("mynondetectingguest", "")
notGuestMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(false, nil)
nonDetectingPlugin := plugin.TestPlugin(t,
notGuestMock,
plugin.WithPluginName("mynondetectingguest"),
plugin.WithPluginTypes(component.GuestType),
)
guestChildMock := BuildTestGuestPlugin("myguest-child", "myguest")
guestChildMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(true, nil)
guestChildMock.SetParentComponent(guestMock)
detectingChildPlugin := plugin.TestPlugin(t,
guestChildMock,
plugin.WithPluginName("myguest-child"),
plugin.WithPluginTypes(component.GuestType),
)
type test struct {
plugins []*plugin.Plugin
errors bool
expectedPluginName string
}
tests := []test{
{plugins: []*plugin.Plugin{commPlugin, detectingPlugin}, errors: false, expectedPluginName: "myguest"},
{plugins: []*plugin.Plugin{commPlugin, detectingChildPlugin}, errors: true, expectedPluginName: "myguest-child"},
{plugins: []*plugin.Plugin{commPlugin, detectingChildPlugin, detectingPlugin}, errors: false, expectedPluginName: "myguest-child"},
{plugins: []*plugin.Plugin{commPlugin, detectingPlugin, nonDetectingPlugin}, errors: false, expectedPluginName: "myguest"},
{plugins: []*plugin.Plugin{commPlugin, nonDetectingPlugin}, errors: true},
{plugins: []*plugin.Plugin{commPlugin}, errors: true},
}
for _, tc := range tests {
pluginManager := plugin.TestManager(t, tc.plugins...)
tp := TestProject(t, WithPluginManager(pluginManager))
tm := TestMachine(t, tp)
guest, err := tm.Guest()
if tc.errors {
require.Error(t, err)
require.Nil(t, guest)
require.Nil(t, tm.cache.Get("guest"))
} else {
require.NoError(t, err)
require.NotNil(t, guest)
require.NotNil(t, tm.cache.Get("guest"))
n, _ := guest.PluginName()
if n != tc.expectedPluginName {
t.Error("Found unexpected plugin, ", n)
}
}
}
}
func TestMachineSetState(t *testing.T) {
tm := TestMinimalMachine(t)
type test struct {
id string
state vagrant_server.Operation_PhysicalState
}
tests := []test{
{id: "running", state: vagrant_server.Operation_CREATED},
{id: "not_created", state: vagrant_server.Operation_NOT_CREATED},
{id: "whakhgldksj", state: vagrant_server.Operation_UNKNOWN},
}
for _, tc := range tests {
// Set MachineState
desiredState := &core.MachineState{ID: tc.id}
tm.SetMachineState(desiredState)
require.Equal(t, tc.id, tm.machine.State.Id)
require.Equal(t, tc.state, tm.target.State)
// Ensure new id is save to db
dbTarget, err := tm.Client().GetTarget(tm.ctx,
&vagrant_server.GetTargetRequest{
Target: tm.Ref().(*vagrant_plugin_sdk.Ref_Target),
},
)
require.NoError(t, err)
require.Equal(t, tc.state, dbTarget.Target.State)
}
}
func TestMachineSyncedFolders(t *testing.T) {
mySyncedFolder := syncedFolderPlugin(t, "mysyncedfolder")
myOtherSyncedFolder := syncedFolderPlugin(t, "myothersyncedfolder")
type test struct {
plugins []*plugin.Plugin
config *component.ConfigData
errors bool
expectedFolders int
}
tests := []test{
// One synced folder and plugin available
{
plugins: []*plugin.Plugin{mySyncedFolder},
errors: false,
config: testSyncedFolderConfig(
[]*testSyncedFolder{
{
source: ".",
destination: "/vagrant",
kind: "mysyncedfolder",
},
},
),
expectedFolders: 1,
},
// Many synced folders and available plugins
{
plugins: []*plugin.Plugin{mySyncedFolder, myOtherSyncedFolder},
errors: false,
config: testSyncedFolderConfig(
[]*testSyncedFolder{
{
source: ".",
destination: "/vagrant",
kind: "mysyncedfolder",
},
{
source: "./two",
destination: "/vagrant-two",
kind: "mysyncedfolder",
},
{
source: "./three",
destination: "/vagrant-three",
kind: "myothersyncedfolder",
},
},
),
expectedFolders: 3,
},
// Synced folder with unavailable plugin
{
plugins: []*plugin.Plugin{mySyncedFolder, myOtherSyncedFolder},
errors: true,
config: testSyncedFolderConfig(
[]*testSyncedFolder{
{
source: ".",
destination: "/vagrant",
kind: "idontexist",
},
{
source: "./two",
destination: "/vagrant-two",
kind: "mysyncedfolder",
},
{
source: "./three",
destination: "/vagrant-three",
kind: "myothersyncedfolder",
},
},
),
},
}
for _, tc := range tests {
pluginManager := plugin.TestManager(t, tc.plugins...)
tp := TestProject(t, WithPluginManager(pluginManager))
tm := TestMachine(t, tp,
WithTestTargetConfig(tc.config),
)
folders, err := tm.SyncedFolders()
if tc.errors {
require.Error(t, err)
} else {
require.NoError(t, err)
require.NotNil(t, folders)
require.Len(t, folders, tc.expectedFolders)
}
}
}
func stringPtr(s string) *string {
return &s
}