package client import ( "context" "fmt" "os" "github.com/hashicorp/go-hclog" "github.com/hashicorp/vagrant-plugin-sdk/config" "github.com/hashicorp/vagrant-plugin-sdk/helper/path" vagrant_plugin_sdk "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" "github.com/hashicorp/vagrant/internal/serverclient" ) // Project is the primary structure for interacting with a Vagrant // server as a client. The client exposes a slightly higher level of // abstraction over the server API for performing operations locally and // remotely. type Project struct { basis *Basis client *Client ctx context.Context logger hclog.Logger path path.Path project *vagrant_server.Project ui terminal.UI vagrant *serverclient.VagrantClient vagrantfile path.Path } // Finds the Vagrantfile associated with the project func (p *Project) LoadVagrantfile() error { l := p.logger.With( "basis", p.basis.basis.Name, "project", p.project.Name, "path", p.vagrantfile, ) l.Trace("attempting to load project vagrantfile") if p.vagrantfile == nil { l.Warn("project vagrantfile has not been set") return nil } // If the path does not exist, no Vagrantfile was found if _, err := os.Stat(p.vagrantfile.String()); os.IsNotExist(err) { l.Warn("project vagrantfile does not exist") return nil } raw, err := p.client.rubyRuntime.Dispense("vagrantrubyruntime") if err != nil { l.Warn("failed to load ruby runtime for vagrantfile parsing", "error", err, ) return err } vp, err := LoadVagrantfile( p.vagrantfile, l, raw.(serverclient.RubyVagrantClient)) if err != nil { return err } p.project.Configuration = vp // Push Vagrantfile updates to project result, err := p.vagrant.UpsertProject( p.ctx, &vagrant_server.UpsertProjectRequest{ Project: p.project, }, ) if err != nil { l.Error("failed to store project updates", "error", err, ) return err } p.project = result.Project return nil } func (p *Project) LoadTarget(n string) (*Target, error) { result, err := p.vagrant.FindTarget( p.ctx, &vagrant_server.FindTargetRequest{ Target: &vagrant_server.Target{ Name: n, Project: p.Ref(), }, }, ) if err != nil { return nil, err } // If the target exists, load and return if result != nil { return &Target{ client: p.client, ctx: p.ctx, logger: p.logger.Named("target"), project: p, target: result.Target, ui: p.ui, vagrant: p.vagrant, }, nil } v, err := config.DecodeVagrantfile(p.project.Configuration.Unfinalized) if err != nil { return nil, err } var vm *config.VM for _, pvm := range v.ListVMs { if true { vm = pvm break } } // If the target hasn't been defined in the Vagrantfile configuration // then we don't create it if vm == nil { return nil, fmt.Errorf("requested target '%s' is not defined", n) } s, err := config.EncodeConfiguration(vm) if err != nil { return nil, err } uresult, err := p.vagrant.UpsertTarget(p.ctx, &vagrant_server.UpsertTargetRequest{ Target: &vagrant_server.Target{ Name: n, Project: p.Ref(), }, }, ) if err != nil { return nil, err } return &Target{ client: p.client, ctx: p.ctx, logger: p.logger.Named("project"), target: uresult.Target, ui: p.ui, vagrant: p.vagrant, }, nil } func (p *Project) UI() terminal.UI { return p.ui } func (p *Project) Close() error { return p.basis.Close() } // Ref returns the raw Vagrant server API client. func (p *Project) Ref() *vagrant_plugin_sdk.Ref_Project { return &vagrant_plugin_sdk.Ref_Project{ Name: p.project.Name, ResourceId: p.project.ResourceId, Basis: p.basis.Ref(), } }