Merge pull request #295 from hashicorp/target-load-stored-configuration

Testing fixes and target loading
This commit is contained in:
Chris Roberts 2022-06-28 10:13:52 -07:00 committed by GitHub
commit f9dd348847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 314 additions and 334 deletions

View File

@ -0,0 +1,72 @@
package core
import (
"testing"
"github.com/hashicorp/vagrant-plugin-sdk/component"
"github.com/hashicorp/vagrant/internal/plugin"
)
// Synced folder entry
type testSyncedFolder struct {
source string
destination string
kind string
}
// Add vm box name to configuration
func testBoxConfig(name string) *component.ConfigData {
return &component.ConfigData{
Data: map[string]interface{}{
"vm": &component.ConfigData{
Data: map[string]interface{}{
"box": name,
},
},
},
}
}
// Add synced folders to vm configuration
func testSyncedFolderConfig(folders []*testSyncedFolder) *component.ConfigData {
f := map[interface{}]interface{}{}
for _, tf := range folders {
f[tf.destination] = map[interface{}]interface{}{
"hostpath": tf.source,
"guestpath": tf.destination,
"type": tf.kind,
}
}
return &component.ConfigData{
Data: map[string]interface{}{
"vm": &component.ConfigData{
Data: map[string]interface{}{
"__synced_folders": f,
},
},
},
}
}
// Set guest name in vm configuration
func testGuestConfig(name string) *component.ConfigData {
return &component.ConfigData{
Data: map[string]interface{}{
"vm": &component.ConfigData{
Data: map[string]interface{}{
"guest": name,
},
},
},
}
}
// Generate a synced folder plugin
func syncedFolderPlugin(t *testing.T, name string) *plugin.Plugin {
return plugin.TestPlugin(t,
BuildTestSyncedFolderPlugin(""),
plugin.WithPluginName(name),
plugin.WithPluginTypes(component.SyncedFolderType),
)
}

View File

