vagrant_cloud 2.0.3 → 3.0.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/README.md +145 -39
- data/lib/vagrant_cloud.rb +20 -10
- data/lib/vagrant_cloud/account.rb +86 -169
- data/lib/vagrant_cloud/box.rb +105 -159
- data/lib/vagrant_cloud/box/provider.rb +173 -0
- data/lib/vagrant_cloud/box/version.rb +161 -0
- data/lib/vagrant_cloud/client.rb +436 -46
- data/lib/vagrant_cloud/data.rb +293 -0
- data/lib/vagrant_cloud/error.rb +47 -0
- data/lib/vagrant_cloud/instrumentor.rb +7 -0
- data/lib/vagrant_cloud/instrumentor/collection.rb +123 -0
- data/lib/vagrant_cloud/instrumentor/core.rb +9 -0
- data/lib/vagrant_cloud/instrumentor/logger.rb +97 -0
- data/lib/vagrant_cloud/logger.rb +60 -0
- data/lib/vagrant_cloud/organization.rb +62 -0
- data/lib/vagrant_cloud/response.rb +7 -0
- data/lib/vagrant_cloud/response/create_token.rb +7 -0
- data/lib/vagrant_cloud/response/request_2fa.rb +7 -0
- data/lib/vagrant_cloud/response/search.rb +65 -0
- data/lib/vagrant_cloud/search.rb +113 -15
- data/lib/vagrant_cloud/version.rb +1 -200
- metadata +31 -25
- data/bin/vagrant_cloud +0 -6
- data/lib/vagrant_cloud/errors.rb +0 -35
- data/lib/vagrant_cloud/provider.rb +0 -155
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 360b98c309f3b2804dce161d08972266978ac3059dbc5516bd12b52e5c1fff98
|
4
|
+
data.tar.gz: d258cb4d89be2bc698e4f087e8a504e2ba9a4e085ff9ab5e48c24c262926784f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4dadf15e12aa55529ff8023f73c19a59f4882496003246cca6ee5e2b798f86004beb343b68c97dcda465205a227312ba49023ba91b83429ac24da8cb919c8372
|
7
|
+
data.tar.gz: b46249ed808cf36ffe8e65f0e234ef7fb291e9648e8cc1d8e044c1fe0ac7b1da2814eda39a14b048728bd5376b7a14a24a31d2ab68ab56f832e4ea0c2ebafb4b
|
data/README.md
CHANGED
@@ -1,43 +1,149 @@
|
|
1
|
-
vagrant_cloud
|
2
|
-
|
1
|
+
# vagrant_cloud
|
2
|
+
|
3
3
|
Ruby client for the [Vagrant Cloud API](https://www.vagrantup.com/docs/vagrant-cloud/api.html).
|
4
4
|
|
5
|
-
[](https://travis-ci.org/hashicorp/vagrant_cloud)
|
6
5
|
[](https://rubygems.org/gems/vagrant_cloud)
|
7
6
|
|
7
|
+
This library provides the functionality to create, modify, and delete boxes, versions,
|
8
|
+
and providers on Vagrant Cloud.
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
The Vagrant Cloud library provides two methods for interacting with the Vagrant Cloud API. The
|
13
|
+
first is direct interaction using a `VagrantCloud::Client` instance. The second is a basic
|
14
|
+
model based approach using a `VagrantCloud::Account` instance.
|
8
15
|
|
9
|
-
|
10
|
-
|
16
|
+
### Direct Client
|
17
|
+
|
18
|
+
The `VagrantCloud::Client` class contains all the underlying functionality which with
|
19
|
+
`vagrant_cloud` library uses for communicating with Vagrant Cloud. It can be used directly
|
20
|
+
for quickly and easily sending requests to Vagrant Cloud. The `VagrantCloud::Client`
|
21
|
+
class will automatically handle any configured authentication, request parameter
|
22
|
+
structuring, and response validation. All API related methods in the `VagrantCloud::Client`
|
23
|
+
class will return `Hash` results.
|
24
|
+
|
25
|
+
Example usage (display box details):
|
11
26
|
|
12
|
-
Usage
|
13
|
-
-----
|
14
|
-
Example usage:
|
15
27
|
```ruby
|
16
|
-
|
17
|
-
box = account.ensure_box('my_box')
|
18
|
-
version = box.ensure_version('0.0.1')
|
19
|
-
provider = version.ensure_provider('virtualbox', 'http://example.com/foo.box')
|
28
|
+
require "vagrant_cloud"
|
20
29
|
|
21
|
-
|
22
|
-
|
30
|
+
client = VagrantCloud::Client.new(access_token: "MY_TOKEN")
|
31
|
+
box = client.box_get(username: "hashicorp", name: "bionic64")
|
32
|
+
|
33
|
+
puts "Box: #{box[:tag]} Description: #{box[:description]}"
|
34
|
+
```
|
35
|
+
|
36
|
+
Example usage (creating box and releasing a new version):
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require "vagrant_cloud"
|
40
|
+
require "net/http"
|
41
|
+
|
42
|
+
# Create a new client
|
43
|
+
client = VagrantCloud::Client.new(access_token: "MY_TOKEN")
|
44
|
+
|
45
|
+
# Create a new box
|
46
|
+
client.box_create(
|
47
|
+
username: "hashicorp",
|
48
|
+
name: "test-bionic64",
|
49
|
+
short_description: "Test Box",
|
50
|
+
long_description: "Testing box for an example",
|
51
|
+
is_private: false
|
52
|
+
)
|
53
|
+
|
54
|
+
# Create a new version
|
55
|
+
client.box_version_create(
|
56
|
+
username: "hashicorp",
|
57
|
+
name: "test-bionic64",
|
58
|
+
version: "1.0.0",
|
59
|
+
description: "Version 1.0.0 release"
|
60
|
+
)
|
61
|
+
|
62
|
+
# Create a new provider
|
63
|
+
client.box_version_provider_create(
|
64
|
+
username: "hashicorp",
|
65
|
+
name: "test-bionic64",
|
66
|
+
version: "1.0.0",
|
67
|
+
provider: "virtualbox"
|
68
|
+
)
|
69
|
+
|
70
|
+
# Request box upload URL
|
71
|
+
upload_url = client.box_version_provider_upload(
|
72
|
+
username: "hashicorp",
|
73
|
+
name: "test-bionic64",
|
74
|
+
version: "1.0.0",
|
75
|
+
provider: "virtualbox"
|
76
|
+
)
|
77
|
+
|
78
|
+
# Upload box asset
|
79
|
+
uri = URI.parse(upload_url[:upload_path])
|
80
|
+
request = Net::HTTP::Post.new(uri)
|
81
|
+
box = File.open(BOX_PATH, "rb")
|
82
|
+
request.set_form([["file", box]], "multipart/form-data")
|
83
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme.eql?("https")) do |http|
|
84
|
+
http.request(request)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Release the version
|
88
|
+
client.box_version_release(
|
89
|
+
username: "hashicorp",
|
90
|
+
name: "test-bionic64",
|
91
|
+
version: "1.0.0"
|
92
|
+
)
|
93
|
+
```
|
94
|
+
|
95
|
+
### Simple Models
|
96
|
+
|
97
|
+
The `VagrantCloud::Account` class is the entry point for using simple models to
|
98
|
+
interact with Vagrant Cloud.
|
99
|
+
|
100
|
+
Example usage (display box details):
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
require "vagrant_cloud"
|
104
|
+
|
105
|
+
account = VagrantCloud::Account.new(access_token: "MY_TOKEN")
|
106
|
+
org = account.organization(name: "hashicorp")
|
107
|
+
box = org.boxes.select { |b| b.name == "bionic64" }
|
108
|
+
|
109
|
+
puts "Box: #{box[:tag]} Description: #{box[:description]}"
|
23
110
|
```
|
24
111
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
112
|
+
Example usage (creating box and releasing a new version):
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
require "vagrant_cloud"
|
116
|
+
|
117
|
+
# Load our account
|
118
|
+
account = VagrantCloud::Account.new(access_token: "MY_TOKEN")
|
119
|
+
|
120
|
+
# Load organization
|
121
|
+
org = account.organization(name: "hashicorp")
|
122
|
+
|
123
|
+
# Create a new box
|
124
|
+
box = org.add_box("test-bionic64")
|
125
|
+
box.description = "Testing box for an example"
|
126
|
+
box.short_description = "Test Box"
|
127
|
+
|
128
|
+
# Create a new version
|
129
|
+
version = box.add_version("1.0.0")
|
130
|
+
version.description = "Version 1.0.0 release"
|
131
|
+
|
132
|
+
# Create a new provider
|
133
|
+
provider = version.add_provider("virtualbox")
|
134
|
+
|
135
|
+
# Save the box, version, and provider
|
136
|
+
box.save
|
137
|
+
|
138
|
+
# Upload box asset
|
139
|
+
provider.upload(path: BOX_PATH)
|
140
|
+
|
141
|
+
# Release the version
|
142
|
+
version.release
|
36
143
|
```
|
37
|
-
If you installed vagrant_cloud with bundler, then you may have to invoke using `bundle exec vagrant_cloud`
|
38
144
|
|
39
|
-
Development & Contributing
|
40
|
-
|
145
|
+
## Development & Contributing
|
146
|
+
|
41
147
|
Pull requests are very welcome!
|
42
148
|
|
43
149
|
Install dependencies:
|
@@ -50,18 +156,18 @@ Run the tests:
|
|
50
156
|
bundle exec rspec
|
51
157
|
```
|
52
158
|
|
53
|
-
|
54
|
-
```
|
55
|
-
bundle exec rubocop
|
56
|
-
```
|
159
|
+
## Releasing
|
57
160
|
|
58
161
|
Release a new version:
|
59
162
|
|
60
|
-
1.
|
61
|
-
|
62
|
-
|
163
|
+
1. Update the version in the `version.txt` file
|
164
|
+
1. Commit the change to master
|
165
|
+
1. Create a new version tag in git: `git tag vX.X.X`
|
166
|
+
1. Push the new tag and master to GitHub `git push origin master --tags`
|
167
|
+
|
168
|
+
The new release will be automatically built and published.
|
169
|
+
|
170
|
+
## History
|
63
171
|
|
64
|
-
|
65
|
-
|
66
|
-
This gem has been developed and maintained by [Cargo Media](https://www.cargomedia.ch) since April 2014.
|
67
|
-
HashiCorp became the official maintainer in October 2017.
|
172
|
+
- This gem was developed and maintained by [Cargo Media](https://www.cargomedia.ch) from April 2014 until October 2017.
|
173
|
+
- The `vagrant_cloud` CLI tool included in this RubyGem has been deprecated and removed. See `vagrant cloud` for a replacement.
|
data/lib/vagrant_cloud.rb
CHANGED
@@ -1,11 +1,21 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "excon"
|
2
|
+
require "log4r"
|
3
|
+
require "json"
|
4
|
+
require "securerandom"
|
5
|
+
require "set"
|
6
|
+
require 'singleton'
|
7
|
+
require "thread"
|
3
8
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
module VagrantCloud
|
10
|
+
autoload :Account, "vagrant_cloud/account"
|
11
|
+
autoload :Box, "vagrant_cloud/box"
|
12
|
+
autoload :Client, "vagrant_cloud/client"
|
13
|
+
autoload :Data, "vagrant_cloud/data"
|
14
|
+
autoload :Error, "vagrant_cloud/error"
|
15
|
+
autoload :Instrumentor, "vagrant_cloud/instrumentor"
|
16
|
+
autoload :Logger, "vagrant_cloud/logger"
|
17
|
+
autoload :Organization, "vagrant_cloud/organization"
|
18
|
+
autoload :Response, "vagrant_cloud/response"
|
19
|
+
autoload :Search, "vagrant_cloud/search"
|
20
|
+
autoload :VERSION, "vagrant_cloud/version"
|
21
|
+
end
|
@@ -1,195 +1,112 @@
|
|
1
1
|
module VagrantCloud
|
2
|
+
# VagrantCloud account
|
2
3
|
class Account
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# @
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@client = Client.new(access_token, custom_server)
|
12
|
-
end
|
13
|
-
|
14
|
-
#---------------------------
|
15
|
-
# Authentication API Helpers
|
16
|
-
#---------------------------
|
17
|
-
|
18
|
-
# @param [String] password
|
19
|
-
# @param [String] description
|
20
|
-
# @param [String] 2FA code
|
21
|
-
# @return [Hash] response body
|
22
|
-
def create_token(password, description = nil, code = nil)
|
23
|
-
token_data_params = {
|
24
|
-
token: { description: description },
|
25
|
-
user: { login: @username, password: password },
|
26
|
-
two_factor: code
|
27
|
-
}.delete_if { |_, v| v.nil? }
|
28
|
-
|
29
|
-
token_response = @client.request('post', '/authenticate', token_data_params)
|
30
|
-
token_response
|
31
|
-
end
|
32
|
-
|
33
|
-
# @param [String] token
|
34
|
-
def delete_token(access_token = nil)
|
35
|
-
token_response = @client.request('delete', '/authenticate', nil, access_token)
|
36
|
-
token_response
|
37
|
-
end
|
38
|
-
|
39
|
-
# Validates a token on the account or a one-off validation token request.
|
40
|
-
# Will return nil if token is valid, otherwise will return Hash of response
|
41
|
-
# from Vagrant Cloud
|
4
|
+
# @return [Client]
|
5
|
+
attr_reader :client
|
6
|
+
# @return [String] username of this account
|
7
|
+
attr_reader :username
|
8
|
+
# @return [Instrumentor::Collection] Instrumentor in use
|
9
|
+
attr_reader :instrumentor
|
10
|
+
|
11
|
+
# Create a new Account instance
|
42
12
|
#
|
43
|
-
# @param [String] access_token
|
44
|
-
# @
|
45
|
-
|
46
|
-
|
47
|
-
|
13
|
+
# @param [String] access_token Authentication token
|
14
|
+
# @param [Client] client Client to use for account
|
15
|
+
# @param [String] custom_server Custom server URL for client
|
16
|
+
# @param [Integer] retry_count Number of retries on idempotent requests
|
17
|
+
# @param [Integer] retry_interval Number of seconds to wait between requests
|
18
|
+
# @param [Instrumentor::Core] instrumentor Instrumentor to use
|
19
|
+
# @return [Account]
|
20
|
+
def initialize(access_token: nil, client: nil, custom_server: nil, retry_count: nil, retry_interval: nil, instrumentor: nil)
|
21
|
+
raise ArgumentError, "Account accepts `access_token` or `client` but not both" if
|
22
|
+
client && access_token
|
23
|
+
raise TypeError, "Expected `#{Client.name}` but received `#{client.class.name}`" if
|
24
|
+
client && !client.is_a?(Client)
|
25
|
+
|
26
|
+
if client
|
27
|
+
@client = client
|
28
|
+
else
|
29
|
+
@client = Client.new(
|
30
|
+
access_token: access_token,
|
31
|
+
url_base: custom_server,
|
32
|
+
retry_count: retry_count,
|
33
|
+
retry_interval: retry_interval,
|
34
|
+
instrumentor: instrumentor
|
35
|
+
)
|
36
|
+
end
|
37
|
+
setup!
|
48
38
|
end
|
49
39
|
|
50
|
-
# @
|
51
|
-
|
52
|
-
|
53
|
-
def request_2fa_code(delivery_method, password)
|
54
|
-
twofa_code_params = {
|
55
|
-
two_factor: { delivery_method: delivery_method },
|
56
|
-
user: { login: @username, password: password }
|
57
|
-
}
|
58
|
-
|
59
|
-
code_response = @client.request('post', '/two-factor/request-code', twofa_code_params)
|
60
|
-
code_response
|
40
|
+
# @return [Search]
|
41
|
+
def searcher
|
42
|
+
Search.new(account: self)
|
61
43
|
end
|
62
44
|
|
63
45
|
#---------------------------
|
64
|
-
#
|
46
|
+
# Authentication API Helpers
|
65
47
|
#---------------------------
|
66
48
|
|
67
|
-
#
|
68
|
-
# @
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
# @param [String] name
|
84
|
-
# @param [Hash] data
|
85
|
-
# @return [Box]
|
86
|
-
def get_box(name, data = nil)
|
87
|
-
Box.new(self, name, data, nil, nil, @client.access_token, @client.url_base)
|
49
|
+
# Create a new access token
|
50
|
+
# @param [String] password Remote password
|
51
|
+
# @param [String] description Description of token
|
52
|
+
# @param [String] code 2FA code
|
53
|
+
# @return [Response::CreateToken]
|
54
|
+
def create_token(password:, description: Data::Nil, code: Data::Nil)
|
55
|
+
r = client.authentication_token_create(username: username,
|
56
|
+
password: password, description: description, code: code)
|
57
|
+
|
58
|
+
Response::CreateToken.new(
|
59
|
+
token: r[:token],
|
60
|
+
token_hash: r[:token_hash],
|
61
|
+
created_at: r[:created_at],
|
62
|
+
description: r[:description]
|
63
|
+
)
|
88
64
|
end
|
89
65
|
|
90
|
-
#
|
91
|
-
#
|
92
|
-
# @return [
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
data = @client.request('post', '/boxes', box: params)
|
98
|
-
get_box(name, data)
|
66
|
+
# Delete the current token
|
67
|
+
#
|
68
|
+
# @return [self]
|
69
|
+
def delete_token
|
70
|
+
client.authentication_token_delete
|
71
|
+
self
|
99
72
|
end
|
100
73
|
|
101
|
-
#
|
102
|
-
#
|
103
|
-
# @return [
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
# try to read the box data
|
108
|
-
begin
|
109
|
-
box = get_box(name)
|
110
|
-
box.data
|
111
|
-
rescue VagrantCloud::ClientError => err
|
112
|
-
# Check if it's a 404 error. If so, then create
|
113
|
-
# the missing box
|
114
|
-
raise if err.error_code != 404
|
115
|
-
|
116
|
-
box = create_box(name, params)
|
117
|
-
# If we've just created the box, we're done.
|
118
|
-
return box
|
119
|
-
end
|
120
|
-
|
121
|
-
# Select elements from params that don't match what we have in the box
|
122
|
-
# data. These are changed parameters and should be updated.
|
123
|
-
update_params = params.select do |k, v|
|
124
|
-
box.data[box.param_name(k)] != v
|
125
|
-
end
|
126
|
-
|
127
|
-
# Update the box with any params that had changed.
|
128
|
-
box.update(update_params) unless update_params.empty?
|
129
|
-
|
130
|
-
box
|
74
|
+
# Validate the current token
|
75
|
+
#
|
76
|
+
# @return [self]
|
77
|
+
def validate_token
|
78
|
+
client.request(path: "authenticate")
|
79
|
+
self
|
131
80
|
end
|
132
81
|
|
133
|
-
|
134
|
-
# Old Box API Helpers
|
135
|
-
#--------------------
|
136
|
-
|
137
|
-
# REMOVED IN FAVOR OF CLIENT CLASS, but still exists to support any old clients
|
82
|
+
# Request a 2FA code is sent
|
138
83
|
#
|
139
|
-
# @param [String] method
|
140
|
-
# @param [String]
|
141
|
-
# @
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
headers['Authorization'] = "Bearer #{access_token}" if access_token
|
147
|
-
|
148
|
-
result = RestClient::Request.execute(
|
149
|
-
method: method,
|
150
|
-
url: url_base + path,
|
151
|
-
payload: params,
|
152
|
-
headers: headers,
|
153
|
-
ssl_version: 'TLSv1'
|
154
|
-
)
|
155
|
-
result = JSON.parse(result)
|
156
|
-
errors = result['errors']
|
157
|
-
raise "Vagrant Cloud returned error: #{errors}" if errors
|
158
|
-
|
159
|
-
result
|
84
|
+
# @param [String] delivery_method Delivery method of 2FA
|
85
|
+
# @param [String] password Account password
|
86
|
+
# @return [Response]
|
87
|
+
def request_2fa_code(delivery_method:, password:)
|
88
|
+
r = client.authentication_request_2fa_code(username: username,
|
89
|
+
password: password, delivery_method: delivery_method)
|
90
|
+
Response::Request2FA.new(destination: r.dig(:two_factor, :obfuscated_destination))
|
160
91
|
end
|
161
92
|
|
162
|
-
|
163
|
-
|
164
|
-
# @
|
165
|
-
|
166
|
-
|
93
|
+
# Fetch the requested organization
|
94
|
+
#
|
95
|
+
# @param [String] name Organization name
|
96
|
+
# @return [Organization]
|
97
|
+
def organization(name: nil)
|
98
|
+
org_name = name || username
|
99
|
+
r = client.organization_get(name: org_name)
|
100
|
+
Organization.load(account: self, **r)
|
167
101
|
end
|
168
102
|
|
169
|
-
|
170
|
-
# @return [Hash]
|
171
|
-
def box_params(*args)
|
172
|
-
# Prepares a hash based on the *args array passed in.
|
173
|
-
# Acceptable parameters are those documented by Hashicorp for the v1 API
|
174
|
-
# at https://vagrantcloud.com/docs
|
103
|
+
protected
|
175
104
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
# Find and remove the first hash we find in *args. Set params to an
|
181
|
-
# empty hash if we weren't passed one.
|
182
|
-
params = args.select { |v| v.is_a?(Hash) }.first
|
183
|
-
if params.nil?
|
184
|
-
params = {}
|
185
|
-
else
|
186
|
-
args.delete_if { |v| v == params }
|
105
|
+
def setup!
|
106
|
+
if client.access_token
|
107
|
+
r = client.request(path: "authenticate")
|
108
|
+
@username = r.dig(:user, :username)
|
187
109
|
end
|
188
|
-
|
189
|
-
# Default boxes to public can be overridden by providing :is_private
|
190
|
-
params[:is_private] = false unless params.key?(:is_private)
|
191
|
-
|
192
|
-
params
|
193
110
|
end
|
194
111
|
end
|
195
112
|
end
|