hyperb 0.2.1 → 0.2.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.
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