Start core updates to make them usable by plugins
This commit is contained in:
parent
741ab97565
commit
17829ff355
@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
"github.com/hashicorp/go-argmapper"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
@ -34,23 +36,30 @@ import (
|
||||
// finished with the basis to properly clean
|
||||
// up any open resources.
|
||||
type Basis struct {
|
||||
name string
|
||||
resourceid string
|
||||
logger hclog.Logger
|
||||
config *config.Config
|
||||
projects map[string]*Project
|
||||
factories map[component.Type]*factory.Factory
|
||||
mappers []*argmapper.Func
|
||||
dir *datadir.Basis
|
||||
env *Environment
|
||||
|
||||
labels map[string]string
|
||||
overrideLabels map[string]string
|
||||
basis *vagrant_server.Basis
|
||||
logger hclog.Logger
|
||||
config *config.Config
|
||||
projects map[string]*Project
|
||||
factories map[component.Type]*factory.Factory
|
||||
mappers []*argmapper.Func
|
||||
dir *datadir.Basis
|
||||
ctx context.Context
|
||||
|
||||
lock sync.Mutex
|
||||
client *serverclient.VagrantClient
|
||||
|
||||
// NOTE: we can't have a broker to easily just shove at the
|
||||
// mappers to help us. instead, lets update our scope defs
|
||||
// in the proto Args so they can be a stream id (when being
|
||||
// wrapped/passed by a plugin) or a connection end point. This
|
||||
// will let us serve it from here without a new process being
|
||||
// spun out, and we can wrap as needed as it gets shuttled around.
|
||||
// Will just need a direct connection end point like we had for the
|
||||
// ruby side before the broker there, and the mapper can just use
|
||||
// what ever is available when doing setup.
|
||||
grpcServer *grpc.Server
|
||||
|
||||
jobInfo *component.JobInfo
|
||||
lock sync.Mutex
|
||||
closers []func() error
|
||||
UI terminal.UI
|
||||
}
|
||||
@ -58,24 +67,43 @@ type Basis struct {
|
||||
// NewBasis creates a new Basis with the given options.
|
||||
func NewBasis(ctx context.Context, opts ...BasisOption) (b *Basis, err error) {
|
||||
b = &Basis{
|
||||
ctx: ctx,
|
||||
logger: hclog.L(),
|
||||
jobInfo: &component.JobInfo{},
|
||||
factories: plugin.BaseFactories,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
if oerr := opt(b); oerr != nil {
|
||||
err = multierror.Append(err, oerr)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if b.basis == nil {
|
||||
return nil, errors.New("basis data was not properly loaded")
|
||||
}
|
||||
|
||||
// Client is required to be provided
|
||||
if b.client == nil {
|
||||
return nil, errors.New("client was not provided to basis")
|
||||
}
|
||||
|
||||
// If we don't have a data directory set, lets do that now
|
||||
// TODO(spox): actually do that
|
||||
if b.dir == nil {
|
||||
return nil, fmt.Errorf("WithDataDir must be specified")
|
||||
}
|
||||
|
||||
// If no UI was provided, initialize a console UI
|
||||
if b.UI == nil {
|
||||
b.UI = terminal.ConsoleUI(ctx)
|
||||
}
|
||||
|
||||
// If the mappers aren't already set, load known mappers
|
||||
if len(b.mappers) == 0 {
|
||||
b.mappers, err = argmapper.NewFuncList(protomappers.All,
|
||||
argmapper.Logger(b.logger),
|
||||
@ -84,28 +112,29 @@ func NewBasis(ctx context.Context, opts ...BasisOption) (b *Basis, err error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
envMapper, _ := argmapper.NewFunc(EnvironmentProto)
|
||||
b.mappers = append(b.mappers, envMapper)
|
||||
|
||||
comandArgMapper, _ := argmapper.NewFunc(CommandArgToMap)
|
||||
b.mappers = append(b.mappers, comandArgMapper)
|
||||
|
||||
if b.client == nil {
|
||||
panic("b.client should never be nil")
|
||||
}
|
||||
|
||||
// TODO(spox): After fixing up datadir, use that to do
|
||||
// configuration loading
|
||||
if b.config == nil {
|
||||
b.config, err = config.Load("", "")
|
||||
if b.config, err = config.Load("", ""); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b.env, err = NewEnvironment(ctx,
|
||||
WithHomePath(b.dir.Dir.RootDir()),
|
||||
WithServerAddr(b.client.ServerTarget()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Finally, init a server to make this basis available
|
||||
b.grpcServer = grpc.NewServer([]grpc.ServerOption{}...)
|
||||
vagrant_plugin_sdk.RegisterTargetServiceServer(b.grpcServer, b)
|
||||
|
||||
// Close down the local server when finished with the basis
|
||||
b.Closer(func() error {
|
||||
b.grpcServer.GracefulStop()
|
||||
return nil
|
||||
})
|
||||
|
||||
// Ensure any modifications to the basis are persisted
|
||||
b.Closer(func() error { b.Save() })
|
||||
|
||||
b.logger.Info("basis initialized")
|
||||
return
|
||||
@ -116,12 +145,28 @@ func (b *Basis) Ui() terminal.UI {
|
||||
}
|
||||
|
||||
func (b *Basis) Ref() interface{} {
|
||||
return &vagrant_server.Ref_Basis{
|
||||
ResourceId: b.resourceid,
|
||||
Name: b.name,
|
||||
return &vagrant_plugin_sdk.Ref_Basis{
|
||||
ResourceId: b.ResourceId(),
|
||||
Name: b.Name(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basis) Name() string {
|
||||
if b.basis == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return b.basis.Name
|
||||
}
|
||||
|
||||
func (b *Basis) ResourceId() string {
|
||||
if b.basis == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return b.basis.ResourceId
|
||||
}
|
||||
|
||||
func (b *Basis) JobInfo() *component.JobInfo {
|
||||
return b.jobInfo
|
||||
}
|
||||
@ -130,10 +175,6 @@ func (b *Basis) Client() *serverclient.VagrantClient {
|
||||
return b.client
|
||||
}
|
||||
|
||||
func (b *Basis) Environment() *Environment {
|
||||
return b.env
|
||||
}
|
||||
|
||||
func (b *Basis) Init() (result *vagrant_server.Job_InitResult, err error) {
|
||||
b.logger.Debug("running init for basis")
|
||||
f := b.factories[component.CommandType]
|
||||
@ -153,6 +194,7 @@ func (b *Basis) Init() (result *vagrant_server.Job_InitResult, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(spox): Update this to use sdk core interface (against server so manual map required)
|
||||
raw, err := b.callDynamicFunc(
|
||||
ctx,
|
||||
b.logger,
|
||||
@ -179,67 +221,125 @@ func (b *Basis) Init() (result *vagrant_server.Job_InitResult, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Basis) LoadProject(ctx context.Context, popts ...ProjectOption) (p *Project, err error) {
|
||||
func (b *Basis) Project(nameOrId string) *Project {
|
||||
if p, ok := b.projects[nameOrId]; ok {
|
||||
return p
|
||||
}
|
||||
for _, p := range b.projects {
|
||||
if p.project.ResourceId == nameOrId {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Basis) LoadProject(popts ...ProjectOption) (p *Project, err error) {
|
||||
// Create our project
|
||||
p = &Project{
|
||||
ctx: b.ctx,
|
||||
basis: b,
|
||||
logger: b.logger.Named("project"),
|
||||
mappers: b.mappers,
|
||||
factories: b.factories,
|
||||
machines: map[string]*Machine{},
|
||||
targets: map[string]*Target{},
|
||||
UI: b.UI,
|
||||
env: b.env,
|
||||
}
|
||||
var opts options
|
||||
|
||||
// Apply any options provided
|
||||
for _, opt := range popts {
|
||||
opt(p, &opts)
|
||||
if oerr := opt(p); oerr != nil {
|
||||
err = multierror.Append(err, oerr)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If we already have this project setup, use it instead
|
||||
if project := b.Project(p.project.ResourceId); project != nil {
|
||||
return project, nil
|
||||
}
|
||||
|
||||
// Ensure project directory is set
|
||||
if p.dir == nil {
|
||||
return nil, fmt.Errorf("WithProjectDataDir must be specified")
|
||||
}
|
||||
|
||||
// Validate the configuration
|
||||
if err = opts.Config.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the labels
|
||||
if errs := config.ValidateLabels(p.overrideLabels); len(errs) > 0 {
|
||||
return nil, multierror.Append(nil, errs...)
|
||||
}
|
||||
|
||||
p.labels = opts.Config.Labels
|
||||
|
||||
for _, mCfg := range opts.Config.Machines {
|
||||
var d *datadir.Machine
|
||||
if d, err = p.dir.Machine(mCfg.Name); err != nil {
|
||||
if p.dir, err = b.dir.Project(p.project.Name); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m := &Machine{
|
||||
name: mCfg.Name,
|
||||
config: mCfg,
|
||||
logger: p.logger.Named(mCfg.Name),
|
||||
dir: d,
|
||||
UI: terminal.ConsoleUI(ctx),
|
||||
// Init server to make this project available
|
||||
p.grpcServer = grpc.NewServer()
|
||||
vagrant_plugin_sdk.RegisterProjectServiceServer(p.grpcServer, p)
|
||||
|
||||
// Close down the local server when complete
|
||||
p.Closer(func() error {
|
||||
p.grpcServer.GracefulStop()
|
||||
return nil
|
||||
})
|
||||
|
||||
// Ensure any modifications to the project are persisted
|
||||
p.Closer(func() error { return p.Save() })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Basis) Closer(c func() error) {
|
||||
b.closers = append(b.closers, c)
|
||||
}
|
||||
|
||||
func (b *Basis) Close() (err error) {
|
||||
defer b.lock.Unlock()
|
||||
b.lock.Lock()
|
||||
|
||||
b.logger.Debug("closing basis", "basis", b.ResourceId())
|
||||
|
||||
// Close down any projects that were loaded
|
||||
for name, p := range b.projects {
|
||||
b.logger.Trace("closing project", "project", name)
|
||||
if cerr := p.Close(); cerr != nil {
|
||||
b.logger.Warn("error closing project", "project", name,
|
||||
"error", cerr)
|
||||
err = multierror.Append(err, cerr)
|
||||
}
|
||||
}
|
||||
|
||||
p.machines[m.name] = m
|
||||
// Call any closers that were registered locally
|
||||
for _, c := range b.closers {
|
||||
if cerr := c(); cerr != nil {
|
||||
b.logger.Warn("error executing closer", "error", cerr)
|
||||
err = multierror.Append(err, cerr)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Basis) Close() error {
|
||||
for _, c := range b.closers {
|
||||
c()
|
||||
// Saves the basis to the db
|
||||
func (b *Basis) Save() (err error) {
|
||||
b.logger.Debug("saving basis to db", "basis", b.ResourceId())
|
||||
_, err := b.Client().UpsertBasis(b.ctx, &vagrant_server.UpsertBasisRequest{
|
||||
Basis: b.basis})
|
||||
if err != nil {
|
||||
b.logger.Trace("failed to save basis", "basis", b.ResourceId(), "error", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
// Saves the basis to the db as well as any projects that have been loaded
|
||||
func (b *Basis) SaveFull() (err error) {
|
||||
b.logger.Debug("performing full save", "basis", b.ResourceId())
|
||||
for _, p := range b.projects {
|
||||
b.logger.Trace("saving project", "basis", b.ResourceId(), "project", p.ResourceId())
|
||||
if perr := p.SaveFull(); perr != nil {
|
||||
b.logger.Trace("error while saving project", "project", p.ResourceId(), "error", err)
|
||||
err = multierror.Append(err, perr)
|
||||
}
|
||||
}
|
||||
if berr := b.Save(); berr != nil {
|
||||
err = multierror.Append(err, berr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Basis) Components(ctx context.Context) ([]*Component, error) {
|
||||
@ -309,7 +409,7 @@ func (b *Basis) specializeComponent(c *Component) (cmp plugin.PluginMetadata, er
|
||||
if cmp, ok = c.Value.(plugin.PluginMetadata); !ok {
|
||||
return nil, fmt.Errorf("component does not support specialization")
|
||||
}
|
||||
cmp.SetRequestMetadata("basis_resource_id", b.resourceid)
|
||||
cmp.SetRequestMetadata("basis_resource_id", b.ResourceId())
|
||||
cmp.SetRequestMetadata("vagrant_service_endpoint", b.client.ServerTarget())
|
||||
|
||||
return
|
||||
@ -409,7 +509,6 @@ func (b *Basis) callDynamicFunc(
|
||||
// Make sure we have access to our context and logger and default args
|
||||
args = append(args,
|
||||
argmapper.Typed(ctx, log),
|
||||
argmapper.Named("labels", &component.LabelSet{Labels: c.labels}),
|
||||
)
|
||||
|
||||
// Build the chain and call it
|
||||
@ -437,18 +536,6 @@ func (b *Basis) callDynamicFunc(
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (b *Basis) mergeLabels(ls ...map[string]string) map[string]string {
|
||||
result := map[string]string{}
|
||||
|
||||
// Merge order
|
||||
mergeOrder := []map[string]string{result, b.labels}
|
||||
mergeOrder = append(mergeOrder, ls...)
|
||||
mergeOrder = append(mergeOrder, b.overrideLabels)
|
||||
|
||||
// Merge them
|
||||
return labelsMerge(mergeOrder...)
|
||||
}
|
||||
|
||||
func (b *Basis) execHook(ctx context.Context, log hclog.Logger, h *config.Hook) error {
|
||||
return execHook(ctx, b, log, h)
|
||||
}
|
||||
@ -458,61 +545,87 @@ func (b *Basis) doOperation(ctx context.Context, log hclog.Logger, op operation)
|
||||
}
|
||||
|
||||
// BasisOption is used to set options for NewBasis.
|
||||
type BasisOption func(*Basis)
|
||||
type BasisOption func(*Basis) error
|
||||
|
||||
// WithClient sets the API client to use.
|
||||
func WithClient(client *serverclient.VagrantClient) BasisOption {
|
||||
return func(b *Basis) {
|
||||
return func(b *Basis) (err error) {
|
||||
b.client = client
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets the logger to use with the project. If this option
|
||||
// is not provided, a default logger will be used (`hclog.L()`).
|
||||
func WithLogger(log hclog.Logger) BasisOption {
|
||||
return func(b *Basis) { b.logger = log }
|
||||
return func(b *Basis) (err error) {
|
||||
b.logger = log
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithFactory sets a factory for a component type. If this isn't set for
|
||||
// any component type, then the builtin mapper will be used.
|
||||
func WithFactory(t component.Type, f *factory.Factory) BasisOption {
|
||||
return func(b *Basis) { b.factories[t] = f }
|
||||
return func(b *Basis) (err error) {
|
||||
b.factories[t] = f
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func WithBasisConfig(c *config.Config) BasisOption {
|
||||
return func(b *Basis) { b.config = c }
|
||||
return func(b *Basis) (err error) {
|
||||
b.config = c
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithComponents sets the factories for components.
|
||||
func WithComponents(fs map[component.Type]*factory.Factory) BasisOption {
|
||||
return func(b *Basis) { b.factories = fs }
|
||||
return func(b *Basis) (err error) {
|
||||
b.factories = fs
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithMappers adds the mappers to the list of mappers.
|
||||
func WithMappers(m ...*argmapper.Func) BasisOption {
|
||||
return func(b *Basis) { b.mappers = append(b.mappers, m...) }
|
||||
return func(b *Basis) (err error) {
|
||||
b.mappers = append(b.mappers, m...)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithUI sets the UI to use. If this isn't set, a BasicUI is used.
|
||||
func WithUI(ui terminal.UI) BasisOption {
|
||||
return func(b *Basis) { b.UI = ui }
|
||||
return func(b *Basis) (err error) {
|
||||
b.UI = ui
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithJobInfo sets the base job info used for any executed operations.
|
||||
func WithJobInfo(info *component.JobInfo) BasisOption {
|
||||
return func(b *Basis) { b.jobInfo = info }
|
||||
return func(b *Basis) (err error) {
|
||||
b.jobInfo = info
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func WithBasisDataDir(dir *datadir.Basis) BasisOption {
|
||||
return func(b *Basis) { b.dir = dir }
|
||||
return func(b *Basis) (err error) {
|
||||
b.dir = dir
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func WithBasisRef(r *vagrant_server.Ref_Basis) BasisOption {
|
||||
return func(b *Basis) {
|
||||
func WithBasisRef(r *vagrant_plugin_sdk.Ref_Basis) BasisOption {
|
||||
return func(b *Basis) (err error) {
|
||||
var basis *vagrant_server.Basis
|
||||
// if we don't have a resource ID we need to upsert
|
||||
if r.ResourceId == "" {
|
||||
result, err := b.client.UpsertBasis(
|
||||
var result *vagrant_server.UpsertBasisResponse
|
||||
result, err = b.client.UpsertBasis(
|
||||
context.Background(),
|
||||
&vagrant_server.UpsertBasisRequest{
|
||||
Basis: &vagrant_server.Basis{
|
||||
@ -522,31 +635,50 @@ func WithBasisRef(r *vagrant_server.Ref_Basis) BasisOption {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic("failed to upsert basis") // TODO(spox): don't panic
|
||||
return
|
||||
}
|
||||
basis = result.Basis
|
||||
} else {
|
||||
result, err := b.client.GetBasis(
|
||||
var result *vagrant_server.GetBasisResponse
|
||||
result, err = b.client.GetBasis(
|
||||
context.Background(),
|
||||
&vagrant_server.GetBasisRequest{
|
||||
Basis: r,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic("failed to retrieve basis") // TODO(spox): don't panic
|
||||
return
|
||||
}
|
||||
basis = result.Basis
|
||||
}
|
||||
b.name = basis.Name
|
||||
b.resourceid = basis.ResourceId
|
||||
b.basis = basis
|
||||
// if the datadir isn't set, do that now
|
||||
if b.dir == nil {
|
||||
var err error
|
||||
b.dir, err = datadir.NewBasis(basis.Path)
|
||||
if err != nil {
|
||||
panic("failed to setup basis datadir") // TODO(spox): don't panic
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func WithBasisResourceId(rid string) BasisOption {
|
||||
return func(b *Basis) (err error) {
|
||||
result, err := b.client.FindBasis(b.ctx, &vagrant_server.FindBasisRequest{
|
||||
Basis: &vagrant_server.Basis{
|
||||
ResourceId: rid,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !result.Found {
|
||||
b.logger.Error("failed to locate basis during setup", "resource-id", rid)
|
||||
return errors.New("requested basis is not found")
|
||||
}
|
||||
b.basis = result.Basis
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,6 @@ type scope interface {
|
||||
JobInfo() *component.JobInfo
|
||||
Client() *serverclient.VagrantClient
|
||||
execHook(ctx context.Context, log hclog.Logger, h *config.Hook) (err error)
|
||||
mergeLabels(...map[string]string) map[string]string
|
||||
}
|
||||
|
||||
// operation is a private interface that we implement for "operations" such
|
||||
|
||||
@ -2,19 +2,22 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/hashicorp/go-argmapper"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/component"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/datadir"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/terminal"
|
||||
|
||||
"github.com/hashicorp/vagrant/internal/config"
|
||||
@ -29,122 +32,100 @@ import (
|
||||
// The Close function should be called when finished with the project
|
||||
// to properly clean up any open resources.
|
||||
type Project struct {
|
||||
project *vagrant_server.Project
|
||||
ctx context.Context
|
||||
basis *Basis
|
||||
config *config.Project
|
||||
logger hclog.Logger
|
||||
machines map[string]*Machine
|
||||
targets map[string]*Target
|
||||
factories map[component.Type]*factory.Factory
|
||||
dir *datadir.Project
|
||||
mappers []*argmapper.Func
|
||||
env *Environment
|
||||
|
||||
// name is the name of the project
|
||||
name string
|
||||
|
||||
// path is the location of the project
|
||||
path string
|
||||
|
||||
// resourceid is the unique identifier of the project
|
||||
resourceid string
|
||||
|
||||
// labels is the list of labels that are assigned to this project.
|
||||
labels map[string]string
|
||||
grpcServer *grpc.Server
|
||||
|
||||
// jobInfo is the base job info for executed functions.
|
||||
jobInfo *component.JobInfo
|
||||
|
||||
// This lock only needs to be held currently to protect localClosers.
|
||||
// This lock only needs to be held currently to protect closers.
|
||||
lock sync.Mutex
|
||||
|
||||
// The below are resources we need to close when Close is called, if non-nil
|
||||
localClosers []io.Closer
|
||||
closers []func() error
|
||||
|
||||
// UI is the terminal UI to use for messages related to the project
|
||||
// as a whole. These messages will show up unprefixed for example compared
|
||||
// to the app-specific UI.
|
||||
UI terminal.UI
|
||||
|
||||
// overrideLabels are the labels specified via the CLI to override
|
||||
// all other conflicting keys.
|
||||
overrideLabels map[string]string
|
||||
}
|
||||
|
||||
func (p *Project) Ui() terminal.UI {
|
||||
return p.UI
|
||||
}
|
||||
|
||||
func (p *Project) Name() string {
|
||||
return p.project.Name
|
||||
}
|
||||
|
||||
func (p *Project) ResourceId() string {
|
||||
return p.project.ResourceId
|
||||
}
|
||||
|
||||
func (p *Project) JobInfo() *component.JobInfo {
|
||||
return p.jobInfo
|
||||
}
|
||||
|
||||
func (p *Project) Environment() *Environment {
|
||||
return p.env
|
||||
func (p *Project) Target(nameOrId string) *Target {
|
||||
if t, ok := p.targets[nameOrId]; ok {
|
||||
return t
|
||||
}
|
||||
for _, t := range p.targets {
|
||||
if t.target.ResourceId == nameOrId {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) MachineFromRef(r *vagrant_server.Ref_Machine) (*Machine, error) {
|
||||
var machine *vagrant_server.Machine
|
||||
if r.ResourceId != "" {
|
||||
result, err := p.Client().GetMachine(
|
||||
context.Background(),
|
||||
&vagrant_server.GetMachineRequest{
|
||||
Project: p.Ref().(*vagrant_server.Ref_Project),
|
||||
Machine: r,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
machine = result.Machine
|
||||
} else {
|
||||
result, err := p.Client().UpsertMachine(
|
||||
context.Background(),
|
||||
&vagrant_server.UpsertMachineRequest{
|
||||
Project: p.Ref().(*vagrant_server.Ref_Project),
|
||||
Machine: &vagrant_server.Machine{
|
||||
Name: r.Name,
|
||||
Project: p.Ref().(*vagrant_server.Ref_Project),
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
machine = result.Machine
|
||||
func (p *Project) LoadTarget(topts ...TargetOption) (t *Target, err error) {
|
||||
// Create our target
|
||||
t = &Target{
|
||||
ctx: p.ctx,
|
||||
project: p,
|
||||
logger: p.logger.Named("target"),
|
||||
UI: p.UI,
|
||||
}
|
||||
mdir, err := p.dir.Machine(machine.Name)
|
||||
|
||||
// Apply any options provided
|
||||
for _, opt := range topts {
|
||||
if oerr := opt(t); oerr != nil {
|
||||
err = multierror.Append(err, oerr)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := &Machine{
|
||||
name: machine.Name,
|
||||
resourceid: machine.ResourceId,
|
||||
project: p,
|
||||
logger: p.logger.Named(machine.Name),
|
||||
dir: mdir,
|
||||
UI: p.UI,
|
||||
return
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// App initializes and returns the machine with the given name.
|
||||
func (p *Project) Machine(name string) (*Machine, error) {
|
||||
m, ok := p.machines[name]
|
||||
if !ok {
|
||||
d, err := p.dir.Machine(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m = &Machine{
|
||||
name: name,
|
||||
project: p,
|
||||
logger: p.logger.Named(name),
|
||||
dir: d,
|
||||
UI: p.UI,
|
||||
}
|
||||
p.machines[name] = m
|
||||
// If the machine is already loaded, return that
|
||||
if target, ok := p.targets[t.Name()]; ok {
|
||||
return target, nil
|
||||
}
|
||||
return p.machines[name], nil
|
||||
|
||||
// Init server to make this target available
|
||||
t.grpcServer = grpc.NewServer()
|
||||
vagrant_plugin_sdk.RegisterTargetServiceServer(t.grpcServer, t)
|
||||
|
||||
// Close down the local server when complete
|
||||
t.Closer(func() error {
|
||||
t.grpcServer.GracefulStop()
|
||||
return nil
|
||||
})
|
||||
|
||||
// Ensure any modifications to the target are persisted
|
||||
t.Closer(func() error { return t.Save() })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Client returns the API client for the backend server.
|
||||
@ -154,10 +135,10 @@ func (p *Project) Client() *serverclient.VagrantClient {
|
||||
|
||||
// Ref returns the project ref for API calls.
|
||||
func (p *Project) Ref() interface{} {
|
||||
return &vagrant_server.Ref_Project{
|
||||
ResourceId: p.resourceid,
|
||||
Name: p.name,
|
||||
Basis: p.basis.Ref().(*vagrant_server.Ref_Basis),
|
||||
return &vagrant_plugin_sdk.Ref_Project{
|
||||
ResourceId: p.project.ResourceId,
|
||||
Name: p.project.Name,
|
||||
Basis: p.project.Basis,
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,14 +170,6 @@ func (p *Project) Components(ctx context.Context) (results []*Component, err err
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (p *Project) specializeComponent(c *Component) (cmp plugin.PluginMetadata, err error) {
|
||||
if cmp, err = p.basis.specializeComponent(c); err != nil {
|
||||
return
|
||||
}
|
||||
cmp.SetRequestMetadata("project_resource_id", p.resourceid)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Project) Run(ctx context.Context, task *vagrant_server.Task) (err error) {
|
||||
p.logger.Debug("running new task", "project", p, "task", task)
|
||||
|
||||
@ -217,8 +190,6 @@ func (p *Project) Run(ctx context.Context, task *vagrant_server.Task) (err error
|
||||
cmd,
|
||||
cmd.Value.(component.Command).ExecuteFunc(strings.Split(task.CommandName, " ")),
|
||||
argmapper.Typed(task.CliArgs),
|
||||
// TODO: add extra args here
|
||||
argmapper.Typed(p.env),
|
||||
)
|
||||
if err != nil || result == nil || result.(int64) != 0 {
|
||||
p.logger.Error("failed to execute command", "type", component.CommandType, "name", task.Component.Name, "result", result, "error", err)
|
||||
@ -228,31 +199,64 @@ func (p *Project) Run(ctx context.Context, task *vagrant_server.Task) (err error
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Project) Closer(c func() error) {
|
||||
p.closers = append(p.closers, c)
|
||||
}
|
||||
|
||||
// Close is called to clean up resources allocated by the project.
|
||||
// This should be called and blocked on to gracefully stop the project.
|
||||
func (p *Project) Close() error {
|
||||
p.lock.Lock()
|
||||
func (p *Project) Close() (err error) {
|
||||
defer p.lock.Unlock()
|
||||
p.lock.Lock()
|
||||
|
||||
p.logger.Debug("closing project", "project", p)
|
||||
|
||||
// Stop all our machines (not sure what this actually affects)
|
||||
for name, m := range p.machines {
|
||||
p.logger.Trace("closing machine", "machine", name)
|
||||
if err := m.Close(); err != nil {
|
||||
p.logger.Warn("error closing machine", "err", err)
|
||||
// close all the loaded targets
|
||||
for name, m := range p.targets {
|
||||
p.logger.Trace("closing target", "target", name)
|
||||
if cerr := m.Close(); cerr != nil {
|
||||
p.logger.Warn("error closing target", "target", name,
|
||||
"err", cerr)
|
||||
err = multierror.Append(err, cerr)
|
||||
}
|
||||
}
|
||||
|
||||
// If we're running in local mode, close our local resources we started
|
||||
for _, c := range p.localClosers {
|
||||
if err := c.Close(); err != nil {
|
||||
return err
|
||||
for _, f := range p.closers {
|
||||
if cerr := f(); cerr != nil {
|
||||
p.logger.Warn("error executing closer", "error", cerr)
|
||||
err = multierror.Append(err, cerr)
|
||||
}
|
||||
}
|
||||
p.localClosers = nil
|
||||
// Remove this project from built project list in basis
|
||||
delete(p.basis.projects, p.Name())
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
// Saves the project to the db
|
||||
func (p *Project) Save() (err error) {
|
||||
p.logger.Trace("saving project to db", "project", p.ResourceId())
|
||||
_, err := p.Client().UpsertProject(p.ctx, &vagrant_server.UpsertProjectRequest{
|
||||
Project: p.project})
|
||||
if err != nil {
|
||||
p.logger.Trace("failed to save project", "project", p.ResourceId())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Saves the project to the db as well as any targets that have been loaded
|
||||
func (p *Project) SaveFull() (err error) {
|
||||
p.logger.Debug("performing full save", "project", p.ResourceId())
|
||||
for _, t := range p.targets {
|
||||
p.logger.Trace("saving target", "project", p.ResourceId(), "target", t.ResourceId())
|
||||
if terr := t.Save(); terr != nil {
|
||||
p.logger.Trace("error while saving target", "target", t.ResourceId(), "error", err)
|
||||
err = multierror.Append(err, terr)
|
||||
}
|
||||
}
|
||||
if perr := p.Save(); perr != nil {
|
||||
err = multierror.Append(err, perr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Project) callDynamicFunc(
|
||||
@ -280,21 +284,12 @@ func (p *Project) callDynamicFunc(
|
||||
return p.basis.callDynamicFunc(ctx, log, result, c, f, args...)
|
||||
}
|
||||
|
||||
// mergeLabels merges the set of labels given. This will set the project
|
||||
// labels as a base automatically and then merge ls in order.
|
||||
func (p *Project) mergeLabels(ls ...map[string]string) map[string]string {
|
||||
result := map[string]string{}
|
||||
|
||||
// Set our builtin labels
|
||||
// result["vagrant/workspace"] = p.workspace
|
||||
|
||||
// Merge order
|
||||
mergeOrder := []map[string]string{result, p.labels}
|
||||
mergeOrder = append(mergeOrder, ls...)
|
||||
mergeOrder = append(mergeOrder, p.overrideLabels)
|
||||
|
||||
// Merge them
|
||||
return labelsMerge(mergeOrder...)
|
||||
func (p *Project) specializeComponent(c *Component) (cmp plugin.PluginMetadata, err error) {
|
||||
if cmp, err = p.basis.specializeComponent(c); err != nil {
|
||||
return
|
||||
}
|
||||
cmp.SetRequestMetadata("project_resource_id", p.ResourceId())
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Project) execHook(ctx context.Context, log hclog.Logger, h *config.Hook) error {
|
||||
@ -314,36 +309,90 @@ type options struct {
|
||||
}
|
||||
|
||||
// ProjectOption is used to set options for LoadProject
|
||||
type ProjectOption func(*Project, *options)
|
||||
|
||||
// WithConfig uses the given project configuration for initializing the
|
||||
// Project. This configuration must be validated already prior to using this
|
||||
// option.
|
||||
func WithConfig(c *config.Project) ProjectOption {
|
||||
return func(p *Project, opts *options) {
|
||||
opts.Config = c
|
||||
}
|
||||
}
|
||||
type ProjectOption func(*Project) error
|
||||
|
||||
func WithBasis(b *Basis) ProjectOption {
|
||||
return func(p *Project, opts *options) {
|
||||
return func(p *Project) (err error) {
|
||||
p.basis = b
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func WithProjectDataDir(dir *datadir.Project) ProjectOption {
|
||||
return func(p *Project, opts *options) {
|
||||
return func(p *Project) (err error) {
|
||||
p.dir = dir
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func WithProjectRef(r *vagrant_server.Ref_Project) ProjectOption {
|
||||
return func(p *Project, opts *options) {
|
||||
func WithProjectName(name string) ProjectOption {
|
||||
return func(p *Project) (err error) {
|
||||
if p.basis == nil {
|
||||
return errors.New("basis must be set before loading project")
|
||||
}
|
||||
if ex := p.basis.Project(name); ex != nil {
|
||||
p.project = ex.project
|
||||
return
|
||||
}
|
||||
|
||||
var match *vagrant_plugin_sdk.Ref_Project
|
||||
for _, m := range p.basis.basis.Projects {
|
||||
if m.Name == name {
|
||||
match = m
|
||||
break
|
||||
}
|
||||
}
|
||||
if match == nil {
|
||||
return errors.New("project is not registered in basis")
|
||||
}
|
||||
result, err := p.Client().FindProject(p.ctx, &vagrant_server.FindProjectRequest{
|
||||
Project: &vagrant_server.Project{Name: name},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !result.Found {
|
||||
p.logger.Error("failed to locate project during setup", "project", name,
|
||||
"basis", p.basis.Ref())
|
||||
return errors.New("failed to load project")
|
||||
}
|
||||
p.project = result.Project
|
||||
p.basis.projects[p.project.Name] = p
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func WithProjectRef(r *vagrant_plugin_sdk.Ref_Project) ProjectOption {
|
||||
return func(p *Project) (err error) {
|
||||
// Basis must be set before we continue
|
||||
if p.basis == nil {
|
||||
return errors.New("basis must be set before loading project")
|
||||
}
|
||||
|
||||
var project *vagrant_server.Project
|
||||
// if we don't have a resource ID we need to upsert
|
||||
if r.ResourceId == "" {
|
||||
result, err := p.Client().UpsertProject(
|
||||
context.Background(),
|
||||
// Check if the basis has already loaded the project. If so,
|
||||
// then initialize on that project
|
||||
if ex := p.basis.projects[r.Name]; ex != nil {
|
||||
project = ex.project
|
||||
return
|
||||
}
|
||||
result, err := p.Client().FindProject(p.ctx,
|
||||
&vagrant_server.FindProjectRequest{
|
||||
Project: &vagrant_server.Project{
|
||||
Name: r.Name,
|
||||
Path: r.Path,
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Found {
|
||||
project = result.Project
|
||||
} else {
|
||||
var result *vagrant_server.UpsertProjectResponse
|
||||
result, err = p.Client().UpsertProject(p.ctx,
|
||||
&vagrant_server.UpsertProjectRequest{
|
||||
Project: &vagrant_server.Project{
|
||||
Name: r.Name,
|
||||
@ -353,31 +402,20 @@ func WithProjectRef(r *vagrant_server.Ref_Project) ProjectOption {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic("failed to upsert project") // TODO(spox): don't panic
|
||||
}
|
||||
project = result.Project
|
||||
} else {
|
||||
result, err := p.Client().GetProject(
|
||||
context.Background(),
|
||||
&vagrant_server.GetProjectRequest{
|
||||
Project: r,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic("failed to retrieve project") // TODO(spox): don't panic
|
||||
return
|
||||
}
|
||||
project = result.Project
|
||||
}
|
||||
p.name = project.Name
|
||||
p.resourceid = project.ResourceId
|
||||
p.path = project.Path
|
||||
if p.dir == nil {
|
||||
var err error
|
||||
p.dir, err = datadir.NewProject(p.path + "/.vagrant")
|
||||
if err != nil {
|
||||
panic("failed to create project data dir") // TODO(spox): don't panic
|
||||
}
|
||||
// Before we init, validate basis is consistent
|
||||
if 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")
|
||||
}
|
||||
p.project = project
|
||||
// Finally set the project into the basis
|
||||
p.basis.projects[p.Name()] = p
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,36 +2,40 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/hashicorp/go-argmapper"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/vagrant/internal/config"
|
||||
"github.com/hashicorp/vagrant/internal/plugin"
|
||||
"github.com/hashicorp/vagrant/internal/serverclient"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/component"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/datadir"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/terminal"
|
||||
|
||||
"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
|
||||
)
|
||||
|
||||
type Target struct {
|
||||
name string
|
||||
resourceid string
|
||||
project *Project
|
||||
logger hclog.Logger
|
||||
config *config.Target
|
||||
dir *datadir.Target
|
||||
ctx context.Context
|
||||
target *vagrant_server.Target
|
||||
project *Project
|
||||
logger hclog.Logger
|
||||
config *config.Target
|
||||
dir *datadir.Target
|
||||
|
||||
labels map[string]string
|
||||
overrideLabels map[string]string
|
||||
|
||||
jobInfo *component.JobInfo
|
||||
UI terminal.UI
|
||||
grpcServer *grpc.Server
|
||||
lock sync.Mutex
|
||||
jobInfo *component.JobInfo
|
||||
closers []func() error
|
||||
UI terminal.UI
|
||||
}
|
||||
|
||||
func (t *Target) Ui() terminal.UI {
|
||||
@ -40,53 +44,79 @@ func (t *Target) Ui() terminal.UI {
|
||||
|
||||
func (t *Target) Ref() interface{} {
|
||||
return &vagrant_plugin_sdk.Ref_Target{
|
||||
ResourceId: m.resourceid,
|
||||
Name: m.name,
|
||||
Project: m.project.Ref().(*vagrant_server.Ref_Project),
|
||||
ResourceId: t.target.ResourceId,
|
||||
Project: t.target.Project,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Machine) JobInfo() *component.JobInfo {
|
||||
return m.jobInfo
|
||||
func (t *Target) Name() string {
|
||||
return t.target.Name
|
||||
}
|
||||
|
||||
func (m *Machine) Client() *serverclient.VagrantClient {
|
||||
return m.project.basis.client
|
||||
func (t *Target) ResourceId() string {
|
||||
return t.target.ResourceId
|
||||
}
|
||||
|
||||
func (m *Machine) Close() (err error) {
|
||||
return
|
||||
func (t *Target) JobInfo() *component.JobInfo {
|
||||
return t.jobInfo
|
||||
}
|
||||
|
||||
func (m *Machine) specializeComponent(c *Component) (cmp plugin.PluginMetadata, err error) {
|
||||
if cmp, err = m.project.specializeComponent(c); err != nil {
|
||||
return
|
||||
func (t *Target) Client() *serverclient.VagrantClient {
|
||||
return t.project.basis.client
|
||||
}
|
||||
|
||||
func (t *Target) Closer(c func() error) {
|
||||
t.closers = append(t.closers, c)
|
||||
}
|
||||
|
||||
func (t *Target) Close() (err error) {
|
||||
defer t.lock.Unlock()
|
||||
t.lock.Lock()
|
||||
|
||||
t.logger.Debug("closing target", "target", t)
|
||||
|
||||
for _, c := range t.closers {
|
||||
if cerr := c(); cerr != nil {
|
||||
t.logger.Warn("error executing closer", "error", cerr)
|
||||
err = multierror.Append(err, cerr)
|
||||
}
|
||||
}
|
||||
cmp.SetRequestMetadata("machine_resource_id", m.resourceid)
|
||||
// Remove this target from built target list in project
|
||||
delete(t.project.targets, t.target.Name)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Machine) Run(ctx context.Context, task *vagrant_server.Task) (err error) {
|
||||
m.logger.Debug("running new task", "machine", m, "task", task)
|
||||
func (t *Target) Save() (err error) {
|
||||
t.logger.Debug("saving target to db", "target", t.ResourceId())
|
||||
_, err := t.Client().UpsertTarget(t.ctx, &vagrant_server.UpsertTargetRequest{
|
||||
Target: t.target})
|
||||
if err != nil {
|
||||
t.logger.Trace("failed to save target", "target", t.ResourceId(), "error", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
cmd, err := m.project.basis.component(
|
||||
func (t *Target) Run(ctx context.Context, task *vagrant_server.Task) (err error) {
|
||||
t.logger.Debug("running new task", "target", t, "task", task)
|
||||
|
||||
cmd, err := t.project.basis.component(
|
||||
ctx, component.CommandType, task.Component.Name)
|
||||
|
||||
if err != nil {
|
||||
m.logger.Error("failed to build requested component", "type", component.CommandType,
|
||||
t.logger.Error("failed to build requested component", "type", component.CommandType,
|
||||
"name", task.Component.Name, "error", err)
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = m.specializeComponent(cmd); err != nil {
|
||||
m.logger.Error("failed to specialize component", "type", component.CommandType,
|
||||
if _, err = t.specializeComponent(cmd); err != nil {
|
||||
t.logger.Error("failed to specialize component", "type", component.CommandType,
|
||||
"name", task.Component.Name, "error", err)
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
result, err := m.callDynamicFunc(
|
||||
result, err := t.callDynamicFunc(
|
||||
ctx,
|
||||
m.logger,
|
||||
t.logger,
|
||||
(interface{})(nil),
|
||||
cmd,
|
||||
cmd.Value.(component.Command).ExecuteFunc(strings.Split(task.CommandName, " ")),
|
||||
@ -94,15 +124,14 @@ func (m *Machine) Run(ctx context.Context, task *vagrant_server.Task) (err error
|
||||
)
|
||||
|
||||
if err != nil || result == nil || result.(int64) != 0 {
|
||||
m.logger.Error("failed to execute command", "type", component.CommandType,
|
||||
t.logger.Error("failed to execute command", "type", component.CommandType,
|
||||
"name", task.Component.Name, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Machine) callDynamicFunc(
|
||||
func (t *Target) callDynamicFunc(
|
||||
ctx context.Context,
|
||||
log hclog.Logger,
|
||||
result interface{}, // expected result type
|
||||
@ -113,37 +142,109 @@ func (m *Machine) callDynamicFunc(
|
||||
|
||||
// Be sure that the status is closed after every operation so we don't leak
|
||||
// weird output outside the normal execution.
|
||||
defer m.UI.Status().Close()
|
||||
defer t.UI.Status().Close()
|
||||
|
||||
args = append(args,
|
||||
argmapper.Typed(
|
||||
m.jobInfo,
|
||||
m.dir,
|
||||
m.UI,
|
||||
t.jobInfo,
|
||||
t.dir,
|
||||
t.UI,
|
||||
),
|
||||
)
|
||||
|
||||
return m.project.callDynamicFunc(ctx, log, result, c, f, args...)
|
||||
return t.project.callDynamicFunc(ctx, log, result, c, f, args...)
|
||||
}
|
||||
|
||||
func (m *Machine) mergeLabels(ls ...map[string]string) map[string]string {
|
||||
result := map[string]string{}
|
||||
|
||||
// Merge order
|
||||
mergeOrder := []map[string]string{result, m.labels}
|
||||
mergeOrder = append(mergeOrder, ls...)
|
||||
mergeOrder = append(mergeOrder, m.overrideLabels)
|
||||
|
||||
// Merge them
|
||||
return labelsMerge(mergeOrder...)
|
||||
func (t *Target) specializeComponent(c *Component) (cmp plugin.PluginMetadata, err error) {
|
||||
if cmp, err = t.project.specializeComponent(c); err != nil {
|
||||
return
|
||||
}
|
||||
cmp.SetRequestMetadata("target_resource_id", t.target.ResourceId)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Machine) execHook(ctx context.Context, log hclog.Logger, h *config.Hook) error {
|
||||
return execHook(ctx, m, log, h)
|
||||
func (t *Target) execHook(ctx context.Context, log hclog.Logger, h *config.Hook) error {
|
||||
return execHook(ctx, t, log, h)
|
||||
}
|
||||
|
||||
func (m *Machine) doOperation(ctx context.Context, log hclog.Logger, op operation) (interface{}, proto.Message, error) {
|
||||
return doOperation(ctx, log, m, op)
|
||||
func (t *Target) doOperation(ctx context.Context, log hclog.Logger, op operation) (interface{}, proto.Message, error) {
|
||||
return doOperation(ctx, log, t, op)
|
||||
}
|
||||
|
||||
var _ *Machine = (*Machine)(nil)
|
||||
type TargetOption func(*Target) error
|
||||
|
||||
func WithTargetName(name string) TargetOption {
|
||||
return func(t *Target) (err error) {
|
||||
if ex := t.project.Target(name); ex != nil {
|
||||
t.target = ex.target
|
||||
return
|
||||
}
|
||||
for _, target := range t.project.targets {
|
||||
if target.Name() != name {
|
||||
continue
|
||||
}
|
||||
var result *vagrant_server.GetTargetResponse
|
||||
result, err = t.Client().GetTarget(t.ctx,
|
||||
&vagrant_server.GetTargetRequest{Target: target.Ref().(*vagrant_plugin_sdk.Ref_Target)})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.target = result.Target
|
||||
t.project.targets[t.target.Name] = t
|
||||
return
|
||||
}
|
||||
return errors.New("target is not registered in project")
|
||||
}
|
||||
}
|
||||
|
||||
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 errors.New("project must be set before loading target")
|
||||
}
|
||||
|
||||
var target *vagrant_server.Target
|
||||
if ex := t.project.Target(r.Name); ex != nil {
|
||||
t.target = ex.target
|
||||
return
|
||||
}
|
||||
result, err := t.Client().FindTarget(t.ctx,
|
||||
&vagrant_server.FindTargetRequest{
|
||||
Target: &vagrant_server.Target{
|
||||
Name: r.Name,
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Found {
|
||||
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: r.Project,
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
target = result.Target
|
||||
}
|
||||
if target.Project.ResourceId != r.Project.ResourceId {
|
||||
t.logger.Error("invalid project for target", "request-project", r.Project,
|
||||
"target-project", target.Project)
|
||||
return errors.New("target project configuration is invalid")
|
||||
}
|
||||
t.target = target
|
||||
t.project.targets[t.Name()] = t
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var _ *Target = (*Target)(nil)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user