If the box does not exist in the db, then a box will be returned with the name and provider information. All other box information at that point is unknown.
318 lines
7.3 KiB
Go
318 lines
7.3 KiB
Go
package core
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
|
|
"github.com/mitchellh/mapstructure"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
|
|
"github.com/hashicorp/go-hclog"
|
|
"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"
|
|
)
|
|
|
|
type Machine struct {
|
|
*Target
|
|
box *Box
|
|
machine *vagrant_server.Target_Machine
|
|
logger hclog.Logger
|
|
guest core.Guest
|
|
}
|
|
|
|
// Close implements core.Machine
|
|
func (m *Machine) Close() (err error) {
|
|
return
|
|
}
|
|
|
|
// ID implements core.Machine
|
|
func (m *Machine) ID() (id string, err error) {
|
|
return m.machine.Id, nil
|
|
}
|
|
|
|
// SetID implements core.Machine
|
|
func (m *Machine) SetID(value string) (err error) {
|
|
m.machine.Id = value
|
|
if value == "" {
|
|
m.target.Record = nil
|
|
err = m.Destroy()
|
|
} else {
|
|
err = m.SaveMachine()
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (m *Machine) Box() (b core.Box, err error) {
|
|
if m.box == nil {
|
|
boxes, _ := m.project.Boxes()
|
|
boxName := m.Config().ConfigVm.Box
|
|
// Get the first provider available - that's the one that
|
|
// will be used to launch the machine
|
|
provider, err := m.ProviderName()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b, err := boxes.Find(boxName, "", provider)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if b == nil {
|
|
return &Box{
|
|
basis: m.project.basis,
|
|
box: &vagrant_server.Box{
|
|
Name: boxName,
|
|
Provider: provider,
|
|
},
|
|
}, nil
|
|
}
|
|
m.machine.Box = b.(*Box).ToProto()
|
|
m.SaveMachine()
|
|
m.box = b.(*Box)
|
|
}
|
|
|
|
return m.box, nil
|
|
}
|
|
|
|
// Guest implements core.Machine
|
|
func (m *Machine) Guest() (g core.Guest, err error) {
|
|
defer func() {
|
|
if g == nil {
|
|
return
|
|
}
|
|
s, ok := g.(core.Seeder)
|
|
if !ok {
|
|
m.logger.Error("synced folder plugin does not implement seeder interface")
|
|
err = fmt.Errorf("cannot seed synced folder plugin")
|
|
return
|
|
}
|
|
err = m.seedWithMachine(s)
|
|
return
|
|
}()
|
|
// Try to see if a guest has already been found
|
|
if m.guest != nil {
|
|
return m.guest, nil
|
|
}
|
|
|
|
// Check if a guest is provided by the Vagrantfile. If it is, then try
|
|
// to use that guest
|
|
// TODO: This check maybe needs to get reworked when the Vagrantfile bits
|
|
// actually start getting used
|
|
if m.target.Configuration.ConfigVm.Guest != "" {
|
|
// Ignore error about guest not being found - will also try detecting the guest
|
|
guest, cerr := m.project.basis.component(
|
|
m.ctx, component.GuestType, m.target.Configuration.ConfigVm.Guest)
|
|
if cerr != nil {
|
|
return nil, cerr
|
|
}
|
|
if guest != nil {
|
|
m.guest = guest.Value.(core.Guest)
|
|
m.seedPlugin(m.guest)
|
|
g = m.guest
|
|
return
|
|
}
|
|
}
|
|
|
|
// Try to detect a guest
|
|
guests, err := m.project.basis.typeComponents(m.ctx, component.GuestType)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
names := make([]string, 0, len(guests))
|
|
pcount := map[string]int{}
|
|
|
|
for name, g := range guests {
|
|
names = append(names, name)
|
|
pcount[name] = g.plugin.ParentCount()
|
|
}
|
|
|
|
sort.Slice(names, func(i, j int) bool {
|
|
in := names[i]
|
|
jn := names[j]
|
|
// TODO check values exist before use
|
|
return pcount[in] > pcount[jn]
|
|
})
|
|
|
|
for _, name := range names {
|
|
guest := guests[name].Value.(core.Guest)
|
|
detected, err := guest.Detect(m.toTarget())
|
|
if err != nil {
|
|
m.logger.Error("guest error on detection check",
|
|
"plugin", name,
|
|
"type", "Guest",
|
|
"error", err)
|
|
|
|
continue
|
|
}
|
|
if detected {
|
|
m.logger.Info("guest detection complete",
|
|
"name", name,
|
|
)
|
|
m.seedPlugin(guest)
|
|
m.guest = guest
|
|
return guest, nil
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("failed to detect guest plugin for current platform")
|
|
}
|
|
|
|
func (m *Machine) seedPlugin(plg interface{}) (err error) {
|
|
if s, ok := plg.(core.Seeder); ok {
|
|
seeds, err := s.Seeds()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
seeds.Typed = append(seeds.Typed, m.Target)
|
|
if err = s.Seed(seeds); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (m *Machine) Inspect() (printable string, err error) {
|
|
name, err := m.Name()
|
|
provider, err := m.Provider()
|
|
printable = "#<" + reflect.TypeOf(m).String() + ": " + name + " (" + reflect.TypeOf(provider).String() + ")>"
|
|
return
|
|
}
|
|
|
|
// Reload implements core.Machine
|
|
func (m *Machine) Reload() (err error) {
|
|
// TODO
|
|
return
|
|
}
|
|
|
|
// ConnectionInfo implements core.Machine
|
|
func (m *Machine) ConnectionInfo() (info *core.ConnectionInfo, err error) {
|
|
// TODO: need Vagrantfile
|
|
return
|
|
}
|
|
|
|
// MachineState implements core.Machine
|
|
func (m *Machine) MachineState() (state *core.MachineState, err error) {
|
|
var result core.MachineState
|
|
return &result, mapstructure.Decode(m.machine.State, &result)
|
|
}
|
|
|
|
// SetMachineState implements core.Machine
|
|
func (m *Machine) SetMachineState(state *core.MachineState) (err error) {
|
|
var st *vagrant_plugin_sdk.Args_Target_Machine_State
|
|
mapstructure.Decode(state, &st)
|
|
m.machine.State = st
|
|
|
|
switch st.Id {
|
|
case "not_created":
|
|
m.target.State = vagrant_server.Operation_UNKNOWN
|
|
case "running":
|
|
m.target.State = vagrant_server.Operation_CREATED
|
|
case "poweroff":
|
|
m.target.State = vagrant_server.Operation_DESTROYED
|
|
case "pending":
|
|
m.target.State = vagrant_server.Operation_PENDING
|
|
default:
|
|
m.target.State = vagrant_server.Operation_UNKNOWN
|
|
}
|
|
|
|
return m.SaveMachine()
|
|
}
|
|
|
|
func (m *Machine) UID() (userId string, err error) {
|
|
return m.machine.Uid, nil
|
|
}
|
|
|
|
// SyncedFolders implements core.Machine
|
|
func (m *Machine) SyncedFolders() (folders []*core.MachineSyncedFolder, err error) {
|
|
config := m.target.Configuration
|
|
machineConfig := config.ConfigVm
|
|
syncedFolders := machineConfig.SyncedFolders
|
|
|
|
folders = []*core.MachineSyncedFolder{}
|
|
for _, folder := range syncedFolders {
|
|
if folder.Type == nil {
|
|
// TODO: get default synced folder type
|
|
defaultType := "virtualbox"
|
|
folder.Type = &defaultType
|
|
}
|
|
plg, err := m.project.basis.component(m.ctx, component.SyncedFolderType, *folder.Type)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if s, ok := plg.Value.(core.Seeder); ok {
|
|
if err = m.seedWithMachine(s); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
m.logger.Error("synced folder plugin does not implement seeder interface")
|
|
return nil, fmt.Errorf("cannot seed synced folder plugin")
|
|
}
|
|
|
|
var f *core.Folder
|
|
mapstructure.Decode(folder, &f)
|
|
folders = append(folders, &core.MachineSyncedFolder{
|
|
Plugin: plg.Value.(core.SyncedFolder),
|
|
Folder: f,
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
func (m *Machine) SaveMachine() (err error) {
|
|
m.logger.Debug("saving machine to db", "machine", m.machine.Id)
|
|
// Update the target record and uuid to match the machine's new state
|
|
m.target.Record, err = anypb.New(m.machine)
|
|
m.target.Uuid = m.machine.Id
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return m.Save()
|
|
}
|
|
|
|
func (m *Machine) toTarget() core.Target {
|
|
return m
|
|
}
|
|
|
|
func (m *Machine) seedWithMachine(s core.Seeder) error {
|
|
m.logger.Debug("seeding machine into plugin",
|
|
"plugin", hclog.Fmt("%T", s),
|
|
)
|
|
seeds, err := s.Seeds()
|
|
if err != nil {
|
|
m.logger.Error("failed to fetch seeds from plugin",
|
|
"plugin", hclog.Fmt("%T", s),
|
|
"error", err,
|
|
)
|
|
|
|
return err
|
|
}
|
|
|
|
for _, t := range seeds.Typed {
|
|
sm, ok := t.(*Machine)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if m.target.ResourceId == sm.target.ResourceId {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
seeds.Typed = append(seeds.Typed, m)
|
|
if err = s.Seed(seeds); err != nil {
|
|
m.logger.Error("failed to seed plugin",
|
|
"plugin", hclog.Fmt("%T", s),
|
|
"error", err,
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var _ core.Machine = (*Machine)(nil)
|
|
var _ core.Target = (*Machine)(nil)
|