digitalocean 0.0.3 → 1.0.0.rc.2
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.
- data/LICENSE.txt +2 -2
- data/README.md +75 -47
- data/lib/digitalocean.rb +138 -16
- data/lib/digitalocean/version.rb +1 -1
- data/spec/digitalocean/domain_spec.rb +34 -50
- data/spec/digitalocean/droplet_spec.rb +125 -36
- data/spec/digitalocean/image_spec.rb +49 -23
- data/spec/digitalocean/integration_spec.rb +12 -0
- data/spec/digitalocean/record_spec.rb +63 -74
- data/spec/digitalocean/region_spec.rb +5 -14
- data/spec/digitalocean/size_spec.rb +5 -14
- data/spec/digitalocean/ssh_key_spec.rb +29 -13
- data/spec/digitalocean_spec.rb +4 -3
- data/spec/spec_helper.rb +0 -10
- metadata +40 -31
- checksums.yaml +0 -7
- data/.env-example +0 -2
- data/lib/digitalocean/domain.rb +0 -34
- data/lib/digitalocean/droplet.rb +0 -67
- data/lib/digitalocean/image.rb +0 -23
- data/lib/digitalocean/record.rb +0 -62
- data/lib/digitalocean/region.rb +0 -14
- data/lib/digitalocean/size.rb +0 -14
- data/lib/digitalocean/ssh_key.rb +0 -21
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2014 Scott Motte
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -6,71 +6,88 @@ This gem is a wrapper for [DigitalOcean.com](https://www.digitalocean.com)'s API
|
|
6
6
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
9
|
-
|
9
|
+
```
|
10
|
+
gem 'digitalocean'
|
11
|
+
```
|
10
12
|
|
11
13
|
And then execute:
|
12
14
|
|
13
|
-
|
15
|
+
```
|
16
|
+
bundle
|
17
|
+
```
|
14
18
|
|
15
19
|
Or install it yourself as:
|
16
20
|
|
17
|
-
|
21
|
+
```
|
22
|
+
gem install digitalocean
|
23
|
+
```
|
18
24
|
|
19
25
|
Then in your application initialize the gem:
|
20
26
|
|
21
|
-
|
22
|
-
|
27
|
+
```ruby
|
28
|
+
Digitalocean.client_id = "your_client_id"
|
29
|
+
Digitalocean.api_key = "your_api_key"
|
30
|
+
```
|
23
31
|
|
24
32
|
## Usage
|
25
33
|
|
26
34
|
### List Droplets
|
27
35
|
|
28
|
-
|
36
|
+
```ruby
|
37
|
+
Digitalocean::Droplet.all
|
38
|
+
```
|
29
39
|
|
30
|
-
###
|
40
|
+
### Find Droplet
|
31
41
|
|
32
|
-
|
42
|
+
```ruby
|
43
|
+
Digitalocean::Droplet.find("id_of_droplet")
|
44
|
+
```
|
33
45
|
|
34
46
|
### Create Droplet
|
35
47
|
|
36
|
-
|
37
|
-
|
48
|
+
```ruby
|
49
|
+
Digitalocean::Droplet.create({:name => droplet_name, :size_id => size_id, :image_id => image_id, :region_id => region_id)
|
50
|
+
```
|
38
51
|
## Available Commands
|
39
52
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
53
|
+
```ruby
|
54
|
+
Digitalocean::Domain.all
|
55
|
+
Digitalocean::Domain.find(id)
|
56
|
+
Digitalocean::Domain.create({name: name, ip_address: ip_address})
|
57
|
+
Digitalocean::Domain.destroy(id)
|
58
|
+
|
59
|
+
Digitalocean::Droplet.all
|
60
|
+
Digitalocean::Droplet.find(id)
|
61
|
+
Digitalocean::Droplet.rename(id, {name: name})
|
62
|
+
Digitalocean::Droplet.reboot(id)
|
63
|
+
Digitalocean::Droplet.power_cycle(id)
|
64
|
+
Digitalocean::Droplet.shut_down(id)
|
65
|
+
Digitalocean::Droplet.power_off(id)
|
66
|
+
Digitalocean::Droplet.power_on(id)
|
67
|
+
Digitalocean::Droplet.snapshot(id, {name: name})
|
68
|
+
Digitalocean::Droplet.create({name: name, size_id: size_id, image_id: image_id, region_id: region_id, ssh_key_ids: ssh_key_ids})
|
69
|
+
Digitalocean::Droplet.destroy(id)
|
70
|
+
|
71
|
+
Digitalocean::Image.all
|
72
|
+
Digitalocean::Image.all({filter: "my_images"})
|
73
|
+
Digitalocean::Image.find(id)
|
74
|
+
Digitalocean::Image.destroy(id)
|
75
|
+
Digitalocean::Image.transfer(id, {region_id: region_id})
|
76
|
+
|
77
|
+
Digitalocean::Record.all(domain_id)
|
78
|
+
Digitalocean::Record.find(domain_id, record_id)
|
79
|
+
Digitalocean::Record.create(domain_id, {record_type: record_type, data: data})
|
80
|
+
Digitalocean::Record.edit(domain_id, record_id, {record_type: record_type, data: data})
|
81
|
+
Digitalocean::Record.destroy(domain_id, record_id)
|
82
|
+
|
83
|
+
Digitalocean::Region.all
|
84
|
+
|
85
|
+
Digitalocean::Size.all
|
86
|
+
|
87
|
+
Digitalocean::SshKey.all
|
88
|
+
Digitalocean::SshKey.find(id)
|
89
|
+
Digitalocean::SshKey.create({name: name, ssh_pub_key: ssh_pub_key})
|
90
|
+
```
|
74
91
|
|
75
92
|
## Contributing
|
76
93
|
|
@@ -80,8 +97,19 @@ Then in your application initialize the gem:
|
|
80
97
|
6. Push to the branch (`git push origin my-new-feature`)
|
81
98
|
7. Create new Pull Request
|
82
99
|
|
100
|
+
When adding methods, add to the list of DEFINITIONS in `lib/digitalocean.rb`. Additionally, write a spec and add it to the list in the README.
|
101
|
+
|
83
102
|
## Running Specs
|
84
103
|
|
85
|
-
|
86
|
-
|
87
|
-
|
104
|
+
```
|
105
|
+
bundle exec rspec spec/*
|
106
|
+
```
|
107
|
+
|
108
|
+
## Publish to RubyGems.org
|
109
|
+
|
110
|
+
You first need to request access from [scottmotte](http://github.com/scottmotte).
|
111
|
+
|
112
|
+
```
|
113
|
+
gem build digitalocean.gemspec
|
114
|
+
gem push digitalocean-1.0.0.rc.1.gem
|
115
|
+
```
|
data/lib/digitalocean.rb
CHANGED
@@ -2,18 +2,84 @@ require "faraday"
|
|
2
2
|
require "faraday_middleware"
|
3
3
|
require "recursive-open-struct"
|
4
4
|
require "digitalocean/version"
|
5
|
-
require "digitalocean/droplet"
|
6
|
-
require "digitalocean/image"
|
7
|
-
require "digitalocean/region"
|
8
|
-
require "digitalocean/size"
|
9
|
-
require "digitalocean/ssh_key"
|
10
|
-
require "digitalocean/domain"
|
11
|
-
require "digitalocean/record"
|
12
|
-
|
13
5
|
|
14
6
|
module Digitalocean
|
15
7
|
extend self
|
16
8
|
|
9
|
+
DEFINITIONS = {
|
10
|
+
"Domain" => {
|
11
|
+
"all" => "https://api.digitalocean.com/domains?client_id=[your_client_id]&api_key=[your_api_key]",
|
12
|
+
"find" => "https://api.digitalocean.com/domains/[domain_id]?client_id=[your_client_id]&api_key=[your_api_key]",
|
13
|
+
"create" => "https://api.digitalocean.com/domains/new?client_id=[your_client_id]&api_key=[your_api_key]&name=[domain]&ip_address=[ip_address]",
|
14
|
+
"destroy" => "https://api.digitalocean.com/domains/[domain_id]/destroy?client_id=[your_client_id]&api_key=[your_api_key]"
|
15
|
+
},
|
16
|
+
"Droplet" => {
|
17
|
+
"all" => "https://api.digitalocean.com/droplets/?client_id=[your_client_id]&api_key=[your_api_key]",
|
18
|
+
"find" => "https://api.digitalocean.com/droplets/[droplet_id]?client_id=[your_client_id]&api_key=[your_api_key]",
|
19
|
+
"rename" => "https://api.digitalocean.com/droplets/[droplet_id]/rename/?client_id=[your_client_id]&api_key=[your_api_key]&name=[name]",
|
20
|
+
"reboot" => "https://api.digitalocean.com/droplets/[droplet_id]/reboot/?client_id=[your_client_id]&api_key=[your_api_key]",
|
21
|
+
"power_cycle" => "https://api.digitalocean.com/droplets/[droplet_id]/power_cycle/?client_id=[your_client_id]&api_key=[your_api_key]",
|
22
|
+
"shut_down" => "https://api.digitalocean.com/droplets/[droplet_id]/shut_down/?client_id=[your_client_id]&api_key=[your_api_key]",
|
23
|
+
"power_off" => "https://api.digitalocean.com/droplets/[droplet_id]/power_off/?client_id=[your_client_id]&api_key=[your_api_key]",
|
24
|
+
"power_on" => "https://api.digitalocean.com/droplets/[droplet_id]/power_on/?client_id=[your_client_id]&api_key=[your_api_key]",
|
25
|
+
"snapshot" => "https://api.digitalocean.com/droplets/[droplet_id]/snapshot/?name=[snapshot_name]&client_id=[your_client_id]&api_key=[your_api_key]",
|
26
|
+
"create" => "https://api.digitalocean.com/droplets/new?client_id=[your_client_id]&api_key=[your_api_key]&name=[droplet_name]&size_id=[size_id]&image_id=[image_id]®ion_id=[region_id]&ssh_key_ids=[ssh_key_ids]", # unique case that is not copy/paste
|
27
|
+
"destroy" => "https://api.digitalocean.com/droplets/[droplet_id]/destroy/?client_id=[your_client_id]&api_key=[your_api_key]"
|
28
|
+
},
|
29
|
+
"Image" => {
|
30
|
+
"all" => "https://api.digitalocean.com/images/?client_id=[your_client_id]&api_key=[your_api_key]",
|
31
|
+
"find" => "https://api.digitalocean.com/images/[image_id]/?client_id=[your_client_id]&api_key=[your_api_key]",
|
32
|
+
"destroy" => "https://api.digitalocean.com/images/[image_id]/destroy/?client_id=[your_client_id]&api_key=[your_api_key]",
|
33
|
+
"transfer" => "https://api.digitalocean.com/images/[image_id]/transfer/?client_id=[your_client_id]&api_key=[your_api_key]®ion_id=[region_id]"
|
34
|
+
},
|
35
|
+
"Record" => {
|
36
|
+
"all" => "https://api.digitalocean.com/domains/[domain_id]/records?client_id=[your_client_id]&api_key=[your_api_key]",
|
37
|
+
"find" => "https://api.digitalocean.com/domains/[domain_id]/records/[record_id]?client_id=[your_client_id]&api_key=[your_api_key]",
|
38
|
+
"create" => "https://api.digitalocean.com/domains/[domain_id]/records/new?client_id=[your_client_id]&api_key=[your_api_key]&record_type=[record_type]&data=[data]",
|
39
|
+
"edit" => "https://api.digitalocean.com/domains/[domain_id]/records/[record_id]/edit?client_id=[your_client_id]&api_key=[your_api_key]",
|
40
|
+
"destroy" => "https://api.digitalocean.com/domains/[domain_id]/records/[record_id]/destroy?client_id=[your_client_id]&api_key=[your_api_key]"
|
41
|
+
},
|
42
|
+
"Region" => {
|
43
|
+
"all" => "https://api.digitalocean.com/regions/?client_id=[your_client_id]&api_key=[your_api_key]"
|
44
|
+
},
|
45
|
+
"Size" => {
|
46
|
+
"all" => "https://api.digitalocean.com/sizes/?client_id=[your_client_id]&api_key=[your_api_key]"
|
47
|
+
},
|
48
|
+
"SshKey" => {
|
49
|
+
"all" => "https://api.digitalocean.com/ssh_keys/?client_id=[your_client_id]&api_key=[your_api_key]",
|
50
|
+
"find" => "https://api.digitalocean.com/ssh_keys/[ssh_key_id]/?client_id=[your_client_id]&api_key=[your_api_key]",
|
51
|
+
"create" => "https://api.digitalocean.com/ssh_keys/new/?name=[ssh_key_name]&ssh_pub_key=[ssh_public_key]&client_id=[your_client_id]&api_key=[your_api_key]"
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
DEFINITIONS.each do |resource|
|
56
|
+
resource_name = resource[0]
|
57
|
+
|
58
|
+
resource_class = Class.new(Object) do
|
59
|
+
# http://stackoverflow.com/questions/3026943/define-method-for-instance-of-class
|
60
|
+
singleton = class << self; self end
|
61
|
+
|
62
|
+
DEFINITIONS[resource_name].each do |method_name, url|
|
63
|
+
parts = url.split("?")
|
64
|
+
pre_query = parts[0]
|
65
|
+
post_query = parts[1]
|
66
|
+
|
67
|
+
singleton.send :define_method, "_#{method_name}" do |*args|
|
68
|
+
pre_query = Digitalocean.process_standard_args_from_part(pre_query, args)
|
69
|
+
post_query = Digitalocean.process_hash_args_from_part(post_query, args)
|
70
|
+
|
71
|
+
[pre_query, post_query].join("?")
|
72
|
+
end
|
73
|
+
|
74
|
+
singleton.send :define_method, method_name do |*args|
|
75
|
+
Digitalocean.request_and_respond send("_#{method_name}", *args)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Digitalocean.const_set(resource_name, resource_class)
|
81
|
+
end
|
82
|
+
|
17
83
|
def request=(request)
|
18
84
|
@request = request
|
19
85
|
end
|
@@ -31,7 +97,7 @@ module Digitalocean
|
|
31
97
|
|
32
98
|
def client_id
|
33
99
|
return @client_id if @client_id
|
34
|
-
"
|
100
|
+
"client_id_required"
|
35
101
|
end
|
36
102
|
|
37
103
|
def api_key=(api_key)
|
@@ -43,15 +109,73 @@ module Digitalocean
|
|
43
109
|
|
44
110
|
def api_key
|
45
111
|
return @api_key if @api_key
|
46
|
-
"
|
112
|
+
"api_key_required"
|
47
113
|
end
|
48
114
|
|
49
115
|
def api_endpoint
|
50
116
|
"https://api.digitalocean.com"
|
51
117
|
end
|
52
118
|
|
53
|
-
def
|
54
|
-
|
119
|
+
def request_and_respond(url)
|
120
|
+
resp = Digitalocean.request.get url
|
121
|
+
hash = RecursiveOpenStruct.new(resp.body, :recurse_over_arrays => true)
|
122
|
+
|
123
|
+
hash
|
124
|
+
end
|
125
|
+
|
126
|
+
def process_standard_args_from_part(part, args)
|
127
|
+
parts = part.split(/\[|\]/)
|
128
|
+
|
129
|
+
if parts.length > 1
|
130
|
+
parts.each_with_index do |v, i|
|
131
|
+
is_every_other = (i%2 == 1)
|
132
|
+
parts[i] = args.shift if is_every_other
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
parts.join("")
|
137
|
+
end
|
138
|
+
|
139
|
+
def process_client_id_and_api_key(parts)
|
140
|
+
# Begin by taking care of the client_id and api_key
|
141
|
+
client_id_index = parts.index "client_id="
|
142
|
+
client_id_index = parts.index "&client_id=" if !client_id_index
|
143
|
+
parts[client_id_index+1] = client_id if client_id_index
|
144
|
+
|
145
|
+
api_key_index = parts.index "api_key="
|
146
|
+
api_key_index = parts.index "&api_key=" if !api_key_index
|
147
|
+
parts[api_key_index+1] = api_key if api_key_index
|
148
|
+
|
149
|
+
parts
|
150
|
+
end
|
151
|
+
|
152
|
+
def process_hash_args_from_part(part, args)
|
153
|
+
parts = part.split(/\[|\]/)
|
154
|
+
parts = process_client_id_and_api_key(parts)
|
155
|
+
|
156
|
+
hash = args[-1]
|
157
|
+
if hash.is_a?(Hash)
|
158
|
+
if parts.length > 1
|
159
|
+
hash.each do |key, value|
|
160
|
+
query_setter = "#{key}="
|
161
|
+
query_arg_index = parts.index query_setter
|
162
|
+
query_arg_index = parts.index "&#{query_setter}" if !query_arg_index # handle case of ampersand
|
163
|
+
|
164
|
+
if query_arg_index != nil
|
165
|
+
parts[query_arg_index+1] = value
|
166
|
+
hash.delete(key) # cleanup
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# append any additional hash arguments (optional params)
|
172
|
+
hash.each do |key, value|
|
173
|
+
appendable_param = "&#{key}=#{value}"
|
174
|
+
parts.push(appendable_param)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
parts.join("")
|
55
179
|
end
|
56
180
|
|
57
181
|
private
|
@@ -59,9 +183,7 @@ module Digitalocean
|
|
59
183
|
def setup_request!
|
60
184
|
options = {
|
61
185
|
:headers => {'Accept' => "application/json"},
|
62
|
-
:ssl => {:verify => false}
|
63
|
-
:url => Digitalocean.api_endpoint,
|
64
|
-
:params => Digitalocean.credential_attrs
|
186
|
+
:ssl => {:verify => false}
|
65
187
|
}
|
66
188
|
|
67
189
|
Digitalocean.request = ::Faraday::Connection.new(options) do |builder|
|
@@ -71,4 +193,4 @@ module Digitalocean
|
|
71
193
|
builder.adapter ::Faraday.default_adapter
|
72
194
|
end
|
73
195
|
end
|
74
|
-
end
|
196
|
+
end
|
data/lib/digitalocean/version.rb
CHANGED
@@ -1,69 +1,53 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Digitalocean::Domain do
|
4
|
-
let(:ok) { "OK" }
|
5
4
|
let(:subject) { Digitalocean::Domain }
|
6
5
|
|
7
|
-
|
6
|
+
describe "._all" do
|
8
7
|
before do
|
9
|
-
|
8
|
+
@url = subject._all
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
context "default" do
|
18
|
-
it do
|
19
|
-
@response.status.should eq ok
|
20
|
-
end
|
21
|
-
end
|
11
|
+
it do
|
12
|
+
@url.should eq "https://api.digitalocean.com/domains?client_id=client_id_required&api_key=api_key_required"
|
13
|
+
end
|
14
|
+
end
|
22
15
|
|
23
|
-
|
24
|
-
|
25
|
-
domain_id = @response.domains.first.id
|
26
|
-
@response2 = subject.find(domain_id)
|
27
|
-
end
|
16
|
+
describe "._find" do
|
17
|
+
let(:domain_id) { "1234" }
|
28
18
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
19
|
+
before do
|
20
|
+
@url = subject._find(domain_id)
|
21
|
+
end
|
35
22
|
|
36
|
-
|
37
|
-
|
23
|
+
it do
|
24
|
+
@url.should eq "https://api.digitalocean.com/domains/#{domain_id}?client_id=client_id_required&api_key=api_key_required"
|
25
|
+
end
|
26
|
+
end
|
38
27
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
28
|
+
describe "._create" do
|
29
|
+
let(:name) { "test_domain" }
|
30
|
+
let(:ip_address) { "test_ip_address" }
|
31
|
+
let(:args) { {name: name, ip_address: ip_address } }
|
43
32
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
33
|
+
before do
|
34
|
+
@url = subject._create(args)
|
35
|
+
end
|
50
36
|
|
51
|
-
|
52
|
-
|
37
|
+
it do
|
38
|
+
@url.should eq "https://api.digitalocean.com/domains/new?client_id=client_id_required&api_key=api_key_required&name=test_domain&ip_address=test_ip_address"
|
39
|
+
end
|
40
|
+
end
|
53
41
|
|
54
|
-
|
55
|
-
|
56
|
-
@response_create = subject.create(domain_name, droplet.ip_address)
|
57
|
-
@response_destroy = subject.destroy(@response_create.id)
|
58
|
-
end
|
42
|
+
describe "._destroy" do
|
43
|
+
let(:domain_id) { "test_domain_id" }
|
59
44
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
45
|
+
before do
|
46
|
+
@url = subject._destroy(domain_id)
|
47
|
+
end
|
66
48
|
|
49
|
+
it do
|
50
|
+
@url.should eq "https://api.digitalocean.com/domains/test_domain_id/destroy?client_id=client_id_required&api_key=api_key_required"
|
67
51
|
end
|
68
52
|
end
|
69
|
-
end
|
53
|
+
end
|