hyperb 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +12 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +41 -0
  6. data/Dockerfile +7 -0
  7. data/Gemfile +16 -0
  8. data/LICENSE.txt +21 -0
  9. data/Makefile +16 -0
  10. data/README.md +188 -0
  11. data/Rakefile +24 -0
  12. data/circle.yml +13 -0
  13. data/examples/README.md +367 -0
  14. data/examples/auth-gcr-registry.md +19 -0
  15. data/examples/compose.md +75 -0
  16. data/examples/handling-errors.md +54 -0
  17. data/examples/streaming-logs.md +28 -0
  18. data/examples/streaming-stats.md +25 -0
  19. data/hyperb.gemspec +30 -0
  20. data/lib/hyperb.rb +4 -0
  21. data/lib/hyperb/api.rb +22 -0
  22. data/lib/hyperb/auth_object.rb +42 -0
  23. data/lib/hyperb/client.rb +32 -0
  24. data/lib/hyperb/compose/compose.rb +116 -0
  25. data/lib/hyperb/containers/container.rb +27 -0
  26. data/lib/hyperb/containers/containers.rb +251 -0
  27. data/lib/hyperb/error.rb +44 -0
  28. data/lib/hyperb/hyper_version.rb +12 -0
  29. data/lib/hyperb/images/image.rb +13 -0
  30. data/lib/hyperb/images/images.rb +108 -0
  31. data/lib/hyperb/network/fips.rb +102 -0
  32. data/lib/hyperb/request.rb +129 -0
  33. data/lib/hyperb/services/services.rb +59 -0
  34. data/lib/hyperb/snapshots/snapshot.rb +12 -0
  35. data/lib/hyperb/snapshots/snapshots.rb +39 -0
  36. data/lib/hyperb/utils.rb +39 -0
  37. data/lib/hyperb/version.rb +3 -0
  38. data/lib/hyperb/volumes/volume.rb +16 -0
  39. data/lib/hyperb/volumes/volumes.rb +67 -0
  40. data/spec/auth_object_spec.rb +45 -0
  41. data/spec/client_spec.rb +27 -0
  42. data/spec/compose_spec.rb +145 -0
  43. data/spec/container_spec.rb +25 -0
  44. data/spec/containers_spec.rb +442 -0
  45. data/spec/create_snapshot.rb +30 -0
  46. data/spec/error_spec.rb +18 -0
  47. data/spec/fixtures/auth_obj.json +12 -0
  48. data/spec/fixtures/compose_rm.json +1 -0
  49. data/spec/fixtures/compose_up.json +1 -0
  50. data/spec/fixtures/container_stats.json +78 -0
  51. data/spec/fixtures/containers.json +160 -0
  52. data/spec/fixtures/create_container.json +4 -0
  53. data/spec/fixtures/create_image.json +1 -0
  54. data/spec/fixtures/create_service.json +35 -0
  55. data/spec/fixtures/create_snapshot.json +8 -0
  56. data/spec/fixtures/fip_allocate.json +7 -0
  57. data/spec/fixtures/fips_ls.json +14 -0
  58. data/spec/fixtures/images.json +32 -0
  59. data/spec/fixtures/inspect_container.json +159 -0
  60. data/spec/fixtures/inspect_image.json +89 -0
  61. data/spec/fixtures/inspect_volume.json +11 -0
  62. data/spec/fixtures/remove_container.json +1 -0
  63. data/spec/fixtures/remove_image.json +5 -0
  64. data/spec/fixtures/volumes.json +13 -0
  65. data/spec/helper.rb +36 -0
  66. data/spec/image_spec.rb +17 -0
  67. data/spec/images_spec.rb +133 -0
  68. data/spec/network_spec.rb +106 -0
  69. data/spec/request_spec.rb +41 -0
  70. data/spec/services_spec.rb +193 -0
  71. data/spec/version_spec.rb +7 -0
  72. data/spec/volumes_spec.rb +88 -0
  73. metadata +74 -3
