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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb644aa4c9adb9ab23859aabb4cf0ab6644822aa22732c8be4f4dff4dfc03d9d
4
- data.tar.gz: c2b3875c5d8423fce062e0a5b1e1dbc58871aa07eedc5ab5cd88ae323f7bf007
3
+ metadata.gz: 360b98c309f3b2804dce161d08972266978ac3059dbc5516bd12b52e5c1fff98
4
+ data.tar.gz: d258cb4d89be2bc698e4f087e8a504e2ba9a4e085ff9ab5e48c24c262926784f
5
5
  SHA512:
6
- metadata.gz: 2fe5bab079a06e2ac5df5a6c572f3015cd4e428622aae819826fd456be0d663cab8ba70574f11d4a6fcd0e33547dd60d54c667aa7c9460a335d6ecc790c535ca
7
- data.tar.gz: edfd6d3934542bdf22fdf228736ba069198097fae2ab91cec5724efdc5fb9bf5b5ca2a2b26dfe48285d5a2a824273fc5d95e935ed3806fd0a142a722d80daffd
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
- [![Build Status](https://img.shields.io/travis/hashicorp/vagrant_cloud/master.svg)](https://travis-ci.org/hashicorp/vagrant_cloud)
6
5
  [![Gem Version](https://img.shields.io/gem/v/vagrant_cloud.svg)](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
- This client allows to create, modify and delete *boxes*, *versions* and *providers*.
10
- The main entry point is an object referencing your *account*.
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
- account = VagrantCloud::Account.new('<username>', '<access_token>')
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
- version.release
22
- puts provider.download_url
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
- __NOTE:__ As of version 2.0.0, the CLI has been deprecated in favor of the `vagrant cloud`
26
- command. More information about how to use the `vagrant cloud` command can be found
27
- on the [Vagrant documentation](https://www.vagrantup.com/docs/cli/cloud.html).
28
-
29
- Example CLI usage:
30
- Create a version and provider within an existing Box, upload a file to be hosted by Vagrant Cloud, and release the version
31
- ```sh
32
- vagrant_cloud create_version --username $USERNAME --token $VAGRANT_CLOUD_TOKEN --box $BOX_NAME --version $BOX_VERSION
33
- vagrant_cloud create_provider --username $USERNAME --token $VAGRANT_CLOUD_TOKEN --box $BOX_NAME --version $BOX_VERSION
34
- vagrant_cloud upload_file --username $USERNAME --token $VAGRANT_CLOUD_TOKEN --box $BOX_NAME --version $BOX_VERSION --provider_file_path $PACKAGE_PATH
35
- vagrant_cloud release_version --username $USERNAME --token $VAGRANT_CLOUD_TOKEN --box $BOX_NAME --version $BOX_VERSION
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
- Check the code syntax:
54
- ```
55
- bundle exec rubocop
56
- ```
159
+ ## Releasing
57
160
 
58
161
  Release a new version:
59
162
 
60
- 1. Bump the version in `vagrant_cloud.gemspec`, merge to master.
61
- 2. Push a new tag to master.
62
- 3. Release to RubyGems with `bundle exec rake release`.
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
- History
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.
@@ -1,11 +1,21 @@
1
- require 'json'
2
- require 'rest_client'
1
+ require "excon"
2
+ require "log4r"
3
+ require "json"
4
+ require "securerandom"
5
+ require "set"
6
+ require 'singleton'
7
+ require "thread"
3
8
 
4
- require 'vagrant_cloud/errors'
5
-
6
- require 'vagrant_cloud/account'
7
- require 'vagrant_cloud/box'
8
- require 'vagrant_cloud/version'
9
- require 'vagrant_cloud/provider'
10
- require 'vagrant_cloud/search'
11
- require 'vagrant_cloud/client'
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
- attr_accessor :username
4
- attr_accessor :access_token
5
-
6
- # @param [String] username
7
- # @param [String] access_token
8
- def initialize(username, access_token, custom_server = nil)
9
- @username = username
10
- @access_token = access_token
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
- # @return [Hash] response body
45
- def validate_token(access_token = nil)
46
- token_response = @client.request('get', '/authenticate', nil, access_token)
47
- token_response
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
- # @param [String] delivery_method
51
- # @param [String] password
52
- # @return [Hash] response body
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
- # Organization API Helpers
46
+ # Authentication API Helpers
65
47
  #---------------------------
66
48
 
67
- # @param [String] - organization
68
- # @return [Hash]
69
- def read_organization(org = nil)
70
- if org
71
- name = org
72
- else
73
- name = @username
74
- end
75
-
76
- @client.request('get', "/user/#{name}")
77
- end
78
-
79
- #--------------------
80
- # Old Box API Helpers
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
- # @param [String] name
91
- # @param [Hash] args
92
- # @return [Box]
93
- def create_box(name, *args)
94
- params = box_params(*args)
95
- params[:name] = name
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
- # @param [String] name
102
- # @param [Hash] args
103
- # @return [Box]
104
- def ensure_box(name, *args)
105
- params = box_params(*args)
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] path
141
- # @param [Hash] params
142
- # @return [Hash]
143
- def request(method, path, params = {})
144
- headers = {}
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
- private
163
-
164
- # @return [String]
165
- def url_base
166
- 'https://vagrantcloud.com/api/v1'
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
- # @param [Array] args
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
- # This dance is to simulate what we could have accomplished with **args
177
- # in Ruby 2.0+
178
- # This will silently discard any options that are not passed in as a
179
- # hash.
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