vagrant-unbundled 2.2.10.0 → 2.2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +60 -0
- data/Gemfile +1 -1
- data/README.md +4 -44
- data/RELEASE.md +1 -1
- data/contrib/zsh/_vagrant +3 -1
- data/contrib/zsh/generate_zsh_completion.rb +2 -3
- data/lib/vagrant.rb +0 -4
- data/lib/vagrant/action/builder.rb +6 -15
- data/lib/vagrant/action/builtin/box_add.rb +5 -1
- data/lib/vagrant/action/builtin/cloud_init_setup.rb +10 -15
- data/lib/vagrant/action/builtin/synced_folders.rb +8 -2
- data/lib/vagrant/action/runner.rb +1 -1
- data/lib/vagrant/box.rb +8 -2
- data/lib/vagrant/box_collection.rb +1 -1
- data/lib/vagrant/bundler.rb +43 -16
- data/lib/vagrant/machine.rb +8 -5
- data/lib/vagrant/machine_index.rb +1 -0
- data/lib/vagrant/plugin/v2/command.rb +2 -1
- data/lib/vagrant/shared_helpers.rb +8 -0
- data/lib/vagrant/util/downloader.rb +3 -2
- data/lib/vagrant/util/is_port_open.rb +1 -1
- data/lib/vagrant/util/mime.rb +92 -0
- data/lib/vagrant/util/platform.rb +2 -1
- data/lib/vagrant/util/template_renderer.rb +2 -2
- data/lib/vagrant/util/uploader.rb +7 -4
- data/plugins/commands/cap/command.rb +5 -1
- data/plugins/commands/cloud/auth/login.rb +20 -23
- data/plugins/commands/cloud/auth/logout.rb +2 -10
- data/plugins/commands/cloud/auth/middleware/add_downloader_authentication.rb +57 -0
- data/plugins/commands/cloud/auth/whoami.rb +18 -20
- data/plugins/commands/cloud/box/create.rb +33 -29
- data/plugins/commands/cloud/box/delete.rb +30 -24
- data/plugins/commands/cloud/box/show.rb +41 -31
- data/plugins/commands/cloud/box/update.rb +34 -26
- data/plugins/commands/cloud/client/client.rb +50 -81
- data/plugins/commands/cloud/list.rb +3 -4
- data/plugins/commands/cloud/locales/en.yml +9 -9
- data/plugins/commands/cloud/plugin.rb +10 -0
- data/plugins/commands/cloud/provider/create.rb +38 -28
- data/plugins/commands/cloud/provider/delete.rb +39 -29
- data/plugins/commands/cloud/provider/update.rb +37 -28
- data/plugins/commands/cloud/provider/upload.rb +44 -34
- data/plugins/commands/cloud/publish.rb +185 -108
- data/plugins/commands/cloud/search.rb +34 -21
- data/plugins/commands/cloud/util.rb +266 -162
- data/plugins/commands/cloud/version/create.rb +33 -28
- data/plugins/commands/cloud/version/delete.rb +35 -28
- data/plugins/commands/cloud/version/release.rb +35 -29
- data/plugins/commands/cloud/version/revoke.rb +36 -29
- data/plugins/commands/cloud/version/update.rb +29 -25
- data/plugins/commands/login/plugin.rb +0 -13
- data/plugins/guests/arch/cap/smb.rb +1 -1
- data/plugins/guests/darwin/cap/darwin_version.rb +40 -0
- data/plugins/guests/darwin/cap/mount_smb_shared_folder.rb +1 -1
- data/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb +12 -2
- data/plugins/guests/darwin/plugin.rb +10 -0
- data/plugins/guests/debian/cap/change_host_name.rb +8 -7
- data/plugins/guests/linux/cap/mount_smb_shared_folder.rb +16 -41
- data/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb +6 -0
- data/plugins/guests/linux/cap/persist_mount_shared_folder.rb +18 -5
- data/plugins/guests/linux/cap/reboot.rb +10 -5
- data/plugins/guests/redhat/cap/change_host_name.rb +6 -2
- data/plugins/guests/suse/cap/change_host_name.rb +32 -11
- data/plugins/guests/windows/cap/reboot.rb +8 -4
- data/plugins/kernel_v2/config/cloud_init.rb +7 -0
- data/plugins/kernel_v2/config/disk.rb +1 -1
- data/plugins/kernel_v2/config/vm.rb +5 -4
- data/plugins/providers/hyperv/action.rb +1 -1
- data/plugins/providers/virtualbox/cap/mount_options.rb +1 -1
- data/plugins/providers/virtualbox/model/storage_controller_array.rb +4 -6
- data/plugins/providers/virtualbox/provider.rb +2 -1
- data/plugins/synced_folders/smb/cap/mount_options.rb +21 -1
- data/plugins/synced_folders/smb/plugin.rb +10 -0
- data/scripts/website_push_www.sh +1 -1
- data/vagrant.gemspec +5 -6
- data/version.txt +1 -1
- metadata +1202 -1595
- data/plugins/commands/login/client.rb +0 -253
- data/plugins/commands/login/command.rb +0 -137
- data/plugins/commands/login/errors.rb +0 -24
- data/plugins/commands/login/locales/en.yml +0 -49
- data/scripts/website_push_docs.sh +0 -40
@@ -5,11 +5,13 @@ module VagrantPlugins
|
|
5
5
|
module CloudCommand
|
6
6
|
module Command
|
7
7
|
class Publish < Vagrant.plugin("2", :command)
|
8
|
+
include Util
|
9
|
+
|
8
10
|
def execute
|
9
|
-
options = {}
|
11
|
+
options = {direct_upload: true}
|
10
12
|
|
11
13
|
opts = OptionParser.new do |o|
|
12
|
-
o.banner = "Usage: vagrant cloud publish [options] organization/box-name version provider-name provider-file"
|
14
|
+
o.banner = "Usage: vagrant cloud publish [options] organization/box-name version provider-name [provider-file]"
|
13
15
|
o.separator ""
|
14
16
|
o.separator "Create and release a new Vagrant Box on Vagrant Cloud"
|
15
17
|
o.separator ""
|
@@ -19,7 +21,7 @@ module VagrantPlugins
|
|
19
21
|
o.on("--box-version VERSION", String, "Version of box to create") do |v|
|
20
22
|
options[:box_version] = v
|
21
23
|
end
|
22
|
-
o.on("--url URL", String, "Remote URL to download this provider") do |u|
|
24
|
+
o.on("--url URL", String, "Remote URL to download this provider (cannot be used with provider-file)") do |u|
|
23
25
|
options[:url] = u
|
24
26
|
end
|
25
27
|
o.on("-d", "--description DESCRIPTION", String, "Full description of box") do |d|
|
@@ -28,154 +30,229 @@ module VagrantPlugins
|
|
28
30
|
o.on("--version-description DESCRIPTION", String, "Description of the version to create") do |v|
|
29
31
|
options[:version_description] = v
|
30
32
|
end
|
31
|
-
o.on("-f", "--force", "Disables confirmation to create or update box") do |f|
|
33
|
+
o.on("-f", "--[no-]force", "Disables confirmation to create or update box") do |f|
|
32
34
|
options[:force] = f
|
33
35
|
end
|
34
|
-
o.on("-p", "--private", "Makes box private") do |p|
|
36
|
+
o.on("-p", "--[no-]private", "Makes box private") do |p|
|
35
37
|
options[:private] = p
|
36
38
|
end
|
37
|
-
o.on("-r", "--release", "Releases box") do |p|
|
39
|
+
o.on("-r", "--[no-]release", "Releases box") do |p|
|
38
40
|
options[:release] = p
|
39
41
|
end
|
40
42
|
o.on("-s", "--short-description DESCRIPTION", String, "Short description of the box") do |s|
|
41
43
|
options[:short_description] = s
|
42
44
|
end
|
43
|
-
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
|
44
|
-
options[:username] = u
|
45
|
-
end
|
46
45
|
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
|
47
46
|
options[:checksum] = c
|
48
47
|
end
|
49
48
|
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
|
50
49
|
options[:checksum_type] = c
|
51
50
|
end
|
51
|
+
o.on("--[no-]direct-upload", "Upload asset directly to backend storage") do |d|
|
52
|
+
options[:direct_upload] = d
|
53
|
+
end
|
52
54
|
end
|
53
55
|
|
54
56
|
# Parse the options
|
55
57
|
argv = parse_options(opts)
|
56
58
|
return if !argv
|
57
59
|
|
58
|
-
if argv.
|
60
|
+
if argv.length < 3 || # missing required arguments
|
61
|
+
argv.length > 4 || # too many arguments
|
62
|
+
(argv.length < 4 && !options.key?(:url)) || # file argument required if url is not provided
|
63
|
+
(argv.length > 3 && options.key?(:url)) # cannot provider url and file argument
|
59
64
|
raise Vagrant::Errors::CLIInvalidUsage,
|
60
65
|
help: opts.help.chomp
|
61
66
|
end
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
box_name = box[1]
|
66
|
-
version = argv[1]
|
67
|
-
provider_name = argv[2]
|
68
|
-
box_file = argv[3]
|
68
|
+
org, box_name = argv.first.split('/', 2)
|
69
|
+
_, version, provider_name, box_file = argv
|
69
70
|
|
70
|
-
if
|
71
|
+
if box_file && !File.file?(box_file)
|
71
72
|
raise Vagrant::Errors::BoxFileNotExist,
|
72
73
|
file: box_file
|
73
74
|
end
|
74
75
|
|
75
|
-
@client =
|
76
|
+
@client = client_login(@env)
|
77
|
+
params = options.slice(:private, :release, :url, :short_description,
|
78
|
+
:description, :version_description, :checksum, :checksum_type)
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
def publish_box(org, box_name, version, provider_name, box_file, options, access_token)
|
81
|
-
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
|
82
|
-
|
83
|
-
@env.ui.warn(I18n.t("cloud_command.publish.confirm.warn"))
|
84
|
-
|
85
|
-
@env.ui.info(I18n.t("cloud_command.publish.confirm.box", org: org,
|
86
|
-
box_name: box_name, version: version, provider_name: provider_name))
|
87
|
-
@env.ui.info(I18n.t("cloud_command.publish.confirm.private")) if options[:private]
|
88
|
-
@env.ui.info(I18n.t("cloud_command.publish.confirm.release")) if options[:release]
|
89
|
-
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_url",
|
90
|
-
url: options[:url])) if options[:url]
|
91
|
-
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_description",
|
92
|
-
description: options[:description])) if options[:description]
|
93
|
-
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_short_desc",
|
94
|
-
short_description: options[:short_description])) if options[:short_description]
|
95
|
-
@env.ui.info(I18n.t("cloud_command.publish.confirm.version_desc",
|
96
|
-
version_description: options[:version_description])) if options[:version_description]
|
80
|
+
# Display output to user describing action to be taken
|
81
|
+
display_preamble(org, box_name, version, provider_name, params)
|
97
82
|
|
98
83
|
if !options[:force]
|
99
84
|
cont = @env.ui.ask(I18n.t("cloud_command.continue"))
|
100
85
|
return 1 if cont.strip.downcase != "y"
|
101
86
|
end
|
102
87
|
|
103
|
-
|
104
|
-
box =
|
105
|
-
|
106
|
-
|
107
|
-
access_token, nil, options[:checksum], options[:checksum_type])
|
108
|
-
|
109
|
-
ui = Vagrant::UI::Prefixed.new(@env.ui, "cloud")
|
110
|
-
|
111
|
-
begin
|
112
|
-
ui.info(I18n.t("cloud_command.publish.box_create"))
|
113
|
-
box.create
|
114
|
-
rescue VagrantCloud::ClientError => e
|
115
|
-
if e.error_code == 422
|
116
|
-
ui.warn(I18n.t("cloud_command.publish.update_continue", obj: "Box"))
|
117
|
-
box.update(options)
|
118
|
-
else
|
119
|
-
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
|
120
|
-
@env.ui.error(e)
|
121
|
-
return 1
|
122
|
-
end
|
123
|
-
end
|
88
|
+
# Load up all the models we'll need to publish the asset
|
89
|
+
box = load_box(org, box_name, @client.token)
|
90
|
+
box_v = load_box_version(box, version)
|
91
|
+
box_p = load_version_provider(box_v, provider_name)
|
124
92
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
rescue VagrantCloud::InvalidVersion => e
|
138
|
-
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
|
139
|
-
@env.ui.error(e)
|
140
|
-
return 1
|
93
|
+
# Update all the data
|
94
|
+
set_box_info(box, params.slice(:private, :short_description, :description))
|
95
|
+
set_version_info(box_v, params.slice(:version_description))
|
96
|
+
set_provider_info(box_p, params.slice(:checksum, :checksum_type, :url))
|
97
|
+
|
98
|
+
# Save any updated state
|
99
|
+
@env.ui.warn(I18n.t("cloud_command.publish.box_save"))
|
100
|
+
box.save
|
101
|
+
|
102
|
+
# If we have a box file asset, upload it
|
103
|
+
if box_file
|
104
|
+
upload_box_file(box_p, box_file, options.slice(:direct_upload))
|
141
105
|
end
|
142
106
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
rescue VagrantCloud::ClientError => e
|
147
|
-
if e.error_code == 422
|
148
|
-
ui.warn(I18n.t("cloud_command.publish.update_continue", obj: "Provider"))
|
149
|
-
provider.update
|
150
|
-
else
|
151
|
-
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
|
152
|
-
@env.ui.error(e)
|
153
|
-
return 1
|
154
|
-
end
|
107
|
+
# If configured to release the box, release it
|
108
|
+
if options[:release] && !box_v.released?
|
109
|
+
release_version(box_v)
|
155
110
|
end
|
156
111
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
112
|
+
# And we're done!
|
113
|
+
@env.ui.success(I18n.t("cloud_command.publish.complete", org: org, box_name: box_name))
|
114
|
+
format_box_results(box, @env)
|
115
|
+
0
|
116
|
+
rescue VagrantCloud::Error => err
|
117
|
+
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
|
118
|
+
@env.ui.error(err.message)
|
119
|
+
1
|
120
|
+
end
|
121
|
+
|
122
|
+
# Upload the file for the given box provider
|
123
|
+
#
|
124
|
+
# @param [VagrantCloud::Box::Provider] provider Vagrant Cloud box version provider
|
125
|
+
# @param [String] box_file Path to local asset for upload
|
126
|
+
# @param [Hash] options
|
127
|
+
# @option options [Boolean] :direct_upload Upload directly to backend storage
|
128
|
+
# @return [nil]
|
129
|
+
def upload_box_file(provider, box_file, options={})
|
130
|
+
box_file = File.absolute_path(box_file)
|
131
|
+
@env.ui.info(I18n.t("cloud_command.publish.upload_provider", file: box_file))
|
132
|
+
provider.upload(direct: options[:direct_upload]) do |upload_url|
|
133
|
+
Vagrant::Util::Uploader.new(upload_url, box_file, ui: @env.ui, method: :put).upload!
|
177
134
|
end
|
178
|
-
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
|
138
|
+
# Release the box version
|
139
|
+
#
|
140
|
+
# @param [VagrantCloud::Box::Version] version Vagrant Cloud box version
|
141
|
+
# @return [nil]
|
142
|
+
def release_version(version)
|
143
|
+
@env.ui.info(I18n.t("cloud_command.publish.release"))
|
144
|
+
version.release
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
|
148
|
+
# Set any box related attributes that were provided
|
149
|
+
#
|
150
|
+
# @param [VagrantCloud::Box] box Vagrant Cloud box
|
151
|
+
# @param [Hash] options
|
152
|
+
# @option options [Boolean] :private Box access is private
|
153
|
+
# @option options [String] :short_description Short description of box
|
154
|
+
# @option options [String] :description Full description of box
|
155
|
+
# @return [VagrantCloud::Box]
|
156
|
+
def set_box_info(box, options={})
|
157
|
+
box.private = options[:private] if options.key?(:private)
|
158
|
+
box.short_description = options[:short_description] if options.key?(:short_description)
|
159
|
+
box.description = options[:description] if options.key?(:description)
|
160
|
+
box
|
161
|
+
end
|
162
|
+
|
163
|
+
# Set any version related attributes that were provided
|
164
|
+
#
|
165
|
+
# @param [VagrantCloud::Box::Version] version Vagrant Cloud box version
|
166
|
+
# @param [Hash] options
|
167
|
+
# @option options [String] :version_description Description for this version
|
168
|
+
# @return [VagrantCloud::Box::Version]
|
169
|
+
def set_version_info(version, options={})
|
170
|
+
version.description = options[:version_description] if options.key?(:version_description)
|
171
|
+
version
|
172
|
+
end
|
173
|
+
|
174
|
+
# Set any provider related attributes that were provided
|
175
|
+
#
|
176
|
+
# @param [VagrantCloud::Box::Provider] provider Vagrant Cloud box version provider
|
177
|
+
# @param [Hash] options
|
178
|
+
# @option options [String] :url Remote URL for self hosted
|
179
|
+
# @option options [String] :checksum_type Type of checksum value provided
|
180
|
+
# @option options [String] :checksum Checksum of the box asset
|
181
|
+
# @return [VagrantCloud::Box::Provider]
|
182
|
+
def set_provider_info(provider, options={})
|
183
|
+
provider.url = options[:url] if options.key?(:url)
|
184
|
+
provider.checksum_type = options[:checksum_type] if options.key?(:checksum_type)
|
185
|
+
provider.checksum = options[:checksum] if options.key?(:checksum)
|
186
|
+
provider
|
187
|
+
end
|
188
|
+
|
189
|
+
# Load the requested version provider
|
190
|
+
#
|
191
|
+
# @param [VagrantCloud::Box::Version] version The version of the Vagrant Cloud box
|
192
|
+
# @param [String] provider_name Name of the provider
|
193
|
+
# @return [VagrantCloud::Box::Provider]
|
194
|
+
def load_version_provider(version, provider_name)
|
195
|
+
provider = version.providers.detect { |pv| pv.name == provider_name }
|
196
|
+
return provider if provider
|
197
|
+
version.add_provider(provider_name)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Load the requested box version
|
201
|
+
#
|
202
|
+
# @param [VagrantCloud::Box] box The Vagrant Cloud box
|
203
|
+
# @param [String] version Version of the box
|
204
|
+
# @return [VagrantCloud::Box::Version]
|
205
|
+
def load_box_version(box, version)
|
206
|
+
v = box.versions.detect { |v| v.version == version }
|
207
|
+
return v if v
|
208
|
+
box.add_version(version)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Load the requested box
|
212
|
+
#
|
213
|
+
# @param [String] org Organization name for box
|
214
|
+
# @param [String] box_name Name of the box
|
215
|
+
# @param [String] access_token User access token
|
216
|
+
# @return [VagrantCloud::Box]
|
217
|
+
def load_box(org, box_name, access_token)
|
218
|
+
account = VagrantCloud::Account.new(
|
219
|
+
custom_server: api_server_url,
|
220
|
+
access_token: access_token
|
221
|
+
)
|
222
|
+
box = account.organization(name: org).boxes.detect { |b| b.name == box_name }
|
223
|
+
return box if box
|
224
|
+
account.organization(name: org).add_box(box_name)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Display publishing information to user before starting process
|
228
|
+
#
|
229
|
+
# @param [String] org Organization name
|
230
|
+
# @param [String] box_name Name of the box to publish
|
231
|
+
# @param [String] version Version of the box to publish
|
232
|
+
# @param [String] provider_name Name of the provider being published
|
233
|
+
# @param [Hash] options
|
234
|
+
# @option options [Boolean] :private Box is private
|
235
|
+
# @option options [Boolean] :release Box should be released
|
236
|
+
# @option options [String] :url Remote URL for self-hosted boxes
|
237
|
+
# @option options [String] :description Description of the box
|
238
|
+
# @option options [String] :short_description Short description of the box
|
239
|
+
# @option options [String] :version_description Description of the box version
|
240
|
+
# @return [nil]
|
241
|
+
def display_preamble(org, box_name, version, provider_name, options={})
|
242
|
+
@env.ui.warn(I18n.t("cloud_command.publish.confirm.warn"))
|
243
|
+
@env.ui.info(I18n.t("cloud_command.publish.confirm.box", org: org,
|
244
|
+
box_name: box_name, version: version, provider_name: provider_name))
|
245
|
+
@env.ui.info(I18n.t("cloud_command.publish.confirm.private")) if options[:private]
|
246
|
+
@env.ui.info(I18n.t("cloud_command.publish.confirm.release")) if options[:release]
|
247
|
+
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_url",
|
248
|
+
url: options[:url])) if options[:url]
|
249
|
+
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_description",
|
250
|
+
description: options[:description])) if options[:description]
|
251
|
+
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_short_desc",
|
252
|
+
short_description: options[:short_description])) if options[:short_description]
|
253
|
+
@env.ui.info(I18n.t("cloud_command.publish.confirm.version_desc",
|
254
|
+
version_description: options[:version_description])) if options[:version_description]
|
255
|
+
nil
|
179
256
|
end
|
180
257
|
end
|
181
258
|
end
|
@@ -4,8 +4,10 @@ module VagrantPlugins
|
|
4
4
|
module CloudCommand
|
5
5
|
module Command
|
6
6
|
class Search < Vagrant.plugin("2", :command)
|
7
|
+
include Util
|
8
|
+
|
7
9
|
def execute
|
8
|
-
options = {}
|
10
|
+
options = {quiet: true}
|
9
11
|
|
10
12
|
opts = OptionParser.new do |o|
|
11
13
|
o.banner = "Usage: vagrant cloud search [options] query"
|
@@ -37,45 +39,56 @@ module VagrantPlugins
|
|
37
39
|
o.on("--sort-by SORT", "Field to sort results on (created, downloads, updated) Default: downloads") do |s|
|
38
40
|
options[:sort] = s
|
39
41
|
end
|
40
|
-
o.on("-
|
41
|
-
options[:
|
42
|
+
o.on("--[no-]auth", "Authenticate with Vagrant Cloud if required before searching") do |l|
|
43
|
+
options[:quiet] = !l
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
45
47
|
# Parse the options
|
46
48
|
argv = parse_options(opts)
|
47
49
|
return if !argv
|
48
|
-
if argv.length
|
50
|
+
if argv.length != 1
|
49
51
|
raise Vagrant::Errors::CLIInvalidUsage,
|
50
52
|
help: opts.help.chomp
|
51
53
|
end
|
52
54
|
|
53
|
-
@client =
|
55
|
+
@client = client_login(@env, options.slice(:quiet))
|
54
56
|
query = argv.first
|
55
57
|
|
56
58
|
options[:limit] = 25 if !(options[:limit].to_i < 1) && !options[:limit]
|
57
59
|
|
58
|
-
search(query,
|
60
|
+
search(query, @client&.token, options)
|
59
61
|
end
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
# Perform requested search and display results to user
|
64
|
+
#
|
65
|
+
# @param [String] query Search query string
|
66
|
+
# @param [Hash] options
|
67
|
+
# @option options [String] :provider Filter by provider
|
68
|
+
# @option options [String] :sort Field to sort results
|
69
|
+
# @option options [Integer] :limit Number of results to display
|
70
|
+
# @option options [Integer] :page Page of results to display
|
71
|
+
# @param [String] access_token User access token
|
72
|
+
# @return [Integer]
|
73
|
+
def search(query, access_token, options={})
|
74
|
+
account = VagrantCloud::Account.new(
|
75
|
+
custom_server: api_server_url,
|
76
|
+
access_token: access_token
|
77
|
+
)
|
78
|
+
params = {query: query}.merge(options.slice(:provider, :sort, :order, :limit, :page))
|
79
|
+
result = account.searcher.search(**params)
|
64
80
|
|
65
|
-
|
66
|
-
|
67
|
-
if !search_results["boxes"].empty?
|
68
|
-
VagrantPlugins::CloudCommand::Util.format_search_results(search_results["boxes"], options[:short], options[:json], @env)
|
69
|
-
else
|
70
|
-
@env.ui.warn(I18n.t("cloud_command.search.no_results", query: query))
|
71
|
-
end
|
81
|
+
if result.boxes.empty?
|
82
|
+
@env.ui.warn(I18n.t("cloud_command.search.no_results", query: query))
|
72
83
|
return 0
|
73
|
-
rescue VagrantCloud::ClientError => e
|
74
|
-
@env.ui.error(I18n.t("cloud_command.errors.search.fail"))
|
75
|
-
@env.ui.error(e)
|
76
|
-
return 1
|
77
84
|
end
|
78
|
-
|
85
|
+
|
86
|
+
format_search_results(result.boxes, options[:short], options[:json], @env)
|
87
|
+
0
|
88
|
+
rescue VagrantCloud::Error => e
|
89
|
+
@env.ui.error(I18n.t("cloud_command.errors.search.fail"))
|
90
|
+
@env.ui.error(e.message)
|
91
|
+
1
|
79
92
|
end
|
80
93
|
end
|
81
94
|
end
|
@@ -1,205 +1,309 @@
|
|
1
1
|
module VagrantPlugins
|
2
2
|
module CloudCommand
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
if !defined?(@_account)
|
11
|
-
@_account = VagrantCloud::Account.new(username, access_token, vagrant_cloud_server)
|
12
|
-
end
|
13
|
-
@_account
|
14
|
-
end
|
15
|
-
|
16
|
-
def api_server_url
|
17
|
-
if Vagrant.server_url == Vagrant::DEFAULT_SERVER_URL
|
18
|
-
return "#{Vagrant.server_url}/api/v1"
|
19
|
-
else
|
20
|
-
return Vagrant.server_url
|
21
|
-
end
|
3
|
+
module Util
|
4
|
+
# @return [String] Vagrant Cloud server URL
|
5
|
+
def api_server_url
|
6
|
+
if Vagrant.server_url == Vagrant::DEFAULT_SERVER_URL
|
7
|
+
return "#{Vagrant.server_url}/api/v1"
|
8
|
+
else
|
9
|
+
return Vagrant.server_url
|
22
10
|
end
|
11
|
+
end
|
23
12
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
13
|
+
# @param [Vagrant::Environment] env
|
14
|
+
# @param [Hash] options
|
15
|
+
# @option options [String] :login Username or email
|
16
|
+
# @option options [String] :description Description of login usage for token
|
17
|
+
# @option options [String] :code 2FA code for login
|
18
|
+
# @option options [Boolean] :quiet Do not prompt user
|
19
|
+
# @returns [VagrantPlugins::CloudCommand::Client, nil]
|
20
|
+
def client_login(env, options={})
|
21
|
+
return @_client if defined?(@_client)
|
22
|
+
@_client = Client.new(env)
|
23
|
+
return @_client if @_client.logged_in?
|
31
24
|
|
32
|
-
|
33
|
-
|
25
|
+
# If directed to be quiet, do not continue and
|
26
|
+
# just return nil
|
27
|
+
return if options[:quiet]
|
34
28
|
|
35
|
-
|
36
|
-
|
37
|
-
env.ui.output("Vagrant Cloud URL: #{Vagrant.server_url}")
|
38
|
-
end
|
29
|
+
# Let the user know what is going on.
|
30
|
+
env.ui.output(I18n.t("cloud_command.command_header") + "\n")
|
39
31
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
env.ui.output("Vagrant Cloud username or email: #{@_client.username_or_email}")
|
45
|
-
else
|
46
|
-
@_client.username_or_email = env.ui.ask("Vagrant Cloud username or email: ")
|
47
|
-
end
|
32
|
+
# If it is a private cloud installation, show that
|
33
|
+
if Vagrant.server_url != Vagrant::DEFAULT_SERVER_URL
|
34
|
+
env.ui.output("Vagrant Cloud URL: #{Vagrant.server_url}")
|
35
|
+
end
|
48
36
|
|
49
|
-
|
37
|
+
options = {} if !options
|
38
|
+
# Ask for the username
|
39
|
+
if options[:login]
|
40
|
+
@_client.username_or_email = options[:login]
|
41
|
+
env.ui.output("Vagrant Cloud username or email: #{@_client.username_or_email}")
|
42
|
+
else
|
43
|
+
@_client.username_or_email = env.ui.ask("Vagrant Cloud username or email: ")
|
44
|
+
end
|
50
45
|
|
51
|
-
|
52
|
-
if !options[:description]
|
53
|
-
description = env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
|
54
|
-
else
|
55
|
-
description = options[:description]
|
56
|
-
env.ui.output("Token description: #{description}")
|
57
|
-
end
|
46
|
+
@_client.password = env.ui.ask("Password (will be hidden): ", echo: false)
|
58
47
|
|
59
|
-
|
48
|
+
description_default = "Vagrant login from #{Socket.gethostname}"
|
49
|
+
if !options[:description]
|
50
|
+
description = env.ui.ask("Token description (Defaults to #{description_default.inspect}): ")
|
51
|
+
else
|
52
|
+
description = options[:description]
|
53
|
+
env.ui.output("Token description: #{description}")
|
54
|
+
end
|
60
55
|
|
61
|
-
|
56
|
+
description = description_default if description.empty?
|
62
57
|
|
63
|
-
|
64
|
-
token = @_client.login(description: description, code: code)
|
65
|
-
rescue Errors::TwoFactorRequired
|
66
|
-
until code
|
67
|
-
code = env.ui.ask("2FA code: ")
|
58
|
+
code = nil
|
68
59
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
60
|
+
begin
|
61
|
+
token = @_client.login(description: description, code: code)
|
62
|
+
rescue Errors::TwoFactorRequired
|
63
|
+
until code
|
64
|
+
code = env.ui.ask("2FA code: ")
|
74
65
|
|
75
|
-
|
66
|
+
if @_client.two_factor_delivery_methods.include?(code.downcase)
|
67
|
+
delivery_method, code = code, nil
|
68
|
+
@_client.request_code delivery_method
|
76
69
|
end
|
77
|
-
|
78
|
-
@_client.store_token(token)
|
79
|
-
Vagrant::Util::CredentialScrubber.sensitive(token)
|
80
|
-
env.ui.success(I18n.t("cloud_command.logged_in"))
|
81
|
-
@_client
|
82
70
|
end
|
83
|
-
@_client
|
84
|
-
end
|
85
71
|
|
86
|
-
|
87
|
-
# Modified from https://stackoverflow.com/a/28685559
|
88
|
-
# for printing arrays of hashes in formatted tables
|
89
|
-
# ===================================================
|
90
|
-
|
91
|
-
# @param [Vagrant::Environment] - env
|
92
|
-
# @param [Hash] - column_labels - A hash of key values for table labels (i.e. {:col1=>"COL1", :col2=>"COL2"})
|
93
|
-
# @param [Array] - results - An array of hashes
|
94
|
-
# @param [Array] - to_jrust_keys - An array of column keys that should be right justified (default is left justified for all columns)
|
95
|
-
def print_search_table(env, column_labels, results, to_rjust_keys)
|
96
|
-
columns = column_labels.each_with_object({}) { |(col,label),h|
|
97
|
-
h[col] = { label: label,
|
98
|
-
width: [results.map { |g| g[col].size }.max, label.size].max
|
99
|
-
}}
|
100
|
-
|
101
|
-
write_header(env, columns)
|
102
|
-
write_divider(env, columns)
|
103
|
-
results.each { |h| write_line(env, columns, h,to_rjust_keys) }
|
104
|
-
write_divider(env, columns)
|
72
|
+
retry
|
105
73
|
end
|
106
74
|
|
107
|
-
|
108
|
-
|
109
|
-
|
75
|
+
@_client.store_token(token)
|
76
|
+
Vagrant::Util::CredentialScrubber.sensitive(token)
|
77
|
+
env.ui.success(I18n.t("cloud_command.logged_in"))
|
78
|
+
@_client
|
79
|
+
end
|
110
80
|
|
111
|
-
|
112
|
-
|
81
|
+
# Print search results from Vagrant Cloud to the console
|
82
|
+
#
|
83
|
+
# @param [Array<VagrantCloud::Box>] search_results Box search results from Vagrant Cloud
|
84
|
+
# @param [Boolean] short Print short summary
|
85
|
+
# @param [Boolean] json Print output in JSON format
|
86
|
+
# @param [Vagrant::Environment] env Current Vagrant environment
|
87
|
+
# @return [nil]
|
88
|
+
def format_search_results(search_results, short, json, env)
|
89
|
+
result = search_results.map do |b|
|
90
|
+
{
|
91
|
+
name: b.tag,
|
92
|
+
version: b.current_version.version,
|
93
|
+
downloads: format_downloads(b.downloads.to_s),
|
94
|
+
providers: b.current_version.providers.map(&:name).join(", ")
|
95
|
+
}
|
113
96
|
end
|
114
97
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
98
|
+
if short
|
99
|
+
result.map { |b| env.ui.info(b[:name]) }
|
100
|
+
elsif json
|
101
|
+
env.ui.info(result.to_json)
|
102
|
+
else
|
103
|
+
column_labels = {}
|
104
|
+
columns = result.first.keys
|
105
|
+
columns.each do |c|
|
106
|
+
column_labels[c] = c.to_s.upcase
|
107
|
+
end
|
108
|
+
print_search_table(env, column_labels, result, [:downloads])
|
124
109
|
end
|
110
|
+
nil
|
111
|
+
end
|
125
112
|
|
126
|
-
|
127
|
-
|
113
|
+
# Output box details result from Vagrant Cloud
|
114
|
+
#
|
115
|
+
# @param [VagrantCloud::Box, VagrantCloud::Box::Version] box Box or box version to display
|
116
|
+
# @param [Vagrant::Environment] env Current Vagrant environment
|
117
|
+
# @return [nil]
|
118
|
+
def format_box_results(box, env)
|
119
|
+
if box.is_a?(VagrantCloud::Box)
|
120
|
+
info = box_info(box)
|
121
|
+
elsif box.is_a?(VagrantCloud::Box::Provider)
|
122
|
+
info = version_info(box.version)
|
123
|
+
else
|
124
|
+
info = version_info(box)
|
125
|
+
end
|
128
126
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
results.delete("description_html")
|
127
|
+
width = info.keys.map(&:size).max
|
128
|
+
info.each do |k, v|
|
129
|
+
whitespace = width - k.size
|
130
|
+
env.ui.info "#{k}: #{"".ljust(whitespace)} #{v}"
|
131
|
+
end
|
132
|
+
nil
|
133
|
+
end
|
137
134
|
|
138
|
-
|
139
|
-
|
140
|
-
|
135
|
+
# Load box and yield
|
136
|
+
#
|
137
|
+
# @param [VagrantCloud::Account] account Vagrant Cloud account
|
138
|
+
# @param [String] org Organization name
|
139
|
+
# @param [String] box Box name
|
140
|
+
# @yieldparam [VagrantCloud::Box] box Requested Vagrant Cloud box
|
141
|
+
# @yieldreturn [Integer]
|
142
|
+
# @return [Integer]
|
143
|
+
def with_box(account:, org:, box:)
|
144
|
+
org = account.organization(name: org)
|
145
|
+
b = org.boxes.detect { |b| b.name == box }
|
146
|
+
if !b
|
147
|
+
@env.ui.error(I18n.t("cloud_command.box.not_found",
|
148
|
+
org: org.username, box_name: box))
|
149
|
+
return 1
|
150
|
+
end
|
151
|
+
yield b
|
152
|
+
end
|
141
153
|
|
142
|
-
|
154
|
+
# Load box version and yield
|
155
|
+
#
|
156
|
+
# @param [VagrantCloud::Account] account Vagrant Cloud account
|
157
|
+
# @param [String] org Organization name
|
158
|
+
# @param [String] box Box name
|
159
|
+
# @param [String] version Box version
|
160
|
+
# @yieldparam [VagrantCloud::Box::Version] version Requested Vagrant Cloud box version
|
161
|
+
# @yieldreturn [Integer]
|
162
|
+
# @return [Integer]
|
163
|
+
def with_version(account:, org:, box:, version:)
|
164
|
+
with_box(account: account, org: org, box: box) do |b|
|
165
|
+
v = b.versions.detect { |v| v.version == version }
|
166
|
+
if !v
|
167
|
+
@env.ui.error(I18n.t("cloud_command.version.not_found",
|
168
|
+
box_name: box, org: org, version: version))
|
169
|
+
return 1
|
143
170
|
end
|
171
|
+
yield v
|
172
|
+
end
|
173
|
+
end
|
144
174
|
|
175
|
+
# Load box version and yield
|
176
|
+
#
|
177
|
+
# @param [VagrantCloud::Account] account Vagrant Cloud account
|
178
|
+
# @param [String] org Organization name
|
179
|
+
# @param [String] box Box name
|
180
|
+
# @param [String] version Box version
|
181
|
+
# @param [String] provider Box version provider name
|
182
|
+
# @yieldparam [VagrantCloud::Box::Provider] provider Requested Vagrant Cloud box version provider
|
183
|
+
# @yieldreturn [Integer]
|
184
|
+
# @return [Integer]
|
185
|
+
def with_provider(account:, org:, box:, version:, provider:)
|
186
|
+
with_version(account: account, org: org, box: box, version: version) do |v|
|
187
|
+
p = v.providers.detect { |p| p.name == provider }
|
188
|
+
if !p
|
189
|
+
@env.ui.error(I18n.t("cloud_command.provider.not_found",
|
190
|
+
org: org, box_name: box, version: version, provider_name: provider))
|
191
|
+
return 1
|
192
|
+
end
|
193
|
+
yield p
|
194
|
+
end
|
195
|
+
end
|
145
196
|
|
146
|
-
|
147
|
-
results.each do |k,v|
|
148
|
-
if k == "versions"
|
149
|
-
v = v.map{ |ver| ver["version"] }.join(", ")
|
150
|
-
elsif k == "current_version"
|
151
|
-
v = v["version"]
|
152
|
-
elsif k == "providers"
|
153
|
-
v = v.map{ |p| p["name"] }.join(", ")
|
154
|
-
elsif k == "downloads"
|
155
|
-
v = format_downloads(v.to_s)
|
156
|
-
end
|
197
|
+
protected
|
157
198
|
|
158
|
-
|
159
|
-
|
199
|
+
# Extract box information for display
|
200
|
+
#
|
201
|
+
# @param [VagrantCloud::Box] box Box for extracting information
|
202
|
+
# @return [Hash<String,String>]
|
203
|
+
def box_info(box)
|
204
|
+
Hash.new.tap do |i|
|
205
|
+
i["Box"] = box.tag
|
206
|
+
i["Description"] = box.description
|
207
|
+
i["Private"] = box.private ? "yes" : "no"
|
208
|
+
i["Created"] = box.created_at
|
209
|
+
i["Updated"] = box.updated_at
|
210
|
+
if !box.current_version.nil?
|
211
|
+
i["Current Version"] = box.current_version.version
|
212
|
+
else
|
213
|
+
i["Current Version"] = "N/A"
|
214
|
+
end
|
215
|
+
i["Versions"] = box.versions.slice(0, 5).map(&:version).join(", ")
|
216
|
+
if box.versions.size > 5
|
217
|
+
i["Versions"] += " ..."
|
160
218
|
end
|
219
|
+
i["Downloads"] = format_downloads(box.downloads)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Extract version information for display
|
224
|
+
#
|
225
|
+
# @param [VagrantCloud::Box::Version] version Box version for extracting information
|
226
|
+
# @return [Hash<String,String>]
|
227
|
+
def version_info(version)
|
228
|
+
Hash.new.tap do |i|
|
229
|
+
i["Box"] = version.box.tag
|
230
|
+
i["Version"] = version.version
|
231
|
+
i["Description"] = version.description
|
232
|
+
i["Status"] = version.status
|
233
|
+
i["Providers"] = version.providers.map(&:name).sort.join(", ")
|
234
|
+
i["Created"] = version.created_at
|
235
|
+
i["Updated"] = version.updated_at
|
161
236
|
end
|
237
|
+
end
|
162
238
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
239
|
+
# Print table results from search request
|
240
|
+
#
|
241
|
+
# @param [Vagrant::Environment] env Current Vagrant environment
|
242
|
+
# @param [Hash] column_labels A hash of key/value pairs for table labels (i.e. {col1: "COL1"})
|
243
|
+
# @param [Array] results An array of hashes representing search resuls
|
244
|
+
# @param [Array] to_jrust_keys - List of columns keys to right justify (left justify is defualt)
|
245
|
+
# @return [nil]
|
246
|
+
# @note Modified from https://stackoverflow.com/a/28685559
|
247
|
+
def print_search_table(env, column_labels, results, to_rjust_keys)
|
248
|
+
columns = column_labels.each_with_object({}) do |(col,label),h|
|
249
|
+
h[col] = {
|
250
|
+
label: label,
|
251
|
+
width: [results.map { |g| g[col].size }.max, label.size].max
|
252
|
+
}
|
170
253
|
end
|
171
254
|
|
255
|
+
write_header(env, columns)
|
256
|
+
write_divider(env, columns)
|
257
|
+
results.each { |h| write_line(env, columns, h, to_rjust_keys) }
|
258
|
+
write_divider(env, columns)
|
259
|
+
end
|
172
260
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
name: b["tag"],
|
183
|
-
version: b["current_version"]["version"],
|
184
|
-
downloads: format_downloads(b["downloads"].to_s),
|
185
|
-
providers: b["current_version"]["providers"].map{ |p| p["name"] }.join(",")
|
186
|
-
}
|
187
|
-
result << box
|
188
|
-
end
|
261
|
+
# Write the header for a table
|
262
|
+
#
|
263
|
+
# @param [Vagrant::Environment] env Current Vagrant environment
|
264
|
+
# @param [Array<Hash>] columns List of columns in Hash format with `:label` and `:width` keys
|
265
|
+
# @return [nil]
|
266
|
+
def write_header(env, columns)
|
267
|
+
env.ui.info "| #{ columns.map { |_,g| g[:label].ljust(g[:width]) }.join(' | ') } |"
|
268
|
+
nil
|
269
|
+
end
|
189
270
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
271
|
+
# Write a row divider for a table
|
272
|
+
#
|
273
|
+
# @param [Vagrant::Environment] env Current Vagrant environment
|
274
|
+
# @param [Array<Hash>] columns List of columns in Hash format with `:label` and `:width` keys
|
275
|
+
# @return [nil]
|
276
|
+
def write_divider(env, columns)
|
277
|
+
env.ui.info "+-#{ columns.map { |_,g| "-"*g[:width] }.join("-+-") }-+"
|
278
|
+
nil
|
279
|
+
end
|
280
|
+
|
281
|
+
# Write a line of content for a table
|
282
|
+
#
|
283
|
+
# @param [Vagrant::Environment] env Current Vagrant environment
|
284
|
+
# @param [Array<Hash>] columns List of columns in Hash format with `:label` and `:width` keys
|
285
|
+
# @param [Hash] h Values to print in row
|
286
|
+
# @param [Array<String>] to_rjust_keys List of columns to right justify
|
287
|
+
# @return [nil]
|
288
|
+
def write_line(env, columns, h, to_rjust_keys)
|
289
|
+
str = h.keys.map { |k|
|
290
|
+
if to_rjust_keys.include?(k)
|
291
|
+
h[k].rjust(columns[k][:width])
|
194
292
|
else
|
195
|
-
|
196
|
-
columns = result.first.keys
|
197
|
-
columns.each do |c|
|
198
|
-
column_labels[c] = c.to_s.upcase
|
199
|
-
end
|
200
|
-
print_search_table(env, column_labels, result, [:downloads])
|
293
|
+
h[k].ljust(columns[k][:width])
|
201
294
|
end
|
202
|
-
|
295
|
+
}.join(" | ")
|
296
|
+
env.ui.info "| #{str} |"
|
297
|
+
nil
|
298
|
+
end
|
299
|
+
|
300
|
+
# Converts a string of numbers into a formatted number
|
301
|
+
#
|
302
|
+
# 1234 -> 1,234
|
303
|
+
#
|
304
|
+
# @param [String] number Numer to format
|
305
|
+
def format_downloads(number)
|
306
|
+
number.to_s.chars.reverse.each_slice(3).map(&:join).join(",").reverse
|
203
307
|
end
|
204
308
|
end
|
205
309
|
end
|