@ -107,6 +107,10 @@ func (m *Machine) Guest() (g core.Guest, err error) {
}
}()
// Note that if we get the guest value from the
// local cache, we return it directly to prevent
// the seeding and cache registration from happening
// again.
i := m.cache.Get("guest")
if i != nil {
return i.(core.Guest), nil
@ -122,13 +126,13 @@ func (m *Machine) Guest() (g core.Guest, err error) {
} else {
guestName, ok := vg.(string)
if ok {
guest, err := m.project.basis.component(m.ctx, component.GuestType, guestName)
var guest *Component
guest, err = m.project.basis.component(m.ctx, component.GuestType, guestName)
if err != nil {
return nil, err
}
if guest != nil {
return guest.Value.(core.Guest), nil
}
g = guest.Value.(core.Guest)
return
} else {
m.logger.Debug("guest name was not a valid string value",
"guest", vg,
@ -388,7 +392,15 @@ func (m *Machine) SyncedFolders() (folders []*core.MachineSyncedFolder, err erro
}
}
if ftype == "" {
ftype = "virtualbox" // TODO(spox): use default type function after rebase
ftype, err = m.project.DefaultProvider(
&core.DefaultProviderOptions{
CheckUsable: false,
MachineName: m.target.Name,
},
)
if err != nil {
return nil, err
}
}
lookup := "syncedfolder_" + ftype

View File

@ -10,15 +10,10 @@ import (
"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
func TestMachineSetValidId(t *testing.T) {
tm, err := TestMinimalMachine(t)
if err != nil {
t.Fatal(err)
}
tm := TestMinimalMachine(t)
// Set valid id
tm.SetID("something")
@ -41,7 +36,7 @@ func TestMachineSetValidId(t *testing.T) {
}
func TestMachineSetEmptyId(t *testing.T) {
tm, _ := TestMinimalMachine(t)
tm := TestMinimalMachine(t)
oldId := tm.target.ResourceId
// Set empty id
@ -85,7 +80,7 @@ func TestMachineSetEmptyId(t *testing.T) {
}
func TestMachineSetIdBlankThenSomethingPreservesDataDir(t *testing.T) {
tm, _ := TestMinimalMachine(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
@ -102,8 +97,8 @@ func TestMachineSetIdBlankThenSomethingPreservesDataDir(t *testing.T) {
func TestMachineGetNonExistentBox(t *testing.T) {
tp := TestMinimalProject(t)
tm, _ := TestMachine(t, tp,
WithTestTargetConfig(testBoxConfig("somename")),
tm := TestMachine(t, tp,
WithTestTargetConfig(testBoxConfig("somebox")),
WithTestTargetProvider("testprovider"),
)
@ -120,133 +115,11 @@ func TestMachineGetNonExistentBox(t *testing.T) {
require.Empty(t, metaurl)
}
func testBoxConfig(name string) *vagrant_plugin_sdk.Args_ConfigData {
b_key, _ := anypb.New(&wrapperspb.StringValue{Value: "box"})
b_name, _ := anypb.New(&wrapperspb.StringValue{Value: name})
vm_key, _ := anypb.New(&wrapperspb.StringValue{Value: "vm"})
vm, _ := anypb.New(&vagrant_plugin_sdk.Args_ConfigData{
Data: &vagrant_plugin_sdk.Args_Hash{
Entries: []*vagrant_plugin_sdk.Args_HashEntry{
{
Key: b_key,
Value: b_name,
},
},
},
})
return &vagrant_plugin_sdk.Args_ConfigData{
Data: &vagrant_plugin_sdk.Args_Hash{
Entries: []*vagrant_plugin_sdk.Args_HashEntry{
{
Key: vm_key,
Value: vm,
},
},
},
}
}
type testSyncedFolder struct {
source string
destination string
kind string
}
func testSyncedFolderConfig(folders []*testSyncedFolder) *vagrant_plugin_sdk.Args_ConfigData {
f := &vagrant_plugin_sdk.Args_Hash{
Entries: []*vagrant_plugin_sdk.Args_HashEntry{},
}
src_key, _ := anypb.New(&wrapperspb.StringValue{Value: "hostpath"})
dst_key, _ := anypb.New(&wrapperspb.StringValue{Value: "guestpath"})
type_key, _ := anypb.New(&wrapperspb.StringValue{Value: "type"})
for i := 0; i < len(folders); i++ {
fld := folders[i]
f_src, _ := anypb.New(&wrapperspb.StringValue{Value: fld.source})
f_dst, _ := anypb.New(&wrapperspb.StringValue{Value: fld.destination})
f_type, _ := anypb.New(&wrapperspb.StringValue{Value: fld.kind})
hsh := &vagrant_plugin_sdk.Args_Hash{
Entries: []*vagrant_plugin_sdk.Args_HashEntry{
{
Key: src_key,
Value: f_src,
},
{
Key: dst_key,
Value: f_dst,
},
{
Key: type_key,
Value: f_type,
},
},
}
entry, _ := anypb.New(hsh)
f.Entries = append(f.Entries,
&vagrant_plugin_sdk.Args_HashEntry{
Key: f_dst,
Value: entry,
},
)
}
f_key, _ := anypb.New(&wrapperspb.StringValue{Value: "__synced_folders"})
f_value, _ := anypb.New(f)
vm_key, _ := anypb.New(&wrapperspb.StringValue{Value: "vm"})
vm, _ := anypb.New(&vagrant_plugin_sdk.Args_ConfigData{
Data: &vagrant_plugin_sdk.Args_Hash{
Entries: []*vagrant_plugin_sdk.Args_HashEntry{
{
Key: f_key,
Value: f_value,
},
},
},
})
return &vagrant_plugin_sdk.Args_ConfigData{
Data: &vagrant_plugin_sdk.Args_Hash{
Entries: []*vagrant_plugin_sdk.Args_HashEntry{
{
Key: vm_key,
Value: vm,
},
},
},
}
}
func testGuestConfig(name string) *vagrant_plugin_sdk.Args_ConfigData {
g_key, _ := anypb.New(&wrapperspb.StringValue{Value: "guest"})
g_name, _ := anypb.New(&wrapperspb.StringValue{Value: name})
vm_key, _ := anypb.New(&wrapperspb.StringValue{Value: "vm"})
vm, _ := anypb.New(&vagrant_plugin_sdk.Args_ConfigData{
Data: &vagrant_plugin_sdk.Args_Hash{
Entries: []*vagrant_plugin_sdk.Args_HashEntry{
{
Key: g_key,
Value: g_name,
},
},
},
})
return &vagrant_plugin_sdk.Args_ConfigData{
Data: &vagrant_plugin_sdk.Args_Hash{
Entries: []*vagrant_plugin_sdk.Args_HashEntry{
{
Key: vm_key,
Value: vm,
},
},
},
}
}
func TestMachineGetExistentBox(t *testing.T) {
tp := TestMinimalProject(t)
tm, _ := TestMachine(t, tp,
tm := TestMachine(t, tp,
WithTestTargetConfig(testBoxConfig("test/box")),
WithTestTargetProvider("virtualbox"),
)
testBox := newFullBox(t, testboxBoxData(), tp.basis)
testBox.Save()
@ -266,7 +139,7 @@ func TestMachineGetExistentBox(t *testing.T) {
func TestMachineConfigedGuest(t *testing.T) {
type test struct {
config *vagrant_plugin_sdk.Args_ConfigData
config *component.ConfigData
errors bool
}
@ -275,7 +148,7 @@ func TestMachineConfigedGuest(t *testing.T) {
{config: testGuestConfig("idontexist"), errors: true},
}
guestMock := BuildTestGuestPlugin("myguest", "")
guestMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(false, nil)
guestMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(true, nil)
guestMock.On("Parent").Return("", nil)
pluginManager := plugin.TestManager(t,
@ -288,7 +161,7 @@ func TestMachineConfigedGuest(t *testing.T) {
for _, tc := range tests {
tp := TestProject(t, WithPluginManager(pluginManager))
tm, _ := TestMachine(t, tp,
tm := TestMachine(t, tp,
WithTestTargetConfig(tc.config),
)
guest, err := tm.Guest()
@ -350,7 +223,7 @@ func TestMachineNoConfigGuest(t *testing.T) {
pluginManager := plugin.TestManager(t, tc.plugins...)
tp := TestProject(t, WithPluginManager(pluginManager))
tm, _ := TestMachine(t, tp, WithTestTargetMinimalConfig())
tm := TestMachine(t, tp)
guest, err := tm.Guest()
if tc.errors {
require.Error(t, err)
@ -369,7 +242,7 @@ func TestMachineNoConfigGuest(t *testing.T) {
}
func TestMachineSetState(t *testing.T) {
tm, _ := TestMinimalMachine(t)
tm := TestMinimalMachine(t)
type test struct {
id string
@ -402,21 +275,13 @@ func TestMachineSetState(t *testing.T) {
}
}
func syncedFolderPlugin(t *testing.T, name string) *plugin.Plugin {
return plugin.TestPlugin(t,
BuildTestSyncedFolderPlugin(""),
plugin.WithPluginName(name),
plugin.WithPluginTypes(component.SyncedFolderType),
)
}
func TestMachineSyncedFolders(t *testing.T) {
mySyncedFolder := syncedFolderPlugin(t, "mysyncedfolder")
myOtherSyncedFolder := syncedFolderPlugin(t, "myothersyncedfolder")
type test struct {
plugins []*plugin.Plugin
config *vagrant_plugin_sdk.Args_ConfigData
config *component.ConfigData
errors bool
expectedFolders int
}
@ -427,7 +292,7 @@ func TestMachineSyncedFolders(t *testing.T) {
errors: false,
config: testSyncedFolderConfig(
[]*testSyncedFolder{
&testSyncedFolder{
{
source: ".",
destination: "/vagrant",
kind: "mysyncedfolder",
@ -442,20 +307,20 @@ func TestMachineSyncedFolders(t *testing.T) {
errors: false,
config: testSyncedFolderConfig(
[]*testSyncedFolder{
&testSyncedFolder{
{
source: ".",
destination: "/vagrant",
kind: "mysyncedfolder",
},
&testSyncedFolder{
{
source: "./two",
destination: "/vagrant-two",
kind: "mysyncedfolder",
},
&testSyncedFolder{
{
source: "./three",
destination: "/vagrant-three",
kind: "mysyncedfolder",
kind: "myothersyncedfolder",
},
},
),
@ -467,20 +332,20 @@ func TestMachineSyncedFolders(t *testing.T) {
errors: true,
config: testSyncedFolderConfig(
[]*testSyncedFolder{
&testSyncedFolder{
{
source: ".",
destination: "/vagrant",
kind: "mysyncedfolder",
kind: "idontexist",
},
&testSyncedFolder{
{
source: "./two",
destination: "/vagrant-two",
kind: "mysyncedfolder",
},
&testSyncedFolder{
{
source: "./three",
destination: "/vagrant-three",
kind: "mysyncedfolder",
kind: "myothersyncedfolder",
},
},
),
@ -490,7 +355,7 @@ func TestMachineSyncedFolders(t *testing.T) {
for _, tc := range tests {
pluginManager := plugin.TestManager(t, tc.plugins...)
tp := TestProject(t, WithPluginManager(pluginManager))
tm, _ := TestMachine(t, tp,
tm := TestMachine(t, tp,
WithTestTargetConfig(tc.config),
)
folders, err := tm.SyncedFolders()

View File

@ -13,7 +13,9 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-multierror"
goplugin "github.com/hashicorp/go-plugin"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"github.com/hashicorp/vagrant-plugin-sdk/component"
@ -436,12 +438,12 @@ func (p *Project) JobInfo() *component.JobInfo {
// LoadTarget loads a target within the current project. If the target is not
// found, it will be created.
func (p *Project) LoadTarget(topts ...TargetOption) (t *Target, err error) {
func (p *Project) LoadTarget(topts ...TargetOption) (*Target, error) {
p.m.Lock()
defer p.m.Unlock()
// Create our target
t = &Target{
t := &Target{
cache: cacher.New(),
ctx: p.ctx,
project: p,
@ -451,6 +453,7 @@ func (p *Project) LoadTarget(topts ...TargetOption) (t *Target, err error) {
},
ui: p.ui,
}
var err error
// Apply any options provided
for _, opt := range topts {
@ -481,12 +484,15 @@ func (p *Project) LoadTarget(topts ...TargetOption) (t *Target, err error) {
if err != nil {
return nil, err
}
t.vagrantfile = tv.(*Vagrantfile)
// Set the vagrantfile if one was returned
if tv != nil {
t.vagrantfile = tv.(*Vagrantfile)
}
}
// If this is the first time through, re-init the target
if err = t.init(); err != nil {
return
return nil, err
}
// If the data directory is set, set it
@ -521,7 +527,7 @@ func (p *Project) LoadTarget(topts ...TargetOption) (t *Target, err error) {
p.targets[t.target.ResourceId] = t
p.targets[t.target.Name] = t
return
return t, nil
}
// Client returns the API client for the backend server.
@ -724,14 +730,7 @@ func (p *Project) InitTargets() (err error) {
updated := false
for _, t := range targets {
_, err = p.Client().UpsertTarget(p.ctx,
&vagrant_server.UpsertTargetRequest{
Target: &vagrant_server.Target{
Name: t,
Project: p.Ref().(*vagrant_plugin_sdk.Ref_Project),
},
},
)
_, err = p.createTarget(t)
if err != nil {
p.logger.Error("failed to initialize target with project",
"project", p.Name(),
@ -775,6 +774,44 @@ func (p *Project) refreshProject() (err error) {
return
}
// Create a target within this project if it does not already exist
func (p *Project) createTarget(
name string, // name of the target
) (*vagrant_server.Target, error) {
result, err := p.Client().FindTarget(p.ctx,
&vagrant_server.FindTargetRequest{
Target: &vagrant_server.Target{
Name: name,
Project: p.Ref().(*vagrant_plugin_sdk.Ref_Project),
},
},
)
// If we encountered any error except a not found, return it
if err != nil && status.Code(err) != codes.NotFound {
return nil, err
}
// If we have no error here, we have an existing result
if err == nil {
return result.Target, nil
}
// And if we are still here, create it
resp, err := p.Client().UpsertTarget(p.ctx,
&vagrant_server.UpsertTargetRequest{
Target: &vagrant_server.Target{
Name: name,
Project: p.Ref().(*vagrant_plugin_sdk.Ref_Project),
},
},
)
if err != nil {
return nil, err
}
return resp.Target, nil
}
// Calls the function provided and converts the
// result to an expected type. If no type conversion
// is required, a `false` value for the expectedType
@ -890,9 +927,10 @@ func WithProjectRef(r *vagrant_plugin_sdk.Ref_Project) ProjectOption {
result, err := p.Client().FindProject(p.ctx,
&vagrant_server.FindProjectRequest{
Project: &vagrant_server.Project{
Basis: r.Basis,
Name: r.Name,
Path: r.Path,
Basis: r.Basis,
Name: r.Name,
Path: r.Path,
ResourceId: r.ResourceId,
},
},
)
@ -916,7 +954,7 @@ func WithProjectRef(r *vagrant_plugin_sdk.Ref_Project) ProjectOption {
}
// Before we init, validate basis is consistent
if project.Basis.ResourceId != r.Basis.ResourceId {
if r.Basis != nil && project.Basis.ResourceId != r.Basis.ResourceId {
p.logger.Error("invalid basis for project", "request-basis", r.Basis,
"project-basis", project.Basis)
return errors.New("project basis configuration is invalid")

View File

@ -11,14 +11,11 @@ import (
func projectTargets(t *testing.T, project *Project, numTargets int) (targets []*Target) {
targets = make([]*Target, numTargets)
for i := 0; i < numTargets; i++ {
tt, err := TestTarget(t, project, &vagrant_server.Target{
tt := TestTarget(t, project, &vagrant_server.Target{
ResourceId: fmt.Sprintf("id-%d", i),
Name: fmt.Sprintf("target-%d", i),
Uuid: fmt.Sprintf("uuid-%d", i),
})
if err != nil {
t.Error(err)
}
targets = append(targets, tt)
}
return
@ -35,14 +32,8 @@ func TestNewProject(t *testing.T) {
func TestProjectGetTarget(t *testing.T) {
tp := TestMinimalProject(t)
// Add targets to project
targetOne, err := TestTarget(t, tp, &vagrant_server.Target{ResourceId: "id-one", Name: "target-one"})
if err != nil {
t.Error(err)
}
targetTwo, err := TestTarget(t, tp, &vagrant_server.Target{ResourceId: "id-two", Name: "target-two"})
if err != nil {
t.Error(err)
}
targetOne := TestTarget(t, tp, &vagrant_server.Target{ResourceId: "id-one", Name: "target-one"})
targetTwo := TestTarget(t, tp, &vagrant_server.Target{ResourceId: "id-two", Name: "target-two"})
// Get by id
one, err := tp.Target("id-one", "")

View File

@ -472,41 +472,20 @@ func (t *Target) init() (err error) {
// If the configuration was updated during load, save it so
// we can re-apply after loading stored data
var conf *vagrant_plugin_sdk.Args_ConfigData
if t.target != nil && t.target.Configuration != nil {
if t.target.Configuration != nil {
conf = t.target.Configuration
}
// First we want to run a lookup if this target already exists
if t.target.ResourceId != "" {
resp, err := t.Client().FindTarget(t.ctx,
&vagrant_server.FindTargetRequest{
Target: &vagrant_server.Target{
ResourceId: t.target.ResourceId,
},
},
)
if err != nil {
return err
}
t.target = resp.Target
} else {
for _, pt := range t.project.project.Targets {
if t.target.Name == pt.Name {
resp, err := t.Client().FindTarget(t.ctx,
&vagrant_server.FindTargetRequest{
Target: &vagrant_server.Target{
ResourceId: pt.ResourceId,
},
},
)
if err != nil {
return err
}
t.target = resp.Target
}
}
// Pull target info
resp, err := t.Client().FindTarget(t.ctx,
&vagrant_server.FindTargetRequest{
Target: t.target,
},
)
if err != nil {
return
}
t.target = resp.Target
// If we have configuration data, re-apply it
if conf != nil {
@ -524,6 +503,10 @@ func (t *Target) init() (err error) {
// If we don't have configuration data, just stub
if t.target.Configuration == nil {
t.target.Configuration = &vagrant_plugin_sdk.Args_ConfigData{}
t.vagrantfile = t.project.vagrantfile.clone("target", t)
t.vagrantfile.root = &component.ConfigData{
Data: map[string]interface{}{},
}
return
}
@ -583,58 +566,34 @@ func WithTargetName(name string) TargetOption {
// Configure target with proto ref
func WithTargetRef(r *vagrant_plugin_sdk.Ref_Target) TargetOption {
return func(t *Target) (err error) {
// Project must be set before we continue
if t.project == nil {
return fmt.Errorf("project must be set before loading target")
}
return func(t *Target) error {
// Target ref must include a resource id or name
if r.Name == "" && r.ResourceId == "" {
return fmt.Errorf("target ref must include ResourceId and/or Name")
}
var target *vagrant_server.Target
// Target ref must include project ref if resource id is empty
if r.Name == "" && r.Project == nil {
return fmt.Errorf("target ref must include Project for name lookup")
}
result, err := t.Client().FindTarget(t.ctx,
&vagrant_server.FindTargetRequest{
Target: &vagrant_server.Target{
ResourceId: r.ResourceId,
Name: r.Name,
Project: t.project.Ref().(*vagrant_plugin_sdk.Ref_Project),
Project: r.Project,
},
},
)
// TODO(spox): check for not found and error if something different
// if err != nil {
// return err
// }
if result != nil {
target = result.Target
} else {
var result *vagrant_server.UpsertTargetResponse
result, err = t.Client().UpsertTarget(t.ctx,
&vagrant_server.UpsertTargetRequest{
Target: &vagrant_server.Target{
Name: r.Name,
Project: t.project.Ref().(*vagrant_plugin_sdk.Ref_Project),
},
},
)
if err != nil {
return
}
target = result.Target
if err != nil {
return err
}
if r.Project != nil && target.Project.ResourceId != r.Project.ResourceId {
t.logger.Error("invalid project for target",
"request-project", r.Project,
"target-project", target.Project)
return fmt.Errorf("target project configuration is invalid")
}
t.target = target
return
t.target = result.Target
return nil
}
}

View File

@ -79,8 +79,7 @@ func TestTargetIndexSet(t *testing.T) {
t.Error(err)
}
tt, err := TestMinimalTarget(t)
require.NoError(t, err)
tt := TestMinimalTarget(t)
tt.target.Name = "newName"
updated, err := ti.Set(tt)

View File

@ -9,7 +9,7 @@ import (
)
func TestTargetSpecializeMachine(t *testing.T) {
tt, _ := TestMinimalTarget(t)
tt := TestMinimalTarget(t)
specialized, err := tt.Specialize((*core.Machine)(nil))
if err != nil {
t.Errorf("Specialize function returned an error")
@ -28,8 +28,8 @@ func TestTargetSpecializeMachine(t *testing.T) {
func TestTargetSpecializeMultiMachine(t *testing.T) {
p := TestMinimalProject(t)
tt1, _ := TestTarget(t, p, &vagrant_server.Target{Name: "tt1"})
tt2, _ := TestTarget(t, p, &vagrant_server.Target{Name: "tt2"})
tt1 := TestTarget(t, p, &vagrant_server.Target{Name: "tt1"})
tt2 := TestTarget(t, p, &vagrant_server.Target{Name: "tt2"})
specialized, err := tt1.Specialize((*core.Machine)(nil))
if err != nil {
@ -53,7 +53,7 @@ func TestTargetSpecializeMultiMachine(t *testing.T) {
}
func TestTargetSpecializeBad(t *testing.T) {
tt, _ := TestMinimalTarget(t)
tt := TestMinimalTarget(t)
specialized, err := tt.Specialize((*core.Project)(nil))
if err != nil {

View File

@ -105,10 +105,3 @@ func TestBasis(t testing.T, opts ...BasisOption) (b *Basis) {
b, _ = NewBasis(context.Background(), append(defaultOpts, opts...)...)
return
}
// func WithTestBasisConfig(config *vagrant_plugin_sdk.Vagrantfile_Vagrantfile) BasisOption {
// return func(m *Basis) (err error) {
// m.basis.Configuration = config
// return
// }
// }

View File

@ -12,12 +12,17 @@ import (
// factories, configuration, etc.
func TestProject(t testing.T, opts ...BasisOption) *Project {
b := TestBasis(t, opts...)
p, _ := b.LoadProject([]ProjectOption{
p, err := b.LoadProject([]ProjectOption{
WithProjectRef(&vagrant_plugin_sdk.Ref_Project{
Basis: b.Ref().(*vagrant_plugin_sdk.Ref_Basis),
Name: "test-project"},
),
}...)
if err != nil {
t.Fatal(err)
}
return p
}
@ -27,11 +32,16 @@ func TestMinimalProject(t testing.T) *Project {
pluginManager := plugin.TestManager(t)
b := TestBasis(t, WithPluginManager(pluginManager))
p, _ := b.LoadProject([]ProjectOption{
p, err := b.LoadProject([]ProjectOption{
WithProjectRef(&vagrant_plugin_sdk.Ref_Project{
Basis: b.Ref().(*vagrant_plugin_sdk.Ref_Basis),
Name: "test-project"},
),
}...)
if err != nil {
t.Fatal(err)
}
return p
}

View File

@ -2,8 +2,12 @@ package core
import (
"context"
"fmt"
"github.com/imdario/mergo"
"github.com/hashicorp/go-multierror"
"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/server/proto/vagrant_server"
@ -14,32 +18,53 @@ import (
// TestTarget returns a fully in-memory and side-effect free Target that
// can be used for testing. Additional options can be given to provide your own
// factories, configuration, etc.
func TestTarget(t testing.T, tp *Project, tt *vagrant_server.Target) (target *Target, err error) {
testingTarget := ptypes.TestTarget(t, tt)
testingTarget.Project = tp.Ref().(*vagrant_plugin_sdk.Ref_Project)
tp.basis.client.UpsertTarget(
func TestTarget(t testing.T, p *Project, st *vagrant_server.Target, opts ...TestTargetOption) (target *Target) {
testingTarget := ptypes.TestTarget(t, st)
testingTarget.Project = p.Ref().(*vagrant_plugin_sdk.Ref_Project)
_, err := p.basis.client.UpsertTarget(
context.Background(),
&vagrant_server.UpsertTargetRequest{
Project: tp.Ref().(*vagrant_plugin_sdk.Ref_Project),
Project: p.Ref().(*vagrant_plugin_sdk.Ref_Project),
Target: testingTarget,
},
)
target, err = tp.LoadTarget([]TargetOption{
WithTargetRef(&vagrant_plugin_sdk.Ref_Target{Project: tp.Ref().(*vagrant_plugin_sdk.Ref_Project), Name: testingTarget.Name}),
if err != nil {
t.Fatal(err)
}
target, err = p.LoadTarget([]TargetOption{
WithTargetRef(
&vagrant_plugin_sdk.Ref_Target{
Project: p.Ref().(*vagrant_plugin_sdk.Ref_Project),
Name: testingTarget.Name,
},
),
}...)
if err != nil {
t.Fatal(err)
return
}
tp.project.Targets = append(tp.project.Targets, target.Ref().(*vagrant_plugin_sdk.Ref_Target))
if err = p.refreshProject(); err != nil {
t.Fatal(err)
}
for _, opt := range opts {
if oerr := opt(target); oerr != nil {
err = multierror.Append(err, oerr)
}
}
if err != nil {
t.Fatal(err)
}
return
}
// TestMinimalTarget uses a minimal project to setup the most basic target
// that will work for testing
func TestMinimalTarget(t testing.T) (target *Target, err error) {
func TestMinimalTarget(t testing.T) (target *Target) {
tp := TestMinimalProject(t)
tp.basis.client.UpsertTarget(
_, err := tp.basis.client.UpsertTarget(
context.Background(),
&vagrant_server.UpsertTargetRequest{
Project: tp.Ref().(*vagrant_plugin_sdk.Ref_Project),
@ -49,10 +74,18 @@ func TestMinimalTarget(t testing.T) (target *Target, err error) {
},
},
)
target, err = tp.LoadTarget([]TargetOption{
WithTargetRef(&vagrant_plugin_sdk.Ref_Target{Project: tp.Ref().(*vagrant_plugin_sdk.Ref_Project), Name: "test-target"}),
}...)
if err != nil {
t.Fatal(err)
}
target, err = tp.LoadTarget([]TargetOption{
WithTargetRef(
&vagrant_plugin_sdk.Ref_Target{
Project: tp.Ref().(*vagrant_plugin_sdk.Ref_Project),
Name: "test-target",
},
),
}...)
if err != nil {
t.Fatal(err)
}
@ -63,63 +96,64 @@ func TestMinimalTarget(t testing.T) (target *Target, err error) {
// TestMachine returns a fully in-memory and side-effect free Machine that
// can be used for testing. Additional options can be given to provide your own
// factories, configuration, etc.
func TestMachine(t testing.T, tp *Project, opts ...TestMachineOption) (machine *Machine, err error) {
tt, _ := TestTarget(t, tp, &vagrant_server.Target{})
func TestMachine(t testing.T, tp *Project, opts ...TestTargetOption) (machine *Machine) {
tt := TestTarget(t, tp, &vagrant_server.Target{})
specialized, err := tt.Specialize((*core.Machine)(nil))
if err != nil {
return nil, err
t.Fatal(err)
}
machine = specialized.(*Machine)
for _, opt := range opts {
if oerr := opt(machine); oerr != nil {
err = multierror.Append(err, oerr)
}
}
if err != nil {
t.Fatal(err)
}
return
}
// TestMinimalMachine uses a minimal project to setup the most basic machine
// that will work for testing
func TestMinimalMachine(t testing.T) (machine *Machine, err error) {
func TestMinimalMachine(t testing.T) (machine *Machine) {
tp := TestMinimalProject(t)
tt, err := TestTarget(t, tp, &vagrant_server.Target{})
if err != nil {
t.Fatal(err)
return
}
tt := TestTarget(t, tp, &vagrant_server.Target{})
specialized, err := tt.Specialize((*core.Machine)(nil))
if err != nil {
t.Fatal(err)
return nil, err
}
machine = specialized.(*Machine)
WithTestTargetMinimalConfig()(machine)
return
}
type TestMachineOption func(*Machine) error
type TestTargetOption func(interface{}) error
func WithTestTargetMinimalConfig() TestMachineOption {
return func(m *Machine) (err error) {
m.target.Configuration = &vagrant_plugin_sdk.Args_ConfigData{}
return
func WithTestTargetConfig(config *component.ConfigData) TestTargetOption {
return func(raw interface{}) (err error) {
switch v := raw.(type) {
case *Target:
return mergo.Merge(v.vagrantfile.root, config)
case *Machine:
return mergo.Merge(v.vagrantfile.root, config)
default:
panic(fmt.Sprintf("Invalid type for TestTargetOption (%T)", raw))
}
}
}
func WithTestTargetConfig(config *vagrant_plugin_sdk.Args_ConfigData) TestMachineOption {
return func(m *Machine) (err error) {
m.target.Configuration = config
return
}
}
func WithTestTargetProvider(provider string) TestMachineOption {
return func(m *Machine) (err error) {
m.target.Provider = provider
func WithTestTargetProvider(provider string) TestTargetOption {
return func(raw interface{}) (err error) {
switch v := raw.(type) {
case *Target:
v.target.Provider = provider
case *Machine:
v.target.Provider = provider
default:
panic(fmt.Sprintf("Invalid type for TestTargetOption (%T)", raw))
}
return
}
}

View File

@ -452,14 +452,15 @@ func (v *Vagrantfile) Target(
return
}
// Convert to actual Vagrantfile for target setup
vf := conf.(*Vagrantfile)
target, err = v.origin.LoadTarget(
WithTargetName(name),
WithTargetVagrantfile(vf),
)
opts := []TargetOption{WithTargetName(name)}
var vf *Vagrantfile
if conf != nil {
// Convert to actual Vagrantfile for target setup
vf = conf.(*Vagrantfile)
opts = append(opts, WithTargetVagrantfile(vf))
}
target, err = v.origin.LoadTarget(opts...)
if err != nil {
return
}
@ -467,21 +468,24 @@ func (v *Vagrantfile) Target(
// Since the target config gives us a Vagrantfile which is
// attached to the project, we need to clone it and attach
// it to the target we loaded
rawTarget := target.(*Target)
tvf := v.clone(name, rawTarget)
if err = tvf.Init(); err != nil {
return nil, err
}
rawTarget.vagrantfile = tvf
if vf != nil {
rawTarget := target.(*Target)
tvf := v.clone(name, rawTarget)
if err = tvf.Init(); err != nil {
return nil, err
}
rawTarget.vagrantfile = tvf
if err = vf.Close(); err != nil {
return nil, err
if err = vf.Close(); err != nil {
return nil, err
}
}
return
}
// Generate a new Vagrantfile for the given target
// NOTE: This function may return a nil result without an error
// TODO(spox): Provider validation is not currently implemented
func (v *Vagrantfile) TargetConfig(
name, // name of the target
@ -498,11 +502,11 @@ func (v *Vagrantfile) TargetConfig(
subvm, err := v.GetValue("vm", "__defined_vms", name)
if err != nil {
v.logger.Error("failed to get subvm",
v.logger.Warn("failed to get subvm",
"name", name,
"error", err,
)
return nil, err
return nil, nil
}
if subvm == nil {

View File

@ -1,12 +1,12 @@
package state
import (
"google.golang.org/protobuf/proto"
"github.com/google/uuid"
"github.com/hashicorp/go-memdb"
bolt "go.etcd.io/bbolt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
@ -165,6 +165,9 @@ func (s *State) targetList(
result = append(result, &vagrant_plugin_sdk.Ref_Target{
ResourceId: next.(*targetIndexRecord).Id,
Name: next.(*targetIndexRecord).Name,
Project: &vagrant_plugin_sdk.Ref_Project{
ResourceId: next.(*targetIndexRecord).ProjectId,
},
})
}