├── Rakefile ├── .travis.yml ├── Gemfile ├── lib ├── scaleway │ └── version.rb └── scaleway.rb ├── CHANGES.md ├── .gitignore ├── Gemfile.lock ├── scaleway.gemspec ├── spec ├── scaleway_spec.rb └── servers_spec.rb └── README.md /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: "bundle exec rspec" 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec -------------------------------------------------------------------------------- /lib/scaleway/version.rb: -------------------------------------------------------------------------------- 1 | module Scaleway 2 | VERSION = '1.0.1' 3 | end 4 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | ## 1.0.1 (Unreleased) 4 | 5 | - No entry. 6 | 7 | ## 1.0.0 (2016-10-31) 8 | 9 | - Add explicit error on missing authentication. 10 | - Add zone option. 11 | - Update default commercial type to VC1S. 12 | - Add marketplace API. 13 | 14 | ## 0.2.1 (2015-04-32) 15 | 16 | - Fix unittests. 17 | 18 | ## 0.2.0 (2015-04-23) 19 | 20 | - Rename to scaleway. 21 | - Add the ability to reboot. 22 | 23 | ## 0.1.1 (2014-11-14) 24 | 25 | - Fix push invalid gem. 26 | 27 | ## 0.1.0 (2014-11-14) 28 | 29 | - Initial setup. 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /test/tmp/ 9 | /test/version_tmp/ 10 | /tmp/ 11 | 12 | ## Specific to RubyMotion: 13 | .dat* 14 | .repl_history 15 | build/ 16 | 17 | ## Documentation cache and generated files: 18 | /.yardoc/ 19 | /_yardoc/ 20 | /doc/ 21 | /rdoc/ 22 | 23 | ## Environment normalisation: 24 | /.bundle/ 25 | /lib/bundler/man/ 26 | 27 | # for a library or gem, you might want to ignore these files since the code is 28 | # intended to run in multiple environments; otherwise, check them in: 29 | # Gemfile.lock 30 | # .ruby-version 31 | # .ruby-gemset 32 | 33 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 34 | .rvmrc 35 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | scaleway (1.0.1) 5 | faraday (~> 0.9) 6 | faraday_middleware (~> 0.9) 7 | recursive-open-struct (~> 1.0) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | diff-lcs (1.3) 13 | faraday (0.11.0) 14 | multipart-post (>= 1.2, < 3) 15 | faraday_middleware (0.11.0.1) 16 | faraday (>= 0.7.4, < 1.0) 17 | its (0.2.0) 18 | rspec-core 19 | multipart-post (2.0.0) 20 | rake (10.5.0) 21 | recursive-open-struct (1.0.2) 22 | rspec (3.5.0) 23 | rspec-core (~> 3.5.0) 24 | rspec-expectations (~> 3.5.0) 25 | rspec-mocks (~> 3.5.0) 26 | rspec-core (3.5.4) 27 | rspec-support (~> 3.5.0) 28 | rspec-expectations (3.5.0) 29 | diff-lcs (>= 1.2.0, < 2.0) 30 | rspec-support (~> 3.5.0) 31 | rspec-mocks (3.5.0) 32 | diff-lcs (>= 1.2.0, < 2.0) 33 | rspec-support (~> 3.5.0) 34 | rspec-support (3.5.0) 35 | 36 | PLATFORMS 37 | ruby 38 | 39 | DEPENDENCIES 40 | its (~> 0.2) 41 | rake (~> 10.1) 42 | rspec (~> 3.1) 43 | scaleway! 44 | 45 | BUNDLED WITH 46 | 1.13.6 47 | -------------------------------------------------------------------------------- /scaleway.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'scaleway/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = "scaleway" 8 | gem.version = Scaleway::VERSION 9 | gem.authors = ["bchatelard"] 10 | gem.email = ["chatel.bast@gmail.com"] 11 | gem.description = %q{Ruby bindings for the Scaleway API.} 12 | gem.summary = %q{Ruby bindings for the Scaleway API.} 13 | 14 | # should change 15 | gem.homepage = "http://github.com/bchatelard/scaleway-ruby" 16 | 17 | gem.add_dependency "faraday", "~> 0.9" 18 | gem.add_dependency "faraday_middleware", "~> 0.9" 19 | gem.add_dependency "recursive-open-struct", "~> 1.0" 20 | 21 | gem.add_development_dependency "rake", "~> 10.1" 22 | gem.add_development_dependency "rspec", "~> 3.1" 23 | gem.add_development_dependency "its", "~> 0.2" 24 | 25 | gem.files = `git ls-files`.split($/) 26 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 27 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 28 | gem.require_paths = ["lib"] 29 | end 30 | -------------------------------------------------------------------------------- /spec/scaleway_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'its' 3 | require 'scaleway' 4 | 5 | describe Scaleway do 6 | subject(:Scaleway) { described_class } 7 | 8 | before do 9 | Scaleway.organization = organization 10 | Scaleway.token = token 11 | Scaleway.zone = zone 12 | end 13 | 14 | describe "defaults" do 15 | let(:organization) { nil } 16 | let(:token) { nil } 17 | let(:zone) { nil } 18 | 19 | its(:compute_endpoint) { should eq "https://cp-par1.scaleway.com" } 20 | its(:account_endpoint) { should eq "https://account.scaleway.com" } 21 | its(:token) { should eq "token_required" } 22 | its(:organization) { should eq "organization_required" } 23 | 24 | it { expect(Scaleway::VERSION).to eq "1.0.1" } 25 | end 26 | 27 | describe "test ams1" do 28 | let(:organization) { nil } 29 | let(:token) { nil } 30 | let(:zone) { "ams1" } 31 | 32 | its(:compute_endpoint) { should eq "https://cp-ams1.scaleway.com" } 33 | end 34 | 35 | describe "test token and organization" do 36 | let(:organization) { "organization_id" } 37 | let(:token) { "token_id" } 38 | let(:zone) { nil } 39 | 40 | its(:organization) { should eq "organization_id" } 41 | its(:token) { should eq "token_id" } 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/servers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'its' 3 | require 'scaleway' 4 | 5 | describe Scaleway::Server do 6 | subject(:server) { described_class } 7 | 8 | before do 9 | stubs = Faraday::Adapter::Test::Stubs.new do |stub| 10 | stub.get('/servers') { |env| [200, {}, {:servers => [test_server]}] } 11 | stub.get('/servers/1234') { |env| [200, {}, {:server => test_server}] } 12 | stub.get('/servers/not_found') { |env| [404, {}, {:type => 'not_found', :message => 'not found'}] } 13 | end 14 | Scaleway.request = Faraday.new do |builder| 15 | builder.adapter :test, stubs 16 | end 17 | end 18 | 19 | describe "all" do 20 | let(:test_server) { RecursiveOpenStruct.new({:name => 'hello', :id => '1234'}, :recurse_over_arrays => true) } 21 | 22 | its(:all) { should eq [test_server] } 23 | end 24 | 25 | describe "item" do 26 | let(:test_server) { RecursiveOpenStruct.new({:name => 'hello', :id => '1234'}, :recurse_over_arrays => true) } 27 | 28 | its(:find, '1234') { should eq test_server } 29 | end 30 | 31 | describe "not found" do 32 | let(:test_server) { RecursiveOpenStruct.new({:name => 'hello', :id => '1234'}, :recurse_over_arrays => true) } 33 | 34 | it { expect{Scaleway::Server.find('not_found')}.to raise_error(Scaleway::NotFound) } 35 | end 36 | 37 | end 38 | 39 | 40 | describe Scaleway::Server do 41 | subject(:server) { described_class } 42 | 43 | before do 44 | stubs = Faraday::Adapter::Test::Stubs.new do |stub| 45 | stub.get('/servers?state=running') { |env| [200, {}, {:servers => []}] } 46 | stub.get('/servers?invalid_filter=42') { |env| [400, {}, {:type => 'invalid_filter', :message => 'invalid filter'}] } 47 | end 48 | Scaleway.request = Faraday.new do |builder| 49 | builder.adapter :test, stubs 50 | end 51 | end 52 | 53 | describe "filter" do 54 | its(:all, state: 'running') { should eq [] } 55 | end 56 | 57 | describe "invalid filter" do 58 | it { expect{Scaleway::Server.all(invalid_filter: 42)}.to raise_error(Scaleway::APIError) } 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scaleway Rubygem 2 | 3 | [![Build Status](https://travis-ci.org/bchatelard/scaleway-ruby.svg?branch=develop)](https://travis-ci.org/bchatelard/scaleway-ruby) 4 | [![Gem Version](https://badge.fury.io/rb/scaleway.svg)](http://badge.fury.io/rb/scaleway) 5 | 6 | Easy to use Scaleway api client. 7 | 8 | ## Installation 9 | 10 | Manual instalation: 11 | 12 | ```bash 13 | gem install scaleway 14 | ``` 15 | 16 | add this to your gemfile: 17 | 18 | ```bash 19 | gem 'scaleway' 20 | ``` 21 | 22 | ## Usage 23 | 24 | ### Configure the client 25 | 26 | ```ruby 27 | require 'scaleway' 28 | 29 | Scaleway.organization = 30 | Scaleway.token = 31 | 32 | # You can specify the zone, the default zone is 'par1' 33 | Scaleway.zone = 'ams1' 34 | ``` 35 | 36 | ### Servers 37 | 38 | ```ruby 39 | # list all servers 40 | Scaleway::Server.all 41 | 42 | # list with filter 43 | Scaleway::Server.all state: :running 44 | 45 | # create a new server with default values 46 | Scaleway::Server.create 47 | 48 | # create a new server with name and tags 49 | Scaleway::Server.create :name => 'my_new_server', tags: ['prod'] 50 | 51 | # get a server by id 52 | server = Scaleway::Server.find 53 | 54 | # edit a server 55 | server = Scaleway::Server.find 56 | server.name = 'new_name' 57 | Scaleway::Server.edit server.id, server 58 | 59 | # actions on a server 60 | Scaleway::Server.power_on server.id 61 | 62 | Scaleway::Server.power_off server.id 63 | 64 | Scaleway::Server.terminate server.id 65 | 66 | # destroy a server 67 | Scaleway::Server.destroy server.id 68 | ``` 69 | 70 | ### Marketplace 71 | 72 | ```ruby 73 | # list all marketplace images 74 | Scaleway::Marketplace.all 75 | 76 | # get local image by id 77 | image = Scaleway::Marketplace.find_local_image 78 | 79 | # get local image by marketplace image id for arch arm 80 | image = Scaleway::Marketplace.find_local_image , arch: 'arm' 81 | 82 | # get local image by name 83 | image = Scaleway::Marketplace.find_local_image_by_name("Docker") 84 | ``` 85 | 86 | ### Images 87 | 88 | ```ruby 89 | # list all images 90 | Scaleway::Image.all 91 | 92 | # get image by id 93 | image = Scaleway::Image.find 94 | 95 | # get image by name 96 | image = Scaleway::Image.find_by_name('Ubuntu') 97 | 98 | # create an image 99 | image = Scaleway::Image.create root_volume: snapshot 100 | 101 | # edit an image 102 | image = Scaleway::Image.edit id, image 103 | 104 | # destroy an image 105 | image = Scaleway::Image.destroy id 106 | ``` 107 | 108 | ### Volumes 109 | 110 | ```ruby 111 | # list all volumes 112 | Scaleway::Volume.all 113 | 114 | # get volume by id 115 | volume = Scaleway::Volume.find 116 | 117 | # create an volume 118 | volume = Scaleway::Volume.create 119 | volume = Scaleway::Volume.create size: 100 * 10 ** 9, volume_type: 'l_ssd' 120 | 121 | # edit an volume 122 | volume = Scaleway::Volume.edit id, volume 123 | 124 | # destroy an volume 125 | volume = Scaleway::Volume.destroy id 126 | ``` 127 | 128 | ### Snapshots 129 | 130 | ```ruby 131 | # list all snapshots 132 | Scaleway::Volume.all 133 | 134 | # get snapshot by id 135 | snapshot = Scaleway::Snapshot.find 136 | 137 | # create an snapshot 138 | snapshot = Scaleway::Snapshot.create volume_id: id 139 | 140 | # edit an snapshot 141 | snapshot = Scaleway::Snapshot.edit id, snapshot 142 | 143 | # destroy an snapshot 144 | snapshot = Scaleway::Snapshot.destroy id 145 | ``` 146 | 147 | ### Ip addresses 148 | 149 | ```ruby 150 | # list all ip addresses 151 | Scaleway::Ip.all 152 | 153 | # get Ip 154 | ip = Scaleway::Ip.find 155 | ip = Scaleway::Ip.find 127.0.0.1 156 | 157 | # reserve an ip 158 | ip = Scaleway::Ip.reserve 159 | # or 160 | ip = Scaleway::Ip.create 161 | 162 | # edit an ip 163 | ip = Scaleway::Ip.edit id, ip 164 | 165 | # release an ip 166 | ip = Scaleway::Ip.destroy id 167 | ``` 168 | 169 | ### Handle exceptions 170 | 171 | ```ruby 172 | # Not found 173 | begin 174 | puts Scaleway::Server.find 175 | rescue Scaleway::NotFound => e 176 | # handle error here 177 | end 178 | 179 | # Other 180 | begin 181 | puts Scaleway::Server.create extra_field: ['nope'] 182 | rescue Scaleway::APIError => e 183 | # handle error here 184 | end 185 | ``` 186 | 187 | ## Example 188 | 189 | ```ruby 190 | require 'scaleway' 191 | 192 | Scaleway.organization = 193 | Scaleway.token = 194 | 195 | # get the docker image from the marketplace 196 | image = Scaleway::Marketplace.find_local_image_by_name('Docker') 197 | 198 | # create 5 new servers 199 | 5.times do |x| 200 | Scaleway::Server.create name: "docker#{x}", image: image.id, tags: ['docker'] 201 | end 202 | 203 | # power on 204 | Scaleway::Server.all.each do |server| 205 | Scaleway::Server.power_on(server.id) 206 | end 207 | 208 | # do something ... 209 | 210 | # terminate 211 | Scaleway::Server.all.each do |server| 212 | Scaleway::Server.terminate(server.id) 213 | end 214 | ``` 215 | -------------------------------------------------------------------------------- /lib/scaleway.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | require 'faraday_middleware' 3 | require 'recursive-open-struct' 4 | require 'scaleway/version' 5 | 6 | module Scaleway 7 | 8 | extend self 9 | 10 | def compute_endpoint 11 | "https://cp-#{Scaleway.zone}.scaleway.com" 12 | end 13 | 14 | def account_endpoint 15 | "https://account.scaleway.com" 16 | end 17 | 18 | def marketplace_endpoint 19 | "https://api-marketplace.scaleway.com" 20 | end 21 | 22 | def zone=(zone) 23 | @zone = zone 24 | @zone 25 | end 26 | 27 | def zone 28 | return @zone || 'par1' 29 | end 30 | 31 | def token=(token) 32 | @token = token 33 | setup! 34 | @token 35 | end 36 | 37 | def token 38 | return @token if @token 39 | "token_required" 40 | end 41 | 42 | def organization=(organization) 43 | @organization = organization 44 | setup! 45 | @organization 46 | end 47 | 48 | def organization 49 | return @organization if @organization 50 | "organization_required" 51 | end 52 | 53 | DEFINITIONS = { 54 | "Marketplace" => { 55 | :all => { 56 | :method => :get, 57 | :endpoint => "#{Scaleway.marketplace_endpoint}/images", 58 | }, 59 | :find_local_image_by_name => { 60 | :method => :get, 61 | :endpoint => "#{Scaleway.marketplace_endpoint}/images", 62 | :default_params => { 63 | arch: 'x86_64', 64 | }, 65 | :filters => [ 66 | Proc.new { |params, body, item| 67 | item.name.include? params.first } 68 | ], 69 | :transform => Proc.new { |params, body, item| 70 | item[0].versions[0].local_images.keep_if do |local_image| 71 | if local_image.zone == Scaleway.zone and local_image.arch == body[:arch] 72 | true 73 | end 74 | end.first 75 | }, 76 | }, 77 | :find_local_image => { 78 | :method => :get, 79 | :endpoint => "#{Scaleway.marketplace_endpoint}/images/%s/versions/current", 80 | :default_params => { 81 | arch: 'x86_64', 82 | }, 83 | :transform => Proc.new { |params, body, item| 84 | item.local_images.keep_if do |local_image| 85 | if local_image.zone == Scaleway.zone and local_image.arch == body[:arch] 86 | true 87 | end 88 | end.first 89 | }, 90 | }, 91 | }, 92 | "Image" => { 93 | :all => { 94 | :method => :get, 95 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/images" }, 96 | :default_params => { 97 | :public => true 98 | } 99 | }, 100 | :find => { 101 | :method => :get, 102 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/images/%s" }, 103 | }, 104 | :find_by_name => { 105 | :method => :get, 106 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/images" }, 107 | :filters => [ 108 | Proc.new { |params, body, item| item.name.include? params.first } 109 | ], 110 | :transform => Proc.new { |params, body, item| item.first }, 111 | }, 112 | :create => { 113 | :method => :post, 114 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/images" }, 115 | :default_params => { 116 | :name => 'default', 117 | :root_volume => 'required', 118 | :organization => Proc.new { Scaleway.organization }, 119 | } 120 | }, 121 | :edit => { 122 | :method => :put, 123 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/images/%s" }, 124 | }, 125 | :destroy => { 126 | :method => :delete, 127 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/images/%s" }, 128 | }, 129 | }, 130 | "Volume" => { 131 | :all => { 132 | :method => :get, 133 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/volumes" }, 134 | }, 135 | :find => { 136 | :method => :get, 137 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/volumes/%s" }, 138 | }, 139 | :edit => { 140 | :method => :put, 141 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/volumes/%s" }, 142 | }, 143 | :destroy => { 144 | :method => :delete, 145 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/volumes/%s" }, 146 | }, 147 | :create => { 148 | :method => :post, 149 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/volumes" }, 150 | :default_params => { 151 | :name => 'default', 152 | :size => 20 * 10**9, 153 | :volume_type => 'l_hdd', 154 | :organization => Proc.new { Scaleway.organization }, 155 | } 156 | }, 157 | }, 158 | "Ip" => { 159 | :all => { 160 | :method => :get, 161 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/ips" }, 162 | }, 163 | :find => { 164 | :method => :get, 165 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/ips/%s" }, 166 | }, 167 | :edit => { 168 | :method => :put, 169 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/ips/%s" }, 170 | }, 171 | :destroy => { 172 | :method => :delete, 173 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/ips/%s" }, 174 | }, 175 | :reserve => { 176 | :method => :post, 177 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/ips" }, 178 | :default_params => { 179 | :organization => Proc.new { Scaleway.organization }, 180 | } 181 | }, 182 | :create => { 183 | :method => :post, 184 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/ips" }, 185 | :default_params => { 186 | :organization => Proc.new { Scaleway.organization }, 187 | } 188 | }, 189 | }, 190 | "Snapshot" => { 191 | :all => { 192 | :method => :get, 193 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/snapshots" }, 194 | }, 195 | :find => { 196 | :method => :get, 197 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/snapshots/%s" }, 198 | }, 199 | :edit => { 200 | :method => :put, 201 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/snapshots/%s" }, 202 | }, 203 | :destroy => { 204 | :method => :delete, 205 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/snapshots/%s" }, 206 | }, 207 | :create => { 208 | :method => :post, 209 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/snapshots" }, 210 | :default_params => { 211 | :name => 'default', 212 | :volume_id => 'required', 213 | :organization => Proc.new { Scaleway.organization }, 214 | } 215 | }, 216 | }, 217 | "Server" => { 218 | :all => { 219 | :method => :get, 220 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers" }, 221 | }, 222 | :power_on => { 223 | :method => :post, 224 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers/%s/action" }, 225 | :default_params => { 226 | :action => :poweron 227 | } 228 | }, 229 | :reboot => { 230 | :method => :post, 231 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers/%s/action" }, 232 | :default_params => { 233 | :action => :reboot 234 | } 235 | }, 236 | :power_off => { 237 | :method => :post, 238 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers/%s/action" }, 239 | :default_params => { 240 | :action => :poweroff 241 | } 242 | }, 243 | :terminate => { 244 | :method => :post, 245 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers/%s/action" }, 246 | :default_params => { 247 | :action => :terminate 248 | } 249 | }, 250 | :find => { 251 | :method => :get, 252 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers/%s" }, 253 | }, 254 | :edit => { 255 | :method => :put, 256 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers/%s" }, 257 | }, 258 | :destroy => { 259 | :method => :delete, 260 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers/%s" }, 261 | }, 262 | :create => { 263 | :method => :post, 264 | :endpoint => Proc.new { "#{Scaleway.compute_endpoint}/servers" }, 265 | :default_params => { 266 | :name => 'default', 267 | :commercial_type => 'VC1S', 268 | :image => Proc.new { |params, body| 269 | if body[:commercial_type] == 'C1' 270 | arch = 'arm' 271 | else 272 | arch = 'x86_64' 273 | end 274 | Scaleway::Marketplace.find_local_image_by_name( 275 | 'Ubuntu Xenial', arch: arch).id 276 | }, 277 | :volumes => {}, 278 | :organization => Proc.new { Scaleway.organization }, 279 | } 280 | } 281 | }, 282 | } 283 | 284 | DEFINITIONS.each do |resource| 285 | resource_name = resource[0] 286 | 287 | resource_class = Class.new(Object) do 288 | singleton = class << self; self end 289 | 290 | DEFINITIONS[resource_name].each do |method_name, query| 291 | singleton.send :define_method, method_name do |*args| 292 | Scaleway.request_and_respond(query, *args) 293 | end 294 | end 295 | end 296 | 297 | Scaleway.const_set(resource_name, resource_class) 298 | end 299 | 300 | def request=(request) 301 | @request = request 302 | end 303 | 304 | def request 305 | @request 306 | end 307 | 308 | def request_and_respond(query, *params) 309 | body = {} 310 | body.merge! query[:default_params] || {} 311 | endpoint = query[:endpoint] 312 | 313 | if endpoint.respond_to? :call 314 | endpoint = endpoint.call() 315 | end 316 | 317 | params.each do |param| 318 | if param.is_a? Hash or param.is_a? RecursiveOpenStruct 319 | body.merge! param 320 | elsif not param.nil? 321 | endpoint = endpoint % param 322 | end 323 | end 324 | 325 | body = Hash[body.map { |k, v| 326 | if v.respond_to? :call then [k, v.call(params, body)] else [k, v] end 327 | }] 328 | 329 | if Scaleway.request.nil? 330 | raise ::RuntimeError.new("Authentication is missing") 331 | end 332 | 333 | resp = Scaleway.request.send(query[:method], endpoint, body) 334 | response_body = resp.body 335 | if resp.status == 204 336 | return 337 | end 338 | 339 | if resp.status == 404 340 | raise Scaleway::NotFound, resp 341 | elsif resp.status >= 300 342 | raise Scaleway::APIError, resp 343 | end 344 | 345 | hash = RecursiveOpenStruct.new(response_body, :recurse_over_arrays => true) 346 | 347 | if response_body.length == 1 348 | hash = hash.send(response_body.keys.first) 349 | end 350 | 351 | if query[:filters] 352 | filters = query[:filters] 353 | hash = hash.find_all do |item| 354 | filters.all? do |filter| 355 | filter.call(params, body, item) 356 | end 357 | end 358 | end 359 | 360 | if query[:transform] 361 | hash = query[:transform].call(params, body, hash) 362 | end 363 | 364 | hash 365 | end 366 | 367 | class APIError < Exception 368 | def initialize(response) 369 | self.status = response.status 370 | self.body = RecursiveOpenStruct.new(response.body, :recurse_over_arrays => true) 371 | self.type = self.body.type 372 | self.error_message = self.body.message 373 | end 374 | 375 | def to_s 376 | "" 377 | end 378 | 379 | def message 380 | "#{self}" 381 | end 382 | 383 | attr_accessor :status 384 | attr_accessor :type 385 | attr_accessor :error_message 386 | attr_accessor :body 387 | end 388 | 389 | class NotFound < Scaleway::APIError 390 | end 391 | 392 | 393 | 394 | private 395 | 396 | def setup! 397 | options = { 398 | :headers => { 399 | 'x-auth-token' => Scaleway.token, 400 | 'content-type' => 'application/json', 401 | } 402 | } 403 | 404 | Scaleway.request = ::Faraday::Connection.new(options) do |builder| 405 | builder.use ::FaradayMiddleware::EncodeJson 406 | builder.use ::FaradayMiddleware::ParseJson 407 | builder.use ::FaradayMiddleware::FollowRedirects 408 | builder.adapter ::Faraday.default_adapter 409 | end 410 | end 411 | end 412 | --------------------------------------------------------------------------------