@@ -0,0 +1,102 @@
1
+ require 'hyperb/request'
2
+ require 'hyperb/utils'
3
+ require 'hyperb/auth_object'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'base64'
7
+
8
+ module Hyperb
9
+ # network api wrapper
10
+ module Network
11
+ include Hyperb::Utils
12
+
13
+ # detach a floating ip from a container
14
+ #
15
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Network/fip_detach.html
16
+ #
17
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
18
+ # @raise [Hyperb::Error::NotFound] raised when container is found.
19
+ #
20
+ # @param params [Hash] A customizable set of params.
21
+ # @option params [String] :container container name/id
22
+ def fip_detach(params = {})
23
+ raise ArgumentError, 'Invalid Arguments' unless check_arguments(params, 'container')
24
+ path = '/fips/detach'
25
+ query = {}
26
+ query[:container] = params[:container] if params.key?(:container)
27
+ Hyperb::Request.new(self, path, query, 'post').perform
28
+ end
29
+
30
+ # attach a floating ip to a container
31
+ #
32
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Network/fip_attach.html
33
+ #
34
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
35
+ # @raise [Hyperb::Error::NotFound] raised when ip are not found.
36
+ #
37
+ # @param params [Hash] A customizable set of params.
38
+ # @option params [String] :ip
39
+ # @option params [String] :container
40
+ def fip_attach(params = {})
41
+ raise ArgumentError, 'Invalid Arguments' unless check_arguments(params, 'container', 'ip')
42
+ path = '/fips/attach'
43
+ query = {}
44
+ query[:ip] = params[:ip] if params.key?(:ip)
45
+ query[:container] = params[:container] if params.key?(:container)
46
+ Hyperb::Request.new(self, path, query, 'post').perform
47
+ end
48
+
49
+ # list floating ips
50
+ #
51
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Network/fip_ls.html
52
+ #
53
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
54
+ # @raise [Hyperb::Error::NotFound] raised when ips are not found.
55
+ #
56
+ # @returns [Array] array of downcased symbolized has
57
+ #
58
+ # @param params [Hash] A customizable set of params.
59
+ # @option params [String] :filters
60
+ def fips_ls(params = {})
61
+ path = '/fips'
62
+ query = {}
63
+ query[:filters] = params[:filters] if params.key?(:filters)
64
+ downcase_symbolize(JSON.parse(Hyperb::Request.new(self, path, query, 'get').perform))
65
+ end
66
+
67
+ # release a floating ip
68
+ #
69
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Network/fip_release.html
70
+ #
71
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
72
+ # @raise [Hyperb::Error::NotFound] raised when ips are not found.
73
+ #
74
+ # @param params [Hash] A customizable set of params.
75
+ # @option params [String] :ip the number of free fips to allocate
76
+ def fip_release(params = {})
77
+ path = '/fips/release'
78
+ query = {}
79
+ query[:ip] = params[:ip] if params.key?(:ip)
80
+ Hyperb::Request.new(self, path, query, 'post').perform
81
+ end
82
+
83
+ # allocate a new floating ip
84
+ #
85
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Network/fip_allocate.html
86
+ #
87
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
88
+ #
89
+ # @return [Array] Array of ips (string).
90
+ #
91
+ # @param params [Hash] A customizable set of params.
92
+ # @option params [String] :count the number of free fips to allocate
93
+ def fip_allocate(params = {})
94
+ raise ArgumentError, 'Invalid Arguments' unless check_arguments(params, 'count')
95
+ path = '/fips/allocate'
96
+ query = {}
97
+ query[:count] = params[:count] if params.key?(:count)
98
+ fips = JSON.parse(Hyperb::Request.new(self, path, query, 'post').perform)
99
+ fips
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,129 @@
1
+ require 'hyperb/error'
2
+ require 'openssl'
3
+ require 'time'
4
+ require 'uri'
5
+ require 'http'
6
+ require 'digest'
7
+ require 'json'
8
+
9
+ module Hyperb
10
+ # wraps all requests, performing aws4 signature
11
+ class Request
12
+ FMT = '%Y%m%dT%H%M%SZ'.freeze
13
+ VERSION = 'v1.23'.freeze
14
+ HOST = 'us-west-1.hyper.sh'.freeze
15
+ REGION = 'us-west-1'.freeze
16
+ SERVICE = 'hyper'.freeze
17
+ ALGORITHM = 'HYPER-HMAC-SHA256'.freeze
18
+ KEYPARTS_REQUEST = 'hyper_request'.freeze
19
+ BASE_URL = ('https://' + HOST + '/').freeze
20
+
21
+ attr_accessor :verb, :path, :client, :date, :headers, :signed
22
+
23
+ def initialize(client, path, query = {}, verb = 'GET', body = '', optional_headers = {})
24
+ @client = client
25
+ @path = VERSION + path
26
+ @query = URI.encode_www_form(query)
27
+ @body = body.empty? ? body : body.to_json
28
+ @hashed_body = hexdigest(@body)
29
+ @verb = verb.upcase
30
+ @date = Time.now.utc.strftime(FMT)
31
+ @headers = {
32
+ content_type: 'application/json',
33
+ x_hyper_date: @date,
34
+ host: HOST,
35
+ x_hyper_content_sha256: @hashed_body
36
+ }
37
+ @headers.merge!(optional_headers) unless optional_headers.empty?
38
+ @signed = false
39
+ end
40
+
41
+ def perform
42
+ sign unless signed
43
+ final = BASE_URL + @path + '?' + @query
44
+ options = {}
45
+ options[:body] = @body unless @body.empty?
46
+ response = HTTP.headers(@headers).public_send(@verb.downcase.to_sym, final, options)
47
+ fail_or_return(response.code, response.body)
48
+ end
49
+
50
+ def fail_or_return(code, body)
51
+ error = Hyperb::Error::ERRORS[code]
52
+ raise(error.new(body, code)) if error
53
+ body
54
+ end
55
+
56
+ # join all headers by `;`
57
+ # ie:
58
+ # content-type;x-hyper-hmac-sha256
59
+ def signed_headers
60
+ @headers.keys.sort.map { |header| header.to_s.tr('_', '-') }.join(';')
61
+ end
62
+
63
+ # sorts all headers, join them by `:`, and re-join by \n
64
+ # ie:
65
+ # content-type:application\nhost:us-west-1.hyper.sh
66
+ def canonical_headers
67
+ canonical = @headers.sort.map do |header, value|
68
+ "#{header.to_s.tr('_', '-')}:#{value}"
69
+ end
70
+ canonical.join("\n") + "\n"
71
+ end
72
+
73
+ # creates Authoriatization header
74
+ def sign
75
+ credential = "#{@client.access_key}/#{credential_scope}"
76
+ auth = "#{ALGORITHM} Credential=#{credential}, "
77
+ auth += "SignedHeaders=#{signed_headers}, "
78
+ auth += "Signature=#{signature}"
79
+ @headers[:authorization] = auth
80
+ @signed = true
81
+ end
82
+
83
+ # setup signature key
84
+ # https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/index.html
85
+ def signature
86
+ k_date = hmac('HYPER' + @client.secret_key, @date[0, 8])
87
+ k_region = hmac(k_date, REGION)
88
+ k_service = hmac(k_region, SERVICE)
89
+ k_credentials = hmac(k_service, KEYPARTS_REQUEST)
90
+ hexhmac(k_credentials, string_to_sign)
91
+ end
92
+
93
+ def string_to_sign
94
+ [ALGORITHM, @date, credential_scope, hexdigest(canonical_request)].join("\n")
95
+ end
96
+
97
+ def canonical_request
98
+ [
99
+ @verb,
100
+ @path,
101
+ @query,
102
+ canonical_headers,
103
+ signed_headers,
104
+ @hashed_body
105
+ ].join("\n")
106
+ end
107
+
108
+ def credential_scope
109
+ [
110
+ @date[0, 8],
111
+ REGION,
112
+ SERVICE,
113
+ KEYPARTS_REQUEST
114
+ ].join("/") # rubocop:disable StringLiterals
115
+ end
116
+
117
+ def hexdigest(value)
118
+ Digest::SHA256.new.update(value).hexdigest
119
+ end
120
+
121
+ def hmac(key, value)
122
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, value)
123
+ end
124
+
125
+ def hexhmac(key, value)
126
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, value)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,59 @@
1
+ require 'hyperb/request'
2
+ require 'hyperb/utils'
3
+ require 'json'
4
+ require 'uri'
5
+ require 'base64'
6
+
7
+ module Hyperb
8
+ # services module
9
+ module Services
10
+ include Hyperb::Utils
11
+
12
+ # create a service
13
+ #
14
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Service/create.html
15
+ #
16
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
17
+ #
18
+ # @return [Hash] Hash containing service information.
19
+ #
20
+ # @param params [Hash] A customizable set of params.
21
+ # @param params image [String] service name
22
+ # @param params name [String] image name
23
+ # @param params replicas [Fixnum] numer of replicas
24
+ # @param params service_port [Fixnum] service port
25
+ # @param params container_port [Fixnum] container port
26
+ # @param params labels [Hash] hash containing labels
27
+ # @param params entrypoint [String] entrypoint
28
+ # @param params cmd [String] command
29
+ # @param params env [Array] array of envs ["env=value", ["env2=value"]]
30
+ # @param params algorithm [String] algorithm of the service, 'roundrobin', 'leastconn'
31
+ # @param params protocol [String] prot
32
+ # @param params workingdir [String] working directory
33
+ def create_service(params = {})
34
+ valid = check_arguments(params, 'name', 'image', 'replicas', 'service_port', 'labels')
35
+ raise ArgumentError, 'Invalid arguments.' unless valid
36
+ path = '/services/create'
37
+ body = {}
38
+ body.merge!(params)
39
+ downcase_symbolize(JSON.parse(Hyperb::Request.new(self, path, {}, 'post', body).perform))
40
+ end
41
+
42
+ # remove service
43
+ #
44
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Service/remove.html
45
+ #
46
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
47
+ # @raise [Hyperb::Error::NotFound] raised service is not found.
48
+ #
49
+ # @param params [Hash] A customizable set of params.
50
+ # @option params [String] :keep if true, keep containers running while removing the service.
51
+ def remove_service(params = {})
52
+ raise ArgumentError, 'Invalid arguments.' unless check_arguments(params, 'name')
53
+ path = '/services/' + params[:name]
54
+ query = {}
55
+ query[:keep] = params[:keep] if params.key?(:keep)
56
+ Hyperb::Request.new(self, path, query, 'delete').perform
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,12 @@
1
+ module Hyperb
2
+ # snapshot object
3
+ class Snapshot
4
+ attr_accessor :id, :name, :size, :volume
5
+
6
+ def initialize(attrs = {})
7
+ attrs.each do |k, v|
8
+ instance_variable_set("@#{k.downcase.to_sym}", v)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ require 'hyperb/snapshots/snapshot'
2
+ require 'hyperb/request'
3
+ require 'hyperb/utils'
4
+ require 'hyperb/auth_object'
5
+ require 'json'
6
+ require 'uri'
7
+ require 'base64'
8
+
9
+ module Hyperb
10
+ # snapshots api wrapper
11
+ module Snapshots
12
+ include Hyperb::Utils
13
+
14
+ # create a snapshot
15
+ #
16
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Snapshot/create.html
17
+ #
18
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
19
+ # @raise [Hyperb::Error::InternalServerError] server error on hyper side.
20
+ # @raise [Hyperb::Error::NotFound] raised when volume is not found.
21
+ # @raise [ArgumentError] when required arguments are not provided.
22
+ #
23
+ # @return [Hash] symbolized json response.
24
+ #
25
+ # @param params [Hash] A customizable set of params.
26
+ #
27
+ # @option params [String] :volume volume id
28
+ # @option params [String] :name snapshot's name
29
+ def create_snapshot(params = {})
30
+ raise ArgumentError, 'Invalid arguments.' unless check_arguments(params, 'name', 'volume')
31
+ path = '/snapshots/create'
32
+ query = {}
33
+ query[:name] = params[:name] if params.key?(:name)
34
+ query[:volume] = params[:volume] if params.key?(:volume)
35
+ res = JSON.parse(Hyperb::Request.new(self, path, query, 'post').perform)
36
+ downcase_symbolize(res)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ module Hyperb
2
+ # utils functions
3
+ module Utils
4
+ # checks if all args are keys into the hash
5
+ #
6
+ # @return [Boolean]
7
+ #
8
+ # @param params [Hash] hash to check.
9
+ # @option *args [String] array of strings to check against the hash
10
+ def check_arguments(params, *args)
11
+ contains = true
12
+ args.each do |arg|
13
+ contains = false unless params.key? arg.to_sym
14
+ end
15
+ contains
16
+ end
17
+
18
+ # hyper.sh responses are capital cased json
19
+ #
20
+ # {"Field": "value"}
21
+ #
22
+ # this fn is used to symbolize and downcase all hyper.sh api reponses
23
+ def downcase_symbolize(obj)
24
+ if obj.is_a? Hash
25
+ return obj.each_with_object({}) do |(k, v), memo|
26
+ memo.tap { |m| m[k.downcase.to_sym] = downcase_symbolize(v) }
27
+ end
28
+ end
29
+
30
+ if obj.is_a? Array
31
+ return obj.each_with_object([]) do |v, memo|
32
+ memo << downcase_symbolize(v)
33
+ memo
34
+ end
35
+ end
36
+ obj
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module Hyperb
2
+ VERSION = '0.2.2'.freeze
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'hyperb/utils'
2
+
3
+ module Hyperb
4
+ # volume object
5
+ class Volume
6
+ include Hyperb::Utils
7
+ attr_accessor :name, :driver, :mountpoint, :labels
8
+
9
+ def initialize(attrs = {})
10
+ attrs.each do |att, value|
11
+ value = downcase_symbolize(value) if value.is_a?(Hash)
12
+ instance_variable_set("@#{att.downcase.to_sym}", value)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,67 @@
1
+ require 'hyperb/request'
2
+ require 'hyperb/utils'
3
+ require 'hyperb/volumes/volume'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'base64'
7
+
8
+ module Hyperb
9
+ # volumes module
10
+ module Volumes
11
+ include Hyperb::Utils
12
+
13
+ # list volumes
14
+ #
15
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Volume/list.html
16
+ #
17
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
18
+ #
19
+ # @return [Hyperb::Volume] Array of Hyperb::Volume.
20
+ #
21
+ # @param params [Hash] A customizable set of params.
22
+ # TODO: @option params [String] :filters JSON encoded value of the filters
23
+ # TODO: @option params filters [Boolean] :dangling
24
+ def volumes(params = {})
25
+ path = '/volumes'
26
+ query = {}
27
+ query[:filters] = params[:filters] if params.key?(:filters)
28
+ response = JSON.parse(Hyperb::Request.new(self, path, query, 'get').perform)
29
+
30
+ # hyper response atm comes inside a Volumes: [], unlike the images API
31
+ # which is returned in a []
32
+ response['Volumes'].map { |vol| Hyperb::Volume.new(vol) }
33
+ end
34
+
35
+ # remove volume
36
+ #
37
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Volume/remove.html
38
+ #
39
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
40
+ #
41
+ # @param params [Hash] A customizable set of params.
42
+ # @option params [String] :id volume id or name
43
+ # @option params [String] :all default is true
44
+ # @option params [String] :filter only return image with the specified name
45
+ def remove_volume(params = {})
46
+ raise ArgumentError, 'Invalid arguments.' unless check_arguments(params, 'id')
47
+ path = '/volumes/' + params[:id]
48
+ Hyperb::Request.new(self, path, {}, 'delete').perform
49
+ end
50
+
51
+ # inspect a volume
52
+ #
53
+ # @see https://docs.hyper.sh/Reference/API/2016-04-04%20[Ver.%201.23]/Volume/inspect.html
54
+ #
55
+ # @raise [Hyperb::Error::Unauthorized] raised when credentials are not valid.
56
+ #
57
+ # @return [Hash] of downcase symbolized json response.
58
+ #
59
+ # @param params [Hash] A customizable set of params.
60
+ # @option params [String] :id volume id or name
61
+ def inspect_volume(params = {})
62
+ raise ArgumentError, 'Invalid arguments.' unless check_arguments(params, 'id')
63
+ path = '/volumes/' + params[:id]
64
+ downcase_symbolize(JSON.parse(Hyperb::Request.new(self, path, {}, 'get').perform))
65
+ end
66
+ end
67
+ end