Fix commands that run without a project
Some commands like `vagrant init` and `vagrant box` should be able to run successfully without a full Project available in VAGRANT_CWD (in other words, they don't require that a valid Vagrantfile be available.) Thus far we've been assuming that a Project is available when dispatching commands, which mean that commands of this nature weren't working. Here we make the Basis available to serve as an alternative client to Vagrant::Environment::Remote such that it can be instantiated and passed through to commands. This required some changes to Environment::Remote to make its interactions with the client more defensive, but we manage to avoid needing to make any changes to the normal legacy codepaths.
This commit is contained in:
parent
61fbd0f622
commit
8f9952089a
@ -16,6 +16,7 @@ import (
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/core"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/datadir"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/helper/path"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/helper/paths"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/cacher"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/dynamic"
|
||||
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/protomappers"
|
||||
@ -201,6 +202,14 @@ func (b *Basis) Config() *vagrant_plugin_sdk.Vagrantfile_Vagrantfile {
|
||||
return b.basis.Configuration
|
||||
}
|
||||
|
||||
func (p *Basis) CWD() (path string, err error) {
|
||||
cwd, err := paths.VagrantCwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cwd.String(), nil
|
||||
}
|
||||
|
||||
// Basis UI is the "default" UI with no prefix modifications
|
||||
func (b *Basis) UI() (terminal.UI, error) {
|
||||
return b.ui, nil
|
||||
@ -211,6 +220,12 @@ func (b *Basis) DataDir() (*datadir.Basis, error) {
|
||||
return b.dir, nil
|
||||
}
|
||||
|
||||
// DefaultPrivateKey implements core.Basis
|
||||
func (b *Basis) DefaultPrivateKey() (path string, err error) {
|
||||
defaultPrivateKeyPath := b.dir.DataDir().Join("insecure_private_key")
|
||||
return defaultPrivateKeyPath.String(), nil
|
||||
}
|
||||
|
||||
// Implements core.Basis
|
||||
// Returns all the registered plugins of the types specified
|
||||
func (b *Basis) Plugins(types ...string) (plugins []*core.NamedPlugin, err error) {
|
||||
@ -275,7 +290,7 @@ func (b *Basis) State() *StateBag {
|
||||
return b.statebag.(*StateBag)
|
||||
}
|
||||
|
||||
func (b *Basis) Boxes() (bc *BoxCollection, err error) {
|
||||
func (b *Basis) Boxes() (bc core.BoxCollection, err error) {
|
||||
if b.boxCollection == nil {
|
||||
b.boxCollection = &BoxCollection{
|
||||
basis: b,
|
||||
|
||||
@ -110,8 +110,7 @@ func (p *Project) Tmp() (path string, err error) {
|
||||
|
||||
// DefaultPrivateKey implements core.Project
|
||||
func (p *Project) DefaultPrivateKey() (path string, err error) {
|
||||
defaultPrivateKeyPath := p.dir.DataDir().Join("insecure_private_key")
|
||||
return defaultPrivateKeyPath.String(), nil
|
||||
return p.basis.DefaultPrivateKey()
|
||||
}
|
||||
|
||||
// Host implements core.Project
|
||||
|
||||
@ -10,6 +10,7 @@ module Vagrant
|
||||
end
|
||||
end
|
||||
|
||||
# Client can be either a Project or a Basis
|
||||
def initialize(opts={})
|
||||
@client = opts[:client]
|
||||
if @client.nil?
|
||||
@ -23,11 +24,11 @@ module Vagrant
|
||||
opts[:ui_class] ||= UI::Remote
|
||||
|
||||
@cwd = Pathname.new(@client.cwd)
|
||||
@home_path = Pathname.new(@client.home)
|
||||
@vagrantfile_name = [@client.vagrantfile_name]
|
||||
@home_path = @client.respond_to?(:home) && Pathname.new(@client.home)
|
||||
@vagrantfile_name = @client.respond_to?(:vagrantfile_name) && [@client.vagrantfile_name]
|
||||
@ui = opts.fetch(:ui, opts[:ui_class].new(@client.ui))
|
||||
@local_data_path = Pathname.new(@client.local_data)
|
||||
@boxes_path = @home_path.join("boxes")
|
||||
@boxes_path = @home_path && @home_path.join("boxes")
|
||||
@data_dir = Pathname.new(@client.data_dir)
|
||||
@gems_path = Vagrant::Bundler.instance.plugin_gem_path
|
||||
@tmp_path = Pathname.new(@client.temp_dir)
|
||||
@ -43,7 +44,7 @@ module Vagrant
|
||||
|
||||
# TODO: aliases
|
||||
@aliases_path = Pathname.new(ENV["VAGRANT_ALIAS_FILE"]).expand_path if ENV.key?("VAGRANT_ALIAS_FILE")
|
||||
@aliases_path ||= @home_path.join("aliases")
|
||||
@aliases_path ||= @home_path && @home_path.join("aliases")
|
||||
|
||||
@default_private_key_path = Pathname.new(@client.default_private_key)
|
||||
copy_insecure_private_key
|
||||
@ -112,20 +113,26 @@ module Vagrant
|
||||
@machine_index ||= Vagrant::MachineIndex.new(client: client.target_index)
|
||||
end
|
||||
|
||||
def vagrantfile
|
||||
if @vagrantfile.nil?
|
||||
def config_loader
|
||||
return @config_loader if @config_loader
|
||||
|
||||
root_vagrantfile = nil
|
||||
if client.respond_to?(:vagrantfile_path) && client.respond_to?(:vagrantfile_name)
|
||||
path = client.vagrantfile_path
|
||||
name = client.vagrantfile_name
|
||||
full_path = path.join(name).to_s
|
||||
|
||||
config_loader = Vagrant::Config::Loader.new(
|
||||
Vagrant::Config::VERSIONS, Vagrant::Config::VERSIONS_ORDER)
|
||||
config_loader.set(:root, full_path)
|
||||
@vagrantfile = Vagrant::Vagrantfile.new(config_loader, [:root])
|
||||
root_vagrantfile = path.join(name).to_s
|
||||
end
|
||||
@vagrantfile
|
||||
@config_loader = Config::Loader.new(
|
||||
Config::VERSIONS, Config::VERSIONS_ORDER)
|
||||
@config_loader.set(:root, root_vagrantfile) if root_vagrantfile
|
||||
@config_loader
|
||||
end
|
||||
|
||||
|
||||
def vagrantfile
|
||||
# When in remote mode we don't load the "home" vagrantfile
|
||||
@vagrantfile ||= Vagrant::Vagrantfile.new(config_loader, [:root])
|
||||
end
|
||||
|
||||
def to_proto
|
||||
client.proto
|
||||
end
|
||||
|
||||
@ -350,9 +350,15 @@ module Hashicorp
|
||||
self.unmarshal_class_method = :decode
|
||||
self.service_name = 'hashicorp.vagrant.sdk.BasisService'
|
||||
|
||||
rpc :CWD, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Path
|
||||
rpc :DataDir, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::DataDir::Basis
|
||||
rpc :DefaultPrivateKey, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Path
|
||||
rpc :UI, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::TerminalUI
|
||||
rpc :Host, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Host
|
||||
rpc :Boxes, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::BoxCollection
|
||||
rpc :TargetIndex, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::TargetIndex
|
||||
rpc :Seed, ::Hashicorp::Vagrant::Sdk::Args::Seeds, ::Google::Protobuf::Empty
|
||||
rpc :Seeds, ::Google::Protobuf::Empty, ::Hashicorp::Vagrant::Sdk::Args::Seeds
|
||||
end
|
||||
|
||||
Stub = Service.rpc_stub_class
|
||||
|
||||
@ -2,6 +2,18 @@ module VagrantPlugins
|
||||
module CommandServe
|
||||
class Client
|
||||
class Basis < Client
|
||||
def boxes
|
||||
BoxCollection.load(
|
||||
client.boxes(Empty.new),
|
||||
broker: broker
|
||||
)
|
||||
end
|
||||
|
||||
def cwd
|
||||
resp = client.cwd(Empty.new)
|
||||
resp.path
|
||||
end
|
||||
|
||||
# return [Sdk::Args::DataDir::Basis]
|
||||
def data_dirs
|
||||
resp = client.data_dir(Empty.new)
|
||||
@ -13,6 +25,18 @@ module VagrantPlugins
|
||||
data_dirs.data_dir
|
||||
end
|
||||
|
||||
def default_private_key
|
||||
resp = client.default_private_key(Empty.new)
|
||||
resp.path
|
||||
end
|
||||
|
||||
def target_index
|
||||
TargetIndex.load(
|
||||
client.target_index(Empty.new),
|
||||
broker: broker
|
||||
)
|
||||
end
|
||||
|
||||
# @return [Terminal]
|
||||
def ui
|
||||
begin
|
||||
@ -21,7 +45,7 @@ module VagrantPlugins
|
||||
broker: @broker,
|
||||
)
|
||||
rescue => err
|
||||
raise "Failed to load terminal via project: #{err}"
|
||||
raise "Failed to load terminal via basis: #{err}"
|
||||
end
|
||||
end
|
||||
|
||||
@ -45,6 +69,14 @@ module VagrantPlugins
|
||||
end
|
||||
plugins
|
||||
end
|
||||
|
||||
def local_data
|
||||
data_dirs.data_dir
|
||||
end
|
||||
|
||||
def temp_dir
|
||||
data_dirs.temp_dir
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -21,7 +21,7 @@ module VagrantPlugins
|
||||
funcspec(
|
||||
args: [
|
||||
SDK::Args::TerminalUI,
|
||||
SDK::Args::Project,
|
||||
SDK::Args::Basis,
|
||||
SDK::Command::Arguments,
|
||||
],
|
||||
result: SDK::Command::ExecuteResp,
|
||||
@ -32,15 +32,29 @@ module VagrantPlugins
|
||||
with_info(ctx, broker: broker) do |info|
|
||||
plugin_name = info.plugin_name
|
||||
|
||||
_, env, arguments = mapper.funcspec_map(
|
||||
ui, basis, arguments = mapper.funcspec_map(
|
||||
req.spec,
|
||||
expect: [
|
||||
Vagrant::UI::Remote,
|
||||
Vagrant::Environment,
|
||||
SDK::Args::Basis,
|
||||
Type::CommandArguments
|
||||
]
|
||||
)
|
||||
|
||||
# We need a Vagrant::Environment to pass to the command. If we got a
|
||||
# Project from seeds we can use that to get an environment.
|
||||
# Otherwise we can initialize a barebones environment from the
|
||||
# Basis we received directly from the funcspec args above.
|
||||
if @seeds && @seeds.named["project"]
|
||||
logger.debug("loading a full environment from project found in seeds")
|
||||
project = mapper.unany(@seeds.named["project"])
|
||||
env = mapper.generate(project, type: Vagrant::Environment)
|
||||
else
|
||||
logger.debug("loading a minimal environment from basis provided in args")
|
||||
client = Client::Basis.load(basis, broker: broker)
|
||||
env = Vagrant::Environment.new(ui: ui, client: client)
|
||||
end
|
||||
|
||||
plugin = Vagrant.plugin("2").local_manager.commands[plugin_name.to_sym].to_a.first
|
||||
if !plugin
|
||||
raise "Failed to locate command plugin for: #{plugin_name}"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user