droplet_kit 1.3.3 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +46 -0
- data/Rakefile +0 -1
- data/droplet_kit.gemspec +1 -1
- data/lib/droplet_kit.rb +9 -1
- data/lib/droplet_kit/client.rb +2 -1
- data/lib/droplet_kit/mappings/droplet_mapping.rb +1 -0
- data/lib/droplet_kit/mappings/tag_mapping.rb +18 -0
- data/lib/droplet_kit/mappings/tagged_droplets_resources_mapping.rb +16 -0
- data/lib/droplet_kit/mappings/tagged_resources_mapping.rb +15 -0
- data/lib/droplet_kit/models/droplet.rb +1 -1
- data/lib/droplet_kit/models/tag.rb +6 -0
- data/lib/droplet_kit/models/tagged_droplets_resources.rb +6 -0
- data/lib/droplet_kit/models/tagged_resources.rb +5 -0
- data/lib/droplet_kit/paginated_resource.rb +5 -1
- data/lib/droplet_kit/resources/droplet_action_resource.rb +24 -0
- data/lib/droplet_kit/resources/droplet_resource.rb +7 -1
- data/lib/droplet_kit/resources/tag_resource.rb +48 -0
- data/lib/droplet_kit/version.rb +1 -1
- data/spec/fixtures/droplets/all.json +4 -0
- data/spec/fixtures/droplets/create.json +3 -0
- data/spec/fixtures/droplets/find.json +4 -0
- data/spec/fixtures/tags/all.json +26 -0
- data/spec/fixtures/tags/all_empty.json +7 -0
- data/spec/fixtures/tags/create.json +11 -0
- data/spec/fixtures/tags/find.json +92 -0
- data/spec/lib/droplet_kit/paginated_resource_spec.rb +11 -2
- data/spec/lib/droplet_kit/resources/droplet_action_resource_spec.rb +53 -4
- data/spec/lib/droplet_kit/resources/droplet_resource_spec.rb +19 -7
- data/spec/lib/droplet_kit/resources/tag_resource_spec.rb +135 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/request_stub_helpers.rb +2 -2
- metadata +22 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3766abf70fbb920d8beb72cfc9b8db1b84c68d93
|
4
|
+
data.tar.gz: a39c01eb0af5fbb13da576c578419a1ba4f8765f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d79cc94964d34123b130791442d112cc1ca9926611132dfff1f3d9d174069a1f24e2ebde48463fcc0545ac6a9a543e40b9b45592ff5f4175ee16d97125e62547
|
7
|
+
data.tar.gz: 38028b0f20a9219ba2db2794de0bb608f922a05bd24dae3c58e5f671eaf5d7f696cfd6109dbf3a61c81a28716ae3ffac2b68ffde4ed3b944c218223105c38f92
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -74,10 +74,12 @@ client.droplets #=> DropletKit::DropletResource
|
|
74
74
|
Actions supported:
|
75
75
|
|
76
76
|
* `client.droplets.all()`
|
77
|
+
* `client.droplets.all(tag_name: 'tag_name')`
|
77
78
|
* `client.droplets.find(id: 'id')`
|
78
79
|
* `client.droplets.create(droplet)`
|
79
80
|
* `client.droplets.create_multiple(droplet)`
|
80
81
|
* `client.droplets.delete(id: 'id')`
|
82
|
+
* `client.droplets.delete_for_tag(tag_name: 'tag_name')`
|
81
83
|
* `client.droplets.kernels(id: 'id')`
|
82
84
|
* `client.droplets.snapshots(id: 'id')`
|
83
85
|
* `client.droplets.backups(id: 'id')`
|
@@ -94,22 +96,33 @@ Actions supported:
|
|
94
96
|
|
95
97
|
* `client.droplet_actions.reboot(droplet_id: droplet.id)`
|
96
98
|
* `client.droplet_actions.power_cycle(droplet_id: droplet.id)`
|
99
|
+
* `client.droplet_actions.power_cycle_for_tag(tag: 'tag_name')`
|
97
100
|
* `client.droplet_actions.shutdown(droplet_id: droplet.id)`
|
101
|
+
* `client.droplet_actions.shutdown_for_tag(tag: 'tag_name')`
|
98
102
|
* `client.droplet_actions.power_off(droplet_id: droplet.id)`
|
103
|
+
* `client.droplet_actions.power_off_for_tag(tag: 'tag_name')`
|
99
104
|
* `client.droplet_actions.power_on(droplet_id: droplet.id)`
|
105
|
+
* `client.droplet_actions.power_on_for_tag(tag: 'tag_name')`
|
100
106
|
* `client.droplet_actions.password_reset(droplet_id: droplet.id)`
|
101
107
|
* `client.droplet_actions.enable_ipv6(droplet_id: droplet.id)`
|
108
|
+
* `client.droplet_actions.enable_ipv6_for_tag(tag: 'tag_name')`
|
102
109
|
* `client.droplet_actions.enable_backups(droplet_id: droplet.id)`
|
110
|
+
* `client.droplet_actions.enable_backups_for_tag(tag: 'tag_name')`
|
103
111
|
* `client.droplet_actions.disable_backups(droplet_id: droplet.id)`
|
112
|
+
* `client.droplet_actions.disable_backups_for_tag(tag: 'tag_name')`
|
104
113
|
* `client.droplet_actions.upgrade(droplet_id: droplet.id)`
|
105
114
|
* `client.droplet_actions.enable_private_networking(droplet_id: droplet.id)`
|
115
|
+
* `client.droplet_actions.enable_private_networking_for_tag(tag: 'tag_name')`
|
106
116
|
* `client.droplet_actions.snapshot(droplet_id: droplet.id, name: 'Snapshot Name')`
|
117
|
+
* `client.droplet_actions.snapshot_for_tag(tag: 'tag_name', name: 'Snapshot Name')`
|
107
118
|
* `client.droplet_actions.change_kernel(droplet_id: droplet.id, kernel: 'kernel_id')`
|
108
119
|
* `client.droplet_actions.rename(droplet_id: droplet.id, name: 'New-Droplet-Name')`
|
109
120
|
* `client.droplet_actions.rebuild(droplet_id: droplet.id, image: 'image_id')`
|
110
121
|
* `client.droplet_actions.restore(droplet_id: droplet.id, image: 'image_id')`
|
111
122
|
* `client.droplet_actions.resize(droplet_id: droplet.id, size: '1gb')`
|
112
123
|
* `client.droplet_actions.find(droplet_id: droplet.id, id: action.id)`
|
124
|
+
* `client.droplet_actions.action_for_id(droplet_id: droplet.id, type: 'event_name', param: 'value')`
|
125
|
+
* `client.droplet_actions.action_for_tag(tag: 'tag_name', type: 'event_name', param: 'value')`
|
113
126
|
|
114
127
|
## Domain resource
|
115
128
|
|
@@ -207,6 +220,16 @@ client = DropletKit::Client.new(access_token: 'TOKEN')
|
|
207
220
|
client.ssh_keys #=> DropletKit::SSHKeyResource
|
208
221
|
```
|
209
222
|
|
223
|
+
When you want to create a droplet using your stored SSH key.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
client = DropletKit::Client.new(access_token: 'YOUR_TOKEN')
|
227
|
+
my_ssh_keys = client.ssh_keys.all.collect {|key| key.fingerprint}
|
228
|
+
droplet = DropletKit::Droplet.new(name: 'mysite.com', region: 'nyc2', image: 'ubuntu-14-04-x64', size: '512mb', ssh_keys: my_ssh_keys)
|
229
|
+
created = client.droplets.create(droplet)
|
230
|
+
# => DropletKit::Droplet(id: 1231, name: 'something.com', ...)
|
231
|
+
```
|
232
|
+
|
210
233
|
Actions supported:
|
211
234
|
|
212
235
|
* `client.ssh_keys.all()`
|
@@ -227,6 +250,29 @@ Actions supported:
|
|
227
250
|
* `client.account.info()`
|
228
251
|
|
229
252
|
|
253
|
+
## Floating IP resource
|
254
|
+
|
255
|
+
client = DropletKit::Client.new(access_token: 'TOKEN')
|
256
|
+
client.floating_ips #=> DropletKit::FloatingIpResource
|
257
|
+
|
258
|
+
Actions supported:
|
259
|
+
|
260
|
+
* `client.floating_ips.all()`
|
261
|
+
* `client.floating_ips.find(ip: 'ip address')`
|
262
|
+
* `client.floating_ips.create(floating_ip)`
|
263
|
+
* `client.floating_ips.delete(ip: 'ip address')`
|
264
|
+
|
265
|
+
## Floating IP Action resource
|
266
|
+
|
267
|
+
client = DropletKit::Client.new(access_token: 'TOKEN')
|
268
|
+
client.floating_ip_actions #=> DropletKit::FloatingIpActionResource
|
269
|
+
|
270
|
+
Actions supported:
|
271
|
+
|
272
|
+
* `client.floating_ip_actions.assign(ip: floating_ip.ip, droplet_id: droplet.id)`
|
273
|
+
* `client.floating_ip_actions.unassign(ip: floating_ip.ip)`
|
274
|
+
|
275
|
+
|
230
276
|
## Contributing
|
231
277
|
|
232
278
|
1. Fork it ( https://github.com/digitalocean/droplet_kit/fork )
|
data/Rakefile
CHANGED
data/droplet_kit.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.required_ruby_version = '>= 2.0.0'
|
22
22
|
|
23
23
|
spec.add_dependency 'virtus', '~> 1.0.3'
|
24
|
-
spec.add_dependency "resource_kit", '~> 0.1.
|
24
|
+
spec.add_dependency "resource_kit", '~> 0.1.5'
|
25
25
|
spec.add_dependency "kartograph", '~> 0.2.3'
|
26
26
|
spec.add_dependency "activesupport", '> 3.0', '< 5.0.0'
|
27
27
|
spec.add_dependency "faraday", '~> 0.9.1'
|
data/lib/droplet_kit.rb
CHANGED
@@ -26,6 +26,9 @@ module DropletKit
|
|
26
26
|
autoload :Account, 'droplet_kit/models/account'
|
27
27
|
autoload :DropletUpgrade, 'droplet_kit/models/droplet_upgrade'
|
28
28
|
autoload :FloatingIp, 'droplet_kit/models/floating_ip'
|
29
|
+
autoload :Tag, 'droplet_kit/models/tag'
|
30
|
+
autoload :TaggedResources, 'droplet_kit/models/tagged_resources'
|
31
|
+
autoload :TaggedDropletsResources, 'droplet_kit/models/tagged_droplets_resources'
|
29
32
|
|
30
33
|
# Resources
|
31
34
|
autoload :DropletResource, 'droplet_kit/resources/droplet_resource'
|
@@ -42,6 +45,7 @@ module DropletKit
|
|
42
45
|
autoload :DropletUpgradeResource, 'droplet_kit/resources/droplet_upgrade_resource'
|
43
46
|
autoload :FloatingIpResource, 'droplet_kit/resources/floating_ip_resource'
|
44
47
|
autoload :FloatingIpActionResource, 'droplet_kit/resources/floating_ip_action_resource'
|
48
|
+
autoload :TagResource, 'droplet_kit/resources/tag_resource'
|
45
49
|
|
46
50
|
# JSON Maps
|
47
51
|
autoload :DropletMapping, 'droplet_kit/mappings/droplet_mapping'
|
@@ -62,6 +66,9 @@ module DropletKit
|
|
62
66
|
autoload :AccountMapping, 'droplet_kit/mappings/account_mapping'
|
63
67
|
autoload :DropletUpgradeMapping, 'droplet_kit/mappings/droplet_upgrade_mapping'
|
64
68
|
autoload :FloatingIpMapping, 'droplet_kit/mappings/floating_ip_mapping'
|
69
|
+
autoload :TagMapping, 'droplet_kit/mappings/tag_mapping'
|
70
|
+
autoload :TaggedResourcesMapping, 'droplet_kit/mappings/tagged_resources_mapping'
|
71
|
+
autoload :TaggedDropletsResourcesMapping, 'droplet_kit/mappings/tagged_droplets_resources_mapping'
|
65
72
|
|
66
73
|
# Utils
|
67
74
|
autoload :PaginatedResource, 'droplet_kit/paginated_resource'
|
@@ -74,7 +81,8 @@ module DropletKit
|
|
74
81
|
FailedUpdate = Class.new(DropletKit::Error)
|
75
82
|
|
76
83
|
class RateLimitReached < DropletKit::Error
|
77
|
-
attr_accessor :
|
84
|
+
attr_accessor :reset_at
|
85
|
+
attr_writer :limit, :remaining
|
78
86
|
|
79
87
|
def limit
|
80
88
|
@limit.to_i if @limit
|
data/lib/droplet_kit/client.rb
CHANGED
@@ -19,6 +19,7 @@ module DropletKit
|
|
19
19
|
property :action_ids, scopes: [:read]
|
20
20
|
property :features, scopes: [:read]
|
21
21
|
property :size_slug, scopes: [:read]
|
22
|
+
property :tags, scopes: [:read]
|
22
23
|
|
23
24
|
property :region, scopes: [:read], include: RegionMapping
|
24
25
|
property :image, scopes: [:read], include: ImageMapping
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module DropletKit
|
2
|
+
class TagMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping Tag
|
7
|
+
root_key plural: 'tags', singular: 'tag', scopes: [:read]
|
8
|
+
|
9
|
+
scoped :read, :create, :update do
|
10
|
+
property :name
|
11
|
+
end
|
12
|
+
|
13
|
+
scoped :read do
|
14
|
+
property :resources, include: TaggedResourcesMapping
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module DropletKit
|
2
|
+
class TaggedDropletsResourcesMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping TaggedDropletsResources
|
7
|
+
|
8
|
+
root_key plural: 'droplets', singular: 'tag', scopes: [:read]
|
9
|
+
|
10
|
+
scoped :read do
|
11
|
+
property :count
|
12
|
+
property :last_tagged, include: DropletMapping
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module DropletKit
|
2
|
+
class TaggedResourcesMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping TaggedResources
|
7
|
+
|
8
|
+
root_key plural: 'resources', singular: 'resource', scopes: [:read]
|
9
|
+
|
10
|
+
scoped :read do
|
11
|
+
property :droplets, include: TaggedDropletsResourcesMapping
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -2,7 +2,7 @@ module DropletKit
|
|
2
2
|
class Droplet < BaseModel
|
3
3
|
[:id, :name, :memory, :vcpus, :disk, :locked, :created_at,
|
4
4
|
:status, :backup_ids, :snapshot_ids, :action_ids, :features,
|
5
|
-
:region, :image, :networks, :kernel, :size_slug].each do |key|
|
5
|
+
:region, :image, :networks, :kernel, :size_slug, :tags].each do |key|
|
6
6
|
attribute(key)
|
7
7
|
end
|
8
8
|
|
@@ -21,6 +21,10 @@ module DropletKit
|
|
21
21
|
@options[:per_page] || PER_PAGE
|
22
22
|
end
|
23
23
|
|
24
|
+
def [](index)
|
25
|
+
@collection[index]
|
26
|
+
end
|
27
|
+
|
24
28
|
def each(start = 0)
|
25
29
|
# Start off with the first page if we have no idea of anything yet
|
26
30
|
fetch_next_page if total.nil?
|
@@ -73,4 +77,4 @@ module DropletKit
|
|
73
77
|
end
|
74
78
|
end
|
75
79
|
end
|
76
|
-
end
|
80
|
+
end
|
@@ -6,9 +6,25 @@ module DropletKit
|
|
6
6
|
password_reset enable_ipv6 enable_backups disable_backups
|
7
7
|
enable_private_networking)
|
8
8
|
|
9
|
+
TAG_ACTIONS = %w(
|
10
|
+
enable_backups disable_backups power_cycle power_on power_off shutdown
|
11
|
+
enable_private_networking enable_ipv6 snapshot
|
12
|
+
)
|
13
|
+
|
9
14
|
resources do
|
10
15
|
default_handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) }
|
11
16
|
|
17
|
+
action :action_for_id, 'POST /v2/droplets/:droplet_id/actions' do
|
18
|
+
body { |hash| hash.tap { |h| h.delete(:droplet_id) }.to_json }
|
19
|
+
handler(201, 200) { |response| ActionMapping.extract_single(response.body, :read) }
|
20
|
+
end
|
21
|
+
|
22
|
+
action :action_for_tag, 'POST /v2/droplets/actions' do
|
23
|
+
query_keys :tag
|
24
|
+
body { |hash| hash.to_json }
|
25
|
+
handler(201, 200) { |response| ActionMapping.extract_single(response.body, :read) }
|
26
|
+
end
|
27
|
+
|
12
28
|
ACTIONS_WITHOUT_INPUT.each do |action_name|
|
13
29
|
action action_name.to_sym, 'POST /v2/droplets/:droplet_id/actions' do
|
14
30
|
body { |_| { type: action_name }.to_json }
|
@@ -16,6 +32,14 @@ module DropletKit
|
|
16
32
|
end
|
17
33
|
end
|
18
34
|
|
35
|
+
TAG_ACTIONS.each do |action_name|
|
36
|
+
action "#{action_name}_for_tag".to_sym, 'POST /v2/droplets/actions' do
|
37
|
+
query_keys :tag
|
38
|
+
body { |_| { type: action_name }.to_json }
|
39
|
+
handler(201, 200) { |response| ActionMapping.extract_collection(response.body, :read) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
19
43
|
action :snapshot, 'POST /v2/droplets/:droplet_id/actions' do
|
20
44
|
body { |hash| { type: 'snapshot', name: hash[:name] }.to_json }
|
21
45
|
handler(201, 200) { |response| ActionMapping.extract_single(response.body, :read) }
|
@@ -4,7 +4,7 @@ module DropletKit
|
|
4
4
|
|
5
5
|
resources do
|
6
6
|
action :all, 'GET /v2/droplets' do
|
7
|
-
query_keys :per_page, :page
|
7
|
+
query_keys :per_page, :page, :tag_name
|
8
8
|
handler(200) { |response| DropletMapping.extract_collection(response.body, :read) }
|
9
9
|
end
|
10
10
|
|
@@ -47,6 +47,12 @@ module DropletKit
|
|
47
47
|
query_keys :per_page, :page
|
48
48
|
handler(200) { |response| ActionMapping.extract_collection(response.body, :read) }
|
49
49
|
end
|
50
|
+
|
51
|
+
action :delete_for_tag, 'DELETE /v2/droplets' do
|
52
|
+
verb :delete
|
53
|
+
query_keys :tag_name
|
54
|
+
handler(204) { |_| true }
|
55
|
+
end
|
50
56
|
end
|
51
57
|
|
52
58
|
def all(*args)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module DropletKit
|
2
|
+
class TagResource < ResourceKit::Resource
|
3
|
+
include ErrorHandlingResourcable
|
4
|
+
|
5
|
+
resources do
|
6
|
+
action :all, 'GET /v2/tags' do
|
7
|
+
query_keys :per_page, :page
|
8
|
+
handler(200) { |response| TagMapping.extract_collection(response.body, :read) }
|
9
|
+
end
|
10
|
+
|
11
|
+
action :find, 'GET /v2/tags/:name' do
|
12
|
+
handler(200) { |response| TagMapping.extract_single(response.body, :read) }
|
13
|
+
end
|
14
|
+
|
15
|
+
action :create, 'POST /v2/tags' do
|
16
|
+
body { |object| TagMapping.representation_for(:create, object) }
|
17
|
+
handler(201) { |response| TagMapping.extract_single(response.body, :read) }
|
18
|
+
handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) }
|
19
|
+
end
|
20
|
+
|
21
|
+
action :update, 'PUT /v2/tags/:name' do
|
22
|
+
body { |object| TagMapping.representation_for(:update, object) }
|
23
|
+
handler(200) { |response| TagMapping.extract_single(response.body, :read) }
|
24
|
+
end
|
25
|
+
|
26
|
+
action :delete, 'DELETE /v2/tags/:name' do
|
27
|
+
handler(204) { |_| true }
|
28
|
+
handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) }
|
29
|
+
end
|
30
|
+
|
31
|
+
action :tag_resources, 'POST /v2/tags/:name/resources' do
|
32
|
+
verb :post
|
33
|
+
body { |hash| { resources: hash[:resources] }.to_json }
|
34
|
+
handler(204) { |_| true }
|
35
|
+
end
|
36
|
+
|
37
|
+
action :untag_resources, 'DELETE /v2/tags/:name/resources' do
|
38
|
+
verb :delete
|
39
|
+
body { |hash| { resources: hash[:resources] }.to_json }
|
40
|
+
handler(204) { |_| true }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def all(*args)
|
45
|
+
PaginatedResource.new(action(:all), self, *args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/droplet_kit/version.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"tags": [
|
3
|
+
{
|
4
|
+
"name": "testing-1",
|
5
|
+
"resources": {
|
6
|
+
"droplets": {
|
7
|
+
"count": 0,
|
8
|
+
"last_tagged": null
|
9
|
+
}
|
10
|
+
}
|
11
|
+
},
|
12
|
+
{
|
13
|
+
"name": "testing-2",
|
14
|
+
"resources": {
|
15
|
+
"droplets": {
|
16
|
+
"count": 0,
|
17
|
+
"last_tagged": null
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
],
|
22
|
+
"links": {},
|
23
|
+
"meta": {
|
24
|
+
"total": 2
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,92 @@
|
|
1
|
+
{
|
2
|
+
"tag": {
|
3
|
+
"name": "testing-1",
|
4
|
+
"resources": {
|
5
|
+
"droplets": {
|
6
|
+
"count": 1,
|
7
|
+
"last_tagged": {
|
8
|
+
"id": 1,
|
9
|
+
"name": "test.example.com",
|
10
|
+
"memory": 1024,
|
11
|
+
"vcpus": 2,
|
12
|
+
"disk": 20,
|
13
|
+
"region": {
|
14
|
+
"slug": "nyc1",
|
15
|
+
"name": "New York",
|
16
|
+
"sizes": [
|
17
|
+
"1024mb",
|
18
|
+
"512mb"
|
19
|
+
],
|
20
|
+
"available": true,
|
21
|
+
"features": [
|
22
|
+
"virtio",
|
23
|
+
"private_networking",
|
24
|
+
"backups",
|
25
|
+
"ipv6"
|
26
|
+
]
|
27
|
+
},
|
28
|
+
"image": {
|
29
|
+
"id": 119192817,
|
30
|
+
"name": "Ubuntu 13.04",
|
31
|
+
"distribution": "ubuntu",
|
32
|
+
"slug": "ubuntu1304",
|
33
|
+
"public": true,
|
34
|
+
"regions": [
|
35
|
+
"nyc1"
|
36
|
+
],
|
37
|
+
"created_at": "2014-07-29T14:35:37Z"
|
38
|
+
},
|
39
|
+
"size_slug": "1024mb",
|
40
|
+
"locked": false,
|
41
|
+
"status": "active",
|
42
|
+
"networks": {
|
43
|
+
"v4": [
|
44
|
+
{
|
45
|
+
"ip_address": "10.0.0.19",
|
46
|
+
"netmask": "255.255.0.0",
|
47
|
+
"gateway": "10.0.0.1",
|
48
|
+
"type": "private"
|
49
|
+
},
|
50
|
+
{
|
51
|
+
"ip_address": "127.0.0.19",
|
52
|
+
"netmask": "255.255.255.0",
|
53
|
+
"gateway": "127.0.0.20",
|
54
|
+
"type": "public"
|
55
|
+
}
|
56
|
+
],
|
57
|
+
"v6": [
|
58
|
+
{
|
59
|
+
"ip_address": "2001::13",
|
60
|
+
"cidr": 124,
|
61
|
+
"gateway": "2400:6180:0000:00D0:0000:0000:0009:7000",
|
62
|
+
"type": "public"
|
63
|
+
}
|
64
|
+
]
|
65
|
+
},
|
66
|
+
"kernel": {
|
67
|
+
"id": 485432985,
|
68
|
+
"name": "DO-recovery-static-fsck",
|
69
|
+
"version": "3.8.0-25-generic"
|
70
|
+
},
|
71
|
+
"created_at": "2014-07-29T14:35:37Z",
|
72
|
+
"features": [
|
73
|
+
"ipv6"
|
74
|
+
],
|
75
|
+
"backup_ids": [
|
76
|
+
449676382
|
77
|
+
],
|
78
|
+
"snapshot_ids": [
|
79
|
+
449676383
|
80
|
+
],
|
81
|
+
"action_ids": [
|
82
|
+
|
83
|
+
],
|
84
|
+
"tags": [
|
85
|
+
"tag-1",
|
86
|
+
"tag-2"
|
87
|
+
]
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'addressable/uri'
|
3
2
|
|
4
3
|
RequestCounter = Struct.new(:count)
|
5
4
|
|
@@ -45,6 +44,16 @@ RSpec.describe DropletKit::PaginatedResource do
|
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
47
|
+
describe '#[]' do
|
48
|
+
subject(:paginated) { DropletKit::PaginatedResource.new(action, resource) }
|
49
|
+
|
50
|
+
it 'returns the nth element in the collection' do
|
51
|
+
paginated.each_with_index do |elem, i|
|
52
|
+
expect(paginated[i]).to eq(elem)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
48
57
|
describe '#each' do
|
49
58
|
subject(:paginated) { DropletKit::PaginatedResource.new(action, resource) }
|
50
59
|
|
@@ -73,4 +82,4 @@ RSpec.describe DropletKit::PaginatedResource do
|
|
73
82
|
end
|
74
83
|
end
|
75
84
|
end
|
76
|
-
end
|
85
|
+
end
|
@@ -8,11 +8,37 @@ RSpec.describe DropletKit::DropletActionResource do
|
|
8
8
|
|
9
9
|
include_context 'resources'
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
describe '#action_for_id' do
|
12
|
+
let(:action) { 'event' }
|
13
|
+
let(:path) { "/v2/droplets/#{droplet_id}/actions" }
|
14
|
+
let(:fixture) { api_fixture('droplet_actions/find') }
|
14
15
|
|
15
|
-
|
16
|
+
it 'performs the action' do
|
17
|
+
request = stub_do_api(path, :post).with(
|
18
|
+
body: { type: action, param_1: 1, param_2: 2 }.to_json
|
19
|
+
).to_return(body: fixture, status: 201)
|
20
|
+
|
21
|
+
resource.action_for_id(droplet_id: droplet_id, type: action, param_1: 1, param_2: 2)
|
22
|
+
expect(request).to have_been_made
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#action_for_tag' do
|
27
|
+
let(:action) { 'event' }
|
28
|
+
let(:path) { '/v2/droplets/actions' }
|
29
|
+
let(:fixture) { api_fixture('droplet_actions/find') }
|
30
|
+
|
31
|
+
it 'performs the action' do
|
32
|
+
request = stub_do_api(path, :post).with(
|
33
|
+
body: { tag: 'test-tag', type: action, param_1: 1, param_2: 2 }.to_json
|
34
|
+
).to_return(body: fixture, status: 201)
|
35
|
+
|
36
|
+
resource.action_for_tag(tag: 'test-tag', type: action, param_1: 1, param_2: 2)
|
37
|
+
expect(request).to have_been_made
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
described_class::ACTIONS_WITHOUT_INPUT.each do |action_name|
|
16
42
|
describe "Action #{action_name}" do
|
17
43
|
let(:action) { action_name }
|
18
44
|
let(:path) { "/v2/droplets/#{droplet_id}/actions" }
|
@@ -35,6 +61,29 @@ RSpec.describe DropletKit::DropletActionResource do
|
|
35
61
|
end
|
36
62
|
end
|
37
63
|
|
64
|
+
described_class::TAG_ACTIONS.each do |action_name|
|
65
|
+
describe "Batch Action #{action_name}" do
|
66
|
+
let(:action) { "#{action_name}_for_tag" }
|
67
|
+
let(:path) { "/v2/droplets/actions?tag=testing-1" }
|
68
|
+
let(:fixture) do
|
69
|
+
single_action = DropletKit::ActionMapping.extract_single(api_fixture("droplet_actions/#{action_name}"), :read)
|
70
|
+
|
71
|
+
DropletKit::ActionMapping.represent_collection_for(:read, [single_action])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'performs the action' do
|
75
|
+
request = stub_do_api(path, :post).with(
|
76
|
+
body: { type: action_name }.to_json
|
77
|
+
).to_return(body: fixture, status: 201)
|
78
|
+
|
79
|
+
returned_actions = resource.send(action, tag: 'testing-1')
|
80
|
+
|
81
|
+
expect(request).to have_been_made
|
82
|
+
expect(returned_actions.first.type).to eq(action_name)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
38
87
|
describe "Action snapshot" do
|
39
88
|
let(:action) { 'snapshot' }
|
40
89
|
|
@@ -5,7 +5,7 @@ RSpec.describe DropletKit::DropletResource do
|
|
5
5
|
include_context 'resources'
|
6
6
|
|
7
7
|
# There's a lot to check
|
8
|
-
def check_droplet(droplet, overrides = {})
|
8
|
+
def check_droplet(droplet, tags = [], overrides = {})
|
9
9
|
attrs = {
|
10
10
|
id: 19,
|
11
11
|
name: 'test.example.com',
|
@@ -25,6 +25,9 @@ RSpec.describe DropletKit::DropletResource do
|
|
25
25
|
expect(droplet.snapshot_ids).to include(449676383)
|
26
26
|
expect(droplet.action_ids).to be_empty
|
27
27
|
expect(droplet.features).to include('ipv6')
|
28
|
+
tags.each do |tag|
|
29
|
+
expect(droplet.tags).to include(tag)
|
30
|
+
end
|
28
31
|
|
29
32
|
expect(droplet.region).to be_kind_of(DropletKit::Region)
|
30
33
|
expect(droplet.region.slug).to eq('nyc1')
|
@@ -74,7 +77,7 @@ RSpec.describe DropletKit::DropletResource do
|
|
74
77
|
droplets = resource.all
|
75
78
|
expect(droplets).to all(be_kind_of(DropletKit::Droplet))
|
76
79
|
|
77
|
-
check_droplet(droplets.first)
|
80
|
+
check_droplet(droplets.first, ['tag-1', 'tag-2'])
|
78
81
|
end
|
79
82
|
|
80
83
|
it 'returns an empty array of droplets' do
|
@@ -94,7 +97,7 @@ RSpec.describe DropletKit::DropletResource do
|
|
94
97
|
stub_do_api('/v2/droplets/20', :get).to_return(body: api_fixture('droplets/find'))
|
95
98
|
droplet = resource.find(id: 20)
|
96
99
|
expect(droplet).to be_kind_of(DropletKit::Droplet)
|
97
|
-
check_droplet(droplet)
|
100
|
+
check_droplet(droplet, ['tag-1', 'tag-2'])
|
98
101
|
end
|
99
102
|
|
100
103
|
it_behaves_like 'resource that handles common errors' do
|
@@ -192,8 +195,8 @@ RSpec.describe DropletKit::DropletResource do
|
|
192
195
|
stub_do_api(path, :post).with(body: as_string).to_return(body: api_fixture('droplets/create_multiple'), status: 202)
|
193
196
|
|
194
197
|
created_droplets = resource.create_multiple(droplet)
|
195
|
-
check_droplet(created_droplets[0], name: 'test-01.example.com')
|
196
|
-
check_droplet(created_droplets[1], id: 20, name: 'test-02.example.com')
|
198
|
+
check_droplet(created_droplets[0], [], name: 'test-01.example.com')
|
199
|
+
check_droplet(created_droplets[1], [], id: 20, name: 'test-02.example.com')
|
197
200
|
end
|
198
201
|
|
199
202
|
it 'reuses the same object' do
|
@@ -207,8 +210,8 @@ RSpec.describe DropletKit::DropletResource do
|
|
207
210
|
json = DropletKit::DropletMapping.representation_for(:create, droplet)
|
208
211
|
stub_do_api(path, :post).with(body: json).to_return(body: api_fixture('droplets/create_multiple'), status: 202)
|
209
212
|
created_droplets = resource.create_multiple(droplet)
|
210
|
-
check_droplet(created_droplets[0], name: 'test-01.example.com')
|
211
|
-
check_droplet(created_droplets[1], id: 20, name: 'test-02.example.com')
|
213
|
+
check_droplet(created_droplets[0], [], name: 'test-01.example.com')
|
214
|
+
check_droplet(created_droplets[1], [], id: 20, name: 'test-02.example.com')
|
212
215
|
end
|
213
216
|
end
|
214
217
|
|
@@ -321,4 +324,13 @@ RSpec.describe DropletKit::DropletResource do
|
|
321
324
|
expect(request).to have_been_made
|
322
325
|
end
|
323
326
|
end
|
327
|
+
|
328
|
+
describe '#delete_tagged' do
|
329
|
+
it 'sends a delete request for the tagged droplet' do
|
330
|
+
request = stub_do_api('/v2/droplets?tag_name=testing-1', :delete)
|
331
|
+
resource.delete_for_tag(tag_name: 'testing-1')
|
332
|
+
|
333
|
+
expect(request).to have_been_made
|
334
|
+
end
|
335
|
+
end
|
324
336
|
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DropletKit::TagResource do
|
4
|
+
subject(:resource) { described_class.new(connection: connection) }
|
5
|
+
include_context 'resources'
|
6
|
+
|
7
|
+
RSpec::Matchers.define :match_tag_fixture do |expected|
|
8
|
+
match do |actual|
|
9
|
+
expect(actual).to be_kind_of(DropletKit::Tag)
|
10
|
+
expect(actual.name).to eq('testing-1')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#all' do
|
15
|
+
it 'returns all of the tags' do
|
16
|
+
stub_do_api('/v2/tags', :get)
|
17
|
+
.to_return(body: api_fixture('tags/all'))
|
18
|
+
tags = resource.all
|
19
|
+
|
20
|
+
expect(tags).to all(be_kind_of(DropletKit::Tag))
|
21
|
+
expect(tags[0].name).to eq('testing-1')
|
22
|
+
expect(tags[1].name).to eq('testing-2')
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when empty' do
|
26
|
+
it 'returns an empty array of tags' do
|
27
|
+
stub_do_api('/v2/tags', :get)
|
28
|
+
.to_return(body: api_fixture('tags/all_empty'))
|
29
|
+
tags = resource.all.map(&:id)
|
30
|
+
|
31
|
+
expect(tags).to be_empty
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it_behaves_like 'a paginated index' do
|
36
|
+
let(:fixture_path) { 'tags/all' }
|
37
|
+
let(:api_path) { '/v2/tags' }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#find' do
|
42
|
+
it 'returns a singular tag' do
|
43
|
+
stub_do_api('/v2/tags/testing-1', :get)
|
44
|
+
.to_return(body: api_fixture('tags/find'))
|
45
|
+
|
46
|
+
tag = resource.find(name: 'testing-1')
|
47
|
+
|
48
|
+
expect(tag).to be_kind_of(DropletKit::Tag)
|
49
|
+
expect(tag).to match_tag_fixture
|
50
|
+
expect(tag.resources.droplets.count).to eq(1)
|
51
|
+
expect(tag.resources.droplets.last_tagged).to be_kind_of(DropletKit::Droplet)
|
52
|
+
expect(tag.resources.droplets.last_tagged.id).to eq(1)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#create' do
|
57
|
+
it 'returns the created tag' do
|
58
|
+
tag = DropletKit::Tag.new(name: 'testing-1')
|
59
|
+
|
60
|
+
as_string = DropletKit::TagMapping.representation_for(:create, tag)
|
61
|
+
|
62
|
+
stub_do_api('/v2/tags', :post).with(body: as_string)
|
63
|
+
.to_return(body: api_fixture('tags/create'), status: 201)
|
64
|
+
created_tag = resource.create(tag)
|
65
|
+
|
66
|
+
expect(created_tag).to match_tag_fixture
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#update' do
|
71
|
+
it 'updateds a tag' do
|
72
|
+
tag = DropletKit::Tag.new(name: 'old-testing-1')
|
73
|
+
tag.name = 'testing-1'
|
74
|
+
|
75
|
+
request = stub_do_api('/v2/tags/old-testing-1', :put)
|
76
|
+
.with(body: DropletKit::TagMapping.representation_for(:update, tag))
|
77
|
+
.to_return(body: api_fixture('tags/find'))
|
78
|
+
|
79
|
+
tag = resource.update(tag, name: 'old-testing-1')
|
80
|
+
expect(tag.name).to eq('testing-1')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#delete' do
|
85
|
+
it 'deletes a tag' do
|
86
|
+
request = stub_do_api('/v2/tags/testing-1', :delete)
|
87
|
+
.to_return(body: '', status: 204)
|
88
|
+
|
89
|
+
resource.delete(name: 'testing-1')
|
90
|
+
expect(request).to have_been_made
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#tag_resources' do
|
95
|
+
it 'adds a tag' do
|
96
|
+
params = {
|
97
|
+
resources: [
|
98
|
+
{
|
99
|
+
resource_id: '1',
|
100
|
+
resource_type: "droplet"
|
101
|
+
}
|
102
|
+
]
|
103
|
+
}
|
104
|
+
|
105
|
+
request = stub_do_api('/v2/tags/testing-1/resources', :post)
|
106
|
+
.with(body: params.to_json)
|
107
|
+
.to_return(body: '', status: 204)
|
108
|
+
|
109
|
+
resource.tag_resources(params.merge(name: 'testing-1'))
|
110
|
+
|
111
|
+
expect(request).to have_been_made
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '#untag_resources' do
|
116
|
+
it 'removes a tag' do
|
117
|
+
params = {
|
118
|
+
resources: [
|
119
|
+
{
|
120
|
+
resource_id: '1',
|
121
|
+
resource_type: "droplet"
|
122
|
+
}
|
123
|
+
]
|
124
|
+
}
|
125
|
+
|
126
|
+
request = stub_do_api('/v2/tags/testing-1/resources', :delete)
|
127
|
+
.with(body: params.to_json)
|
128
|
+
.to_return(body: '', status: 204)
|
129
|
+
|
130
|
+
resource.untag_resources(params.merge(name: 'testing-1'))
|
131
|
+
|
132
|
+
expect(request).to have_been_made
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module RequestStubHelpers
|
2
2
|
def stub_do_api(path, verb = :any)
|
3
|
-
stub_request(verb, %r[#{DropletKit::Client::DIGITALOCEAN_API}#{path}])
|
3
|
+
stub_request(verb, %r[#{DropletKit::Client::DIGITALOCEAN_API}#{Regexp.escape(path)}])
|
4
4
|
end
|
5
5
|
|
6
6
|
def api_fixture(fixture_name)
|
@@ -25,4 +25,4 @@ module RequestStubHelpers
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
|
-
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: droplet_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Ross
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: virtus
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.1.
|
33
|
+
version: 0.1.5
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.1.
|
40
|
+
version: 0.1.5
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: kartograph
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -207,6 +207,9 @@ files:
|
|
207
207
|
- lib/droplet_kit/mappings/size_mapping.rb
|
208
208
|
- lib/droplet_kit/mappings/snapshot_mapping.rb
|
209
209
|
- lib/droplet_kit/mappings/ssh_key_mapping.rb
|
210
|
+
- lib/droplet_kit/mappings/tag_mapping.rb
|
211
|
+
- lib/droplet_kit/mappings/tagged_droplets_resources_mapping.rb
|
212
|
+
- lib/droplet_kit/mappings/tagged_resources_mapping.rb
|
210
213
|
- lib/droplet_kit/models/account.rb
|
211
214
|
- lib/droplet_kit/models/action.rb
|
212
215
|
- lib/droplet_kit/models/backup.rb
|
@@ -227,6 +230,9 @@ files:
|
|
227
230
|
- lib/droplet_kit/models/size.rb
|
228
231
|
- lib/droplet_kit/models/snapshot.rb
|
229
232
|
- lib/droplet_kit/models/ssh_key.rb
|
233
|
+
- lib/droplet_kit/models/tag.rb
|
234
|
+
- lib/droplet_kit/models/tagged_droplets_resources.rb
|
235
|
+
- lib/droplet_kit/models/tagged_resources.rb
|
230
236
|
- lib/droplet_kit/paginated_resource.rb
|
231
237
|
- lib/droplet_kit/resources/account_resource.rb
|
232
238
|
- lib/droplet_kit/resources/action_resource.rb
|
@@ -242,6 +248,7 @@ files:
|
|
242
248
|
- lib/droplet_kit/resources/region_resource.rb
|
243
249
|
- lib/droplet_kit/resources/size_resource.rb
|
244
250
|
- lib/droplet_kit/resources/ssh_key_resource.rb
|
251
|
+
- lib/droplet_kit/resources/tag_resource.rb
|
245
252
|
- lib/droplet_kit/version.rb
|
246
253
|
- lib/tasks/resource_doc.rake
|
247
254
|
- spec/fixtures/account/info.json
|
@@ -305,6 +312,10 @@ files:
|
|
305
312
|
- spec/fixtures/ssh_keys/create.json
|
306
313
|
- spec/fixtures/ssh_keys/find.json
|
307
314
|
- spec/fixtures/ssh_keys/update.json
|
315
|
+
- spec/fixtures/tags/all.json
|
316
|
+
- spec/fixtures/tags/all_empty.json
|
317
|
+
- spec/fixtures/tags/create.json
|
318
|
+
- spec/fixtures/tags/find.json
|
308
319
|
- spec/lib/droplet_kit/client_spec.rb
|
309
320
|
- spec/lib/droplet_kit/models/base_model_spec.rb
|
310
321
|
- spec/lib/droplet_kit/models/droplet_spec.rb
|
@@ -323,6 +334,7 @@ files:
|
|
323
334
|
- spec/lib/droplet_kit/resources/region_resource_spec.rb
|
324
335
|
- spec/lib/droplet_kit/resources/size_resource_spec.rb
|
325
336
|
- spec/lib/droplet_kit/resources/ssh_key_resource_spec.rb
|
337
|
+
- spec/lib/droplet_kit/resources/tag_resource_spec.rb
|
326
338
|
- spec/spec_helper.rb
|
327
339
|
- spec/support/fake_server.rb
|
328
340
|
- spec/support/request_stub_helpers.rb
|
@@ -350,7 +362,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
350
362
|
version: '0'
|
351
363
|
requirements: []
|
352
364
|
rubyforge_project:
|
353
|
-
rubygems_version: 2.
|
365
|
+
rubygems_version: 2.5.1
|
354
366
|
signing_key:
|
355
367
|
specification_version: 4
|
356
368
|
summary: Droplet Kit is the official Ruby library for DigitalOcean's API
|
@@ -416,6 +428,10 @@ test_files:
|
|
416
428
|
- spec/fixtures/ssh_keys/create.json
|
417
429
|
- spec/fixtures/ssh_keys/find.json
|
418
430
|
- spec/fixtures/ssh_keys/update.json
|
431
|
+
- spec/fixtures/tags/all.json
|
432
|
+
- spec/fixtures/tags/all_empty.json
|
433
|
+
- spec/fixtures/tags/create.json
|
434
|
+
- spec/fixtures/tags/find.json
|
419
435
|
- spec/lib/droplet_kit/client_spec.rb
|
420
436
|
- spec/lib/droplet_kit/models/base_model_spec.rb
|
421
437
|
- spec/lib/droplet_kit/models/droplet_spec.rb
|
@@ -434,6 +450,7 @@ test_files:
|
|
434
450
|
- spec/lib/droplet_kit/resources/region_resource_spec.rb
|
435
451
|
- spec/lib/droplet_kit/resources/size_resource_spec.rb
|
436
452
|
- spec/lib/droplet_kit/resources/ssh_key_resource_spec.rb
|
453
|
+
- spec/lib/droplet_kit/resources/tag_resource_spec.rb
|
437
454
|
- spec/spec_helper.rb
|
438
455
|
- spec/support/fake_server.rb
|
439
456
|
- spec/support/request_stub_helpers.rb
|