aliyun-odps 0.1.0 → 0.4.0
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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +31 -0
- data/Gemfile +3 -0
- data/README.md +55 -12
- data/Rakefile +15 -5
- data/aliyun-odps.gemspec +22 -11
- data/bin/console +10 -3
- data/lib/aliyun/odps.rb +69 -2
- data/lib/aliyun/odps/authorization.rb +90 -0
- data/lib/aliyun/odps/client.rb +40 -0
- data/lib/aliyun/odps/configuration.rb +16 -0
- data/lib/aliyun/odps/error.rb +97 -0
- data/lib/aliyun/odps/http.rb +138 -0
- data/lib/aliyun/odps/list.rb +40 -0
- data/lib/aliyun/odps/model/function.rb +16 -0
- data/lib/aliyun/odps/model/functions.rb +113 -0
- data/lib/aliyun/odps/model/instance.rb +130 -0
- data/lib/aliyun/odps/model/instance_task.rb +30 -0
- data/lib/aliyun/odps/model/instances.rb +119 -0
- data/lib/aliyun/odps/model/projects.rb +73 -0
- data/lib/aliyun/odps/model/resource.rb +26 -0
- data/lib/aliyun/odps/model/resources.rb +144 -0
- data/lib/aliyun/odps/model/table.rb +37 -0
- data/lib/aliyun/odps/model/table_column.rb +13 -0
- data/lib/aliyun/odps/model/table_partition.rb +9 -0
- data/lib/aliyun/odps/model/table_partitions.rb +90 -0
- data/lib/aliyun/odps/model/table_schema.rb +13 -0
- data/lib/aliyun/odps/model/tables.rb +125 -0
- data/lib/aliyun/odps/model/task_result.rb +9 -0
- data/lib/aliyun/odps/modelable.rb +16 -0
- data/lib/aliyun/odps/project.rb +47 -0
- data/lib/aliyun/odps/service_object.rb +27 -0
- data/lib/aliyun/odps/struct.rb +126 -0
- data/lib/aliyun/odps/tunnel/download_session.rb +98 -0
- data/lib/aliyun/odps/tunnel/router.rb +15 -0
- data/lib/aliyun/odps/tunnel/snappy_reader.rb +19 -0
- data/lib/aliyun/odps/tunnel/snappy_writer.rb +45 -0
- data/lib/aliyun/odps/tunnel/table_tunnels.rb +81 -0
- data/lib/aliyun/odps/tunnel/upload_block.rb +9 -0
- data/lib/aliyun/odps/tunnel/upload_session.rb +132 -0
- data/lib/aliyun/odps/utils.rb +102 -0
- data/lib/aliyun/odps/version.rb +1 -1
- data/requirements.png +0 -0
- data/wiki/error.md +188 -0
- data/wiki/functions.md +39 -0
- data/wiki/get_start.md +34 -0
- data/wiki/installation.md +15 -0
- data/wiki/instances.md +32 -0
- data/wiki/projects.md +51 -0
- data/wiki/resources.md +62 -0
- data/wiki/ssl.md +7 -0
- data/wiki/tables.md +75 -0
- data/wiki/tunnels.md +80 -0
- metadata +195 -13
- data/requirements.mindnode/QuickLook/Preview.jpg +0 -0
- data/requirements.mindnode/contents.xml +0 -10711
- data/requirements.mindnode/viewState.plist +0 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module Odps
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :access_key, :secret_key, :endpoint, :tunnel_endpoint, :project, :options, :ssl_ca_file
|
7
|
+
def initialize
|
8
|
+
@options = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def protocol
|
12
|
+
Addressable::URI.parse(@endpoint).scheme
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# [Aliyun::Odps::RequestError] when Odps give a Non 2xx response
|
6
|
+
class RequestError < Error
|
7
|
+
# Error Code defined by Odps
|
8
|
+
attr_reader :code
|
9
|
+
|
10
|
+
# Error Message defined by Odps
|
11
|
+
attr_reader :message
|
12
|
+
|
13
|
+
# It's the UUID to uniquely identifies this request;
|
14
|
+
# When you can't solve the problem, you can request help from the ODPS development engineer with the RequestId.
|
15
|
+
attr_reader :request_id
|
16
|
+
|
17
|
+
# The Origin Httparty Response
|
18
|
+
attr_reader :origin_response
|
19
|
+
|
20
|
+
def initialize(response)
|
21
|
+
assign_error_code(response)
|
22
|
+
assign_request_id(response)
|
23
|
+
@origin_response = response
|
24
|
+
super("#{@request_id} - #{@code}: #{@message}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def assign_error_code(response)
|
28
|
+
result = response.parsed_response
|
29
|
+
if result.key?('Error')
|
30
|
+
@code = result['Error']['Code']
|
31
|
+
@message = result['Error']['Message']
|
32
|
+
elsif result.key?('Code')
|
33
|
+
@code = result['Code']
|
34
|
+
@message = result['Message']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def assign_request_id(response)
|
39
|
+
@request_id = response.headers['x-odps-request-id']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class XmlElementMissingError < Error
|
44
|
+
def initialize(element)
|
45
|
+
super("Missing #{element} Element in xml")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class MissingProjectConfigurationError < Error
|
50
|
+
def initialize
|
51
|
+
super("Must config project first. Use Aliyun::Odps.configure {|config| config.project = 'your-project' }")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class PriorityInvalidError < Error
|
56
|
+
def initialize
|
57
|
+
super('Priority must more than or equal to zero.')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class InstanceTaskNotSuccessError < Error
|
62
|
+
def initialize(name, status, task_result)
|
63
|
+
super("Task #{name} #{status}: #{task_result}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class InstanceNameInvalidError < Error
|
68
|
+
def initialize(name)
|
69
|
+
super("#{name} should match pattern: #{Instance::NAME_PATTERN}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class TunnelEndpointMissingError < Error
|
74
|
+
def initialize
|
75
|
+
super("Tunnel Endpoint auto detect fail, Use Aliyun::Odps.configure {|config| config.tunnel_endpoint = 'your-project' } to config")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ValueNotSupportedError < Error
|
80
|
+
def initialize(attr, supported_value)
|
81
|
+
super("#{attr} only support: #{Utils.wrap(supported_value).join(', ')} !!")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class ResourceMissingContentError < Error
|
86
|
+
def initialize
|
87
|
+
super('A Resource must exist file or table')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class RecordNotMatchSchemaError < Error
|
92
|
+
def initialize(values, schema)
|
93
|
+
super("#{values} not match with #{schema}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'addressable/uri'
|
3
|
+
require 'aliyun/odps/error'
|
4
|
+
|
5
|
+
module Aliyun
|
6
|
+
module Odps
|
7
|
+
class Http # nodoc
|
8
|
+
include HTTParty
|
9
|
+
|
10
|
+
class BetterXmlParser < HTTParty::Parser
|
11
|
+
protected
|
12
|
+
|
13
|
+
def xml
|
14
|
+
MultiXml.parse(body)
|
15
|
+
rescue
|
16
|
+
body
|
17
|
+
end
|
18
|
+
end
|
19
|
+
parser BetterXmlParser
|
20
|
+
|
21
|
+
attr_reader :config
|
22
|
+
|
23
|
+
def initialize(config)
|
24
|
+
@config = config
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(uri, options = {})
|
28
|
+
request('GET', uri, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def put(uri, options = {})
|
32
|
+
headers = default_content_type.merge(options[:headers] || {})
|
33
|
+
request('PUT', uri, options.merge(headers: headers))
|
34
|
+
end
|
35
|
+
|
36
|
+
def post(uri, options = {})
|
37
|
+
headers = default_content_type.merge(options[:headers] || {})
|
38
|
+
request('POST', uri, options.merge(headers: headers))
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(uri, options = {})
|
42
|
+
headers = default_content_type.merge(options[:headers] || {})
|
43
|
+
request('DELETE', uri, options.merge(headers: headers))
|
44
|
+
end
|
45
|
+
|
46
|
+
def options(uri, options = {})
|
47
|
+
request('OPTIONS', uri, options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def head(uri, options = {})
|
51
|
+
request('HEAD', uri, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def request(verb, resource, options = {})
|
57
|
+
query = options.fetch(:query, {})
|
58
|
+
headers = options.fetch(:headers, {})
|
59
|
+
body = options.delete(:body)
|
60
|
+
|
61
|
+
append_headers!(headers, verb, body, options.merge(path: resource))
|
62
|
+
path = config.endpoint + resource
|
63
|
+
options = { headers: headers, query: query, body: body }
|
64
|
+
append_options!(options, path)
|
65
|
+
|
66
|
+
wrap(self.class.__send__(verb.downcase, path, options))
|
67
|
+
end
|
68
|
+
|
69
|
+
def wrap(response)
|
70
|
+
case response.code
|
71
|
+
when 200..299
|
72
|
+
response
|
73
|
+
else
|
74
|
+
fail RequestError, response
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def append_headers!(headers, verb, body, options)
|
79
|
+
append_default_headers!(headers)
|
80
|
+
append_body_headers!(headers, body)
|
81
|
+
append_authorization_headers!(headers, verb, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def append_options!(options, url)
|
85
|
+
options.merge!(uri_adapter: Addressable::URI)
|
86
|
+
if config.ssl_ca_file
|
87
|
+
options.merge!(ssl_ca_file: config.ssl_ca_file)
|
88
|
+
elsif url.start_with?('https://')
|
89
|
+
options.merge!(verify_peer: true)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def append_default_headers!(headers)
|
94
|
+
headers.merge!(default_headers)
|
95
|
+
end
|
96
|
+
|
97
|
+
def append_body_headers!(headers, body)
|
98
|
+
return headers unless body
|
99
|
+
|
100
|
+
unless headers.key?('Content-MD5')
|
101
|
+
headers.merge!('Content-MD5' => Utils.md5_hexdigest(body))
|
102
|
+
end
|
103
|
+
|
104
|
+
return if headers.key?('Content-Length')
|
105
|
+
headers.merge!('Content-Length' => Utils.content_size(body).to_s)
|
106
|
+
end
|
107
|
+
|
108
|
+
def append_authorization_headers!(headers, verb, options)
|
109
|
+
auth_key = get_auth_key(
|
110
|
+
options.merge(verb: verb, headers: headers, date: headers['Date'])
|
111
|
+
)
|
112
|
+
headers.merge!('Authorization' => auth_key)
|
113
|
+
end
|
114
|
+
|
115
|
+
def get_auth_key(options)
|
116
|
+
Authorization.get_authorization(config.access_key, config.secret_key, options)
|
117
|
+
end
|
118
|
+
|
119
|
+
def default_headers
|
120
|
+
{
|
121
|
+
'User-Agent' => user_agent,
|
122
|
+
'Date' => Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
def default_content_type
|
127
|
+
{
|
128
|
+
'Content-Type' => 'application/xml'
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def user_agent
|
133
|
+
"aliyun-odps-sdk-ruby/#{Aliyun::Odps::VERSION} " \
|
134
|
+
"(#{RbConfig::CONFIG['host_os']} ruby-#{RbConfig::CONFIG['ruby_version']})"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module Odps
|
5
|
+
# Wrap for simple array and give marker and max_items methods
|
6
|
+
class List
|
7
|
+
include Enumerable
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
attr_reader :marker, :max_items
|
11
|
+
def_delegators :@objects, :[], :each, :size, :inspect
|
12
|
+
|
13
|
+
def initialize(marker, max_items, objects)
|
14
|
+
@marker = marker
|
15
|
+
@max_items = max_items.to_i
|
16
|
+
@objects = objects
|
17
|
+
end
|
18
|
+
|
19
|
+
# Auto detect marker, max_items, values from result,
|
20
|
+
# build a object, where you can access marker, max_items, and values
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
#
|
24
|
+
# Aliyun::Odps::List.build(result, %w(Projects Project)) do |hash|
|
25
|
+
# Project.new(hash.merge(client: client))
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# @return [List]
|
29
|
+
def self.build(result, keys, &_block)
|
30
|
+
top_key = keys.first
|
31
|
+
marker = Utils.dig_value(result, top_key, 'Marker')
|
32
|
+
max_items = Utils.dig_value(result, top_key, 'MaxItems')
|
33
|
+
objects = Utils.wrap(Utils.dig_value(result, *keys)).map do |hash|
|
34
|
+
yield hash
|
35
|
+
end
|
36
|
+
new(marker, max_items, objects)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
class Function < Struct::Base
|
4
|
+
extend Aliyun::Odps::Modelable
|
5
|
+
|
6
|
+
property :name, String, required: true
|
7
|
+
property :owner, String
|
8
|
+
property :class_type, String
|
9
|
+
property :creation_time, DateTime
|
10
|
+
property :resources, Array
|
11
|
+
property :location, String
|
12
|
+
|
13
|
+
alias_method :alias=, :name=
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
# Methods for Functions
|
4
|
+
class Functions < ServiceObject
|
5
|
+
# List Functions of project
|
6
|
+
#
|
7
|
+
# @see http://repo.aliyun.com/api-doc/Function/get_functions/index.html Get functions
|
8
|
+
#
|
9
|
+
# @param options [Hash] options
|
10
|
+
# @option options [String] :name specify function name
|
11
|
+
# @option options [String] :owner specify function owner
|
12
|
+
# @option options [String] :marker
|
13
|
+
# @option options [String] :maxitems (1000)
|
14
|
+
#
|
15
|
+
# @return [List]
|
16
|
+
def list(options = {})
|
17
|
+
Utils.stringify_keys!(options)
|
18
|
+
path = "/projects/#{project.name}/registration/functions"
|
19
|
+
query = Utils.hash_slice(options, 'name', 'owner', 'marker', 'maxitems')
|
20
|
+
result = client.get(path, query: query).parsed_response
|
21
|
+
|
22
|
+
Aliyun::Odps::List.build(result, %w(Functions Function)) do |hash|
|
23
|
+
Function.new(hash)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get Function
|
28
|
+
#
|
29
|
+
# @param name specify function name
|
30
|
+
#
|
31
|
+
# @return [Function]
|
32
|
+
def get(name)
|
33
|
+
path = "/projects/#{project.name}/registration/functions/#{name}"
|
34
|
+
|
35
|
+
result = client.get(path).parsed_response
|
36
|
+
Function.new(Utils.dig_value(result, 'Function'))
|
37
|
+
end
|
38
|
+
alias_method :function, :get
|
39
|
+
|
40
|
+
# Register function in project
|
41
|
+
#
|
42
|
+
# @see http://repo.aliyun.com/api-doc/Function/post_function/index.html Post function
|
43
|
+
#
|
44
|
+
# @param name [String] specify function name
|
45
|
+
# @param class_path [String] specify class Path used by function
|
46
|
+
# @param resources [Array<Model::Resource>] specify resources used by function
|
47
|
+
#
|
48
|
+
# @return [Function]
|
49
|
+
def create(name, class_path, resources = [])
|
50
|
+
path = "/projects/#{project.name}/registration/functions"
|
51
|
+
|
52
|
+
function = Function.new(
|
53
|
+
name: name,
|
54
|
+
class_type: class_path,
|
55
|
+
resources: resources
|
56
|
+
)
|
57
|
+
|
58
|
+
resp = client.post(path, body: build_create_body(function))
|
59
|
+
|
60
|
+
function.tap do |obj|
|
61
|
+
obj.location = resp.headers['Location']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Update function in project
|
66
|
+
#
|
67
|
+
# @see http://repo.aliyun.com/api-doc/Function/put_function/index.html Put function
|
68
|
+
#
|
69
|
+
# @param name [String] specify function name
|
70
|
+
# @param class_path [String] specify class Path used by function
|
71
|
+
# @param resources [Array<Model::Resource>] specify resources used by function
|
72
|
+
#
|
73
|
+
# @return [true]
|
74
|
+
def update(name, class_path, resources = [])
|
75
|
+
path = "/projects/#{project.name}/registration/functions/#{name}"
|
76
|
+
|
77
|
+
function = Function.new(
|
78
|
+
name: name,
|
79
|
+
class_type: class_path,
|
80
|
+
resources: resources
|
81
|
+
)
|
82
|
+
!!client.put(path, body: build_create_body(function))
|
83
|
+
end
|
84
|
+
|
85
|
+
# Delete function in project
|
86
|
+
#
|
87
|
+
# @see http://repo.aliyun.com/api-doc/Function/delete_function/index.html Delete function
|
88
|
+
#
|
89
|
+
# @param name [String] specify function name
|
90
|
+
#
|
91
|
+
# @return [true]
|
92
|
+
def delete(name)
|
93
|
+
path = "/projects/#{project.name}/registration/functions/#{name}"
|
94
|
+
!!client.delete(path)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def build_create_body(function)
|
100
|
+
fail XmlElementMissingError, 'ClassType' if function.class_type.nil?
|
101
|
+
fail XmlElementMissingError, 'Resources' if function.resources.empty?
|
102
|
+
|
103
|
+
Utils.to_xml(
|
104
|
+
'Function' => {
|
105
|
+
'Alias' => function.name,
|
106
|
+
'ClassType' => function.class_type,
|
107
|
+
'Resources' => function.resources.map(&:to_hash)
|
108
|
+
}
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Odps
|
3
|
+
class Instance < Struct::Base
|
4
|
+
extend Aliyun::Odps::Modelable
|
5
|
+
|
6
|
+
NAME_PATTERN = /^([a-z]|[A-Z]){1,}([a-z]|[A-Z]|[\d]|_)*/
|
7
|
+
|
8
|
+
property :project, Project, required: true
|
9
|
+
|
10
|
+
property :name, String, required: true
|
11
|
+
property :owner, String
|
12
|
+
property :comment, String
|
13
|
+
property :priority, Integer
|
14
|
+
property :tasks, Array
|
15
|
+
property :status, String
|
16
|
+
property :start_time, DateTime
|
17
|
+
property :end_time, DateTime
|
18
|
+
property :location, String
|
19
|
+
|
20
|
+
# Get task detail of instance
|
21
|
+
#
|
22
|
+
# @see http://repo.aliyun.com/api-doc/Instance/get_instance_detail/index.html Get instance detail
|
23
|
+
#
|
24
|
+
# @params task_name [String] specify task name
|
25
|
+
#
|
26
|
+
# @return [Hash]
|
27
|
+
def task_detail(task_name)
|
28
|
+
path = "/projects/#{project.name}/instances/#{name}"
|
29
|
+
query = { instancedetail: true, taskname: task_name }
|
30
|
+
client.get(path, query: query).parsed_response
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get task progress of instance
|
34
|
+
#
|
35
|
+
# @see http://repo.aliyun.com/api-doc/Instance/get_instance_progress/index.html Get instance progress
|
36
|
+
#
|
37
|
+
# @params task_name [String] specify task name
|
38
|
+
#
|
39
|
+
# @return [Hash]
|
40
|
+
def task_progress(task_name)
|
41
|
+
path = "/projects/#{project.name}/instances/#{name}"
|
42
|
+
query = { instanceprogress: true, taskname: task_name }
|
43
|
+
client.get(path, query: query).parsed_response['Progress']
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get task summary of instance
|
47
|
+
#
|
48
|
+
# @see http://repo.aliyun.com/api-doc/Instance/get_instance_summary/index.html Get instance summary
|
49
|
+
#
|
50
|
+
# @params task_name [String] specify task name
|
51
|
+
#
|
52
|
+
# @return [Hash]
|
53
|
+
def task_summary(task_name)
|
54
|
+
path = "/projects/#{project.name}/instances/#{name}"
|
55
|
+
query = { instancesummary: true, taskname: task_name }
|
56
|
+
client.get(path, query: query).parsed_response
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get task results
|
60
|
+
#
|
61
|
+
# @return [Hash<name, TaskResult>]
|
62
|
+
def task_results
|
63
|
+
path = "/projects/#{project.name}/instances/#{name}"
|
64
|
+
query = { result: true }
|
65
|
+
result = client.get(path, query: query).parsed_response
|
66
|
+
task_results = Utils.dig_value(result, 'Instance', 'Tasks', 'Task')
|
67
|
+
Hash[Utils.wrap(task_results).map { |v| [v['Name'], Aliyun::Odps::TaskResult.new(v)] }]
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get tasks of instance
|
71
|
+
#
|
72
|
+
# @see http://repo.aliyun.com/api-doc/Instance/get_instance_task/index.html Get instance task
|
73
|
+
#
|
74
|
+
# @return [List]
|
75
|
+
def list_tasks
|
76
|
+
path = "/projects/#{project.name}/instances/#{name}"
|
77
|
+
query = { taskstatus: true }
|
78
|
+
result = client.get(path, query: query).parsed_response
|
79
|
+
|
80
|
+
keys = %w(Instance Tasks Task)
|
81
|
+
Utils.wrap(Utils.dig_value(result, *keys)).map do |hash|
|
82
|
+
InstanceTask.new(hash)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Terminate the instance
|
87
|
+
#
|
88
|
+
# @see http://repo.aliyun.com/api-doc/Instance/put_instance_terminate/index.html Put instance terminated
|
89
|
+
#
|
90
|
+
# @return true
|
91
|
+
def terminate
|
92
|
+
path = "/projects/#{project.name}/instances/#{name}"
|
93
|
+
|
94
|
+
body = Utils.to_xml(
|
95
|
+
'Instance' => { 'Status' => 'Terminated' }
|
96
|
+
)
|
97
|
+
!!client.put(path, body: body)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Get status
|
101
|
+
#
|
102
|
+
# @see http://repo.aliyun.com/api-doc/Instance/get_instance/index.html Get instance
|
103
|
+
#
|
104
|
+
# @return [String] Instance status: Suspended, Running, Terminated
|
105
|
+
def get_status
|
106
|
+
path = "/projects/#{project.name}/instances/#{name}"
|
107
|
+
result = client.get(path).parsed_response
|
108
|
+
Utils.dig_value(result, 'Instance', 'Status')
|
109
|
+
end
|
110
|
+
|
111
|
+
# Block process until instance success
|
112
|
+
#
|
113
|
+
# @raise [InstanceTaskNotSuccessError] if task not success
|
114
|
+
def wait_for_success(interval = 0.01)
|
115
|
+
wait_for_terminated(interval)
|
116
|
+
|
117
|
+
list_tasks.each do |task|
|
118
|
+
if task.status.upcase != 'SUCCESS'
|
119
|
+
fail InstanceTaskNotSuccessError.new(task.name, task.status, task_results[task.name].result)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Block process until instance terminated
|
125
|
+
def wait_for_terminated(interval = 0.01)
|
126
|
+
sleep interval while get_status != 'Terminated'
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|