k8s-client 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rubocop.relaxed.yml +176 -0
- data/.rubocop.yml +57 -0
- data/Rakefile +11 -1
- data/bin/k8s-client +32 -36
- data/k8s-client.gemspec +2 -0
- data/lib/k8s-client.rb +2 -16
- data/lib/k8s/api.rb +3 -1
- data/lib/k8s/api/metav1.rb +2 -0
- data/lib/k8s/api/metav1/api_group.rb +2 -0
- data/lib/k8s/api/metav1/api_resource.rb +2 -0
- data/lib/k8s/api/metav1/list.rb +2 -0
- data/lib/k8s/api/metav1/object.rb +2 -0
- data/lib/k8s/api/metav1/status.rb +2 -0
- data/lib/k8s/api/version.rb +2 -0
- data/lib/k8s/api_client.rb +25 -28
- data/lib/k8s/client.rb +34 -15
- data/lib/k8s/client/version.rb +3 -1
- data/lib/k8s/config.rb +14 -7
- data/lib/k8s/error.rb +27 -26
- data/lib/k8s/logging.rb +4 -2
- data/lib/k8s/resource.rb +9 -7
- data/lib/k8s/resource_client.rb +39 -45
- data/lib/k8s/stack.rb +22 -14
- data/lib/k8s/transport.rb +59 -50
- data/lib/k8s/util.rb +10 -9
- metadata +32 -2
data/lib/k8s/logging.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'logger'
|
2
4
|
|
3
5
|
module K8s
|
@@ -51,8 +53,8 @@ module K8s
|
|
51
53
|
# @return [Logger]
|
52
54
|
def logger(target: LOG_TARGET, level: nil)
|
53
55
|
@logger ||= Logger.new(target).tap do |logger|
|
54
|
-
logger.progname =
|
55
|
-
logger.level = level ||
|
56
|
+
logger.progname = name
|
57
|
+
logger.level = level || log_level || K8s::Logging.log_level || LOG_LEVEL
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
data/lib/k8s/resource.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'deep_merge'
|
2
4
|
require 'recursive-open-struct'
|
3
5
|
require 'hashdiff'
|
@@ -11,7 +13,7 @@ module K8s
|
|
11
13
|
# @param data [Hash]
|
12
14
|
# @return [self]
|
13
15
|
def self.from_json(data)
|
14
|
-
|
16
|
+
new(data)
|
15
17
|
end
|
16
18
|
|
17
19
|
# @param filename [String] file path
|
@@ -27,9 +29,9 @@ module K8s
|
|
27
29
|
|
28
30
|
if stat.directory?
|
29
31
|
# recurse
|
30
|
-
Dir.glob("#{path}/*.{yml,yaml}").sort.map { |dir|
|
32
|
+
Dir.glob("#{path}/*.{yml,yaml}").sort.map { |dir| from_files(dir) }.flatten
|
31
33
|
else
|
32
|
-
::YAML.load_stream(File.read(path), path).map{|doc| new(doc) }
|
34
|
+
::YAML.load_stream(File.read(path), path).map{ |doc| new(doc) }
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
@@ -67,7 +69,7 @@ module K8s
|
|
67
69
|
end
|
68
70
|
|
69
71
|
def checksum
|
70
|
-
@checksum ||= Digest::MD5.hexdigest(Marshal
|
72
|
+
@checksum ||= Digest::MD5.hexdigest(Marshal.dump(to_hash))
|
71
73
|
end
|
72
74
|
|
73
75
|
def merge_patch_ops(attrs, config_annotation)
|
@@ -78,7 +80,7 @@ module K8s
|
|
78
80
|
#
|
79
81
|
# @return [Hash]
|
80
82
|
def current_config(config_annotation)
|
81
|
-
current_cfg =
|
83
|
+
current_cfg = metadata.annotations&.dig(config_annotation)
|
82
84
|
return {} unless current_cfg
|
83
85
|
|
84
86
|
current_hash = JSON.parse(current_cfg)
|
@@ -89,11 +91,11 @@ module K8s
|
|
89
91
|
end
|
90
92
|
|
91
93
|
def can_patch?(config_annotation)
|
92
|
-
!!
|
94
|
+
!!metadata.annotations&.dig(config_annotation)
|
93
95
|
end
|
94
96
|
|
95
97
|
def stringify_hash(hash)
|
96
|
-
JSON.
|
98
|
+
JSON.parse(JSON.dump(hash))
|
97
99
|
end
|
98
100
|
end
|
99
101
|
end
|
data/lib/k8s/resource_client.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module K8s
|
2
4
|
# Per-APIResource type client.
|
3
5
|
#
|
4
6
|
# Used to get/list/update/patch/delete specific types of resources, optionally in some specific namespace.
|
5
7
|
class ResourceClient
|
6
|
-
|
7
8
|
# Common helpers used in both class/instance methods
|
8
9
|
module Utils
|
9
10
|
# @param selector [nil, String, Hash{String => String}]
|
@@ -15,7 +16,7 @@ module K8s
|
|
15
16
|
when String
|
16
17
|
selector
|
17
18
|
when Hash
|
18
|
-
selector.map{|k, v| "#{k}=#{v}"}.join ','
|
19
|
+
selector.map{ |k, v| "#{k}=#{v}" }.join ','
|
19
20
|
else
|
20
21
|
fail "Invalid selector type. #{selector.inspect}"
|
21
22
|
end
|
@@ -47,17 +48,18 @@ module K8s
|
|
47
48
|
# @param skip_forbidden [Boolean] skip resources that return HTTP 403 errors
|
48
49
|
# @return [Array<K8s::Resource>]
|
49
50
|
def self.list(resources, transport, namespace: nil, labelSelector: nil, fieldSelector: nil, skip_forbidden: false)
|
50
|
-
api_paths = resources.map{|resource| resource.path(namespace: namespace) }
|
51
|
-
api_lists = transport.gets(
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
51
|
+
api_paths = resources.map{ |resource| resource.path(namespace: namespace) }
|
52
|
+
api_lists = transport.gets(
|
53
|
+
*api_paths,
|
54
|
+
response_class: K8s::API::MetaV1::List,
|
55
|
+
query: make_query(
|
56
|
+
'labelSelector' => selector_query(labelSelector),
|
57
|
+
'fieldSelector' => selector_query(fieldSelector)
|
58
|
+
),
|
59
|
+
skip_forbidden: skip_forbidden
|
60
|
+
)
|
61
|
+
|
62
|
+
resources.zip(api_lists).map { |resource, api_list| api_list ? resource.process_list(api_list) : [] }.flatten
|
61
63
|
end
|
62
64
|
|
63
65
|
# @param transport [K8s::Transport]
|
@@ -78,7 +80,7 @@ module K8s
|
|
78
80
|
@subresource = nil
|
79
81
|
end
|
80
82
|
|
81
|
-
fail "Resource #{api_resource.name} is not namespaced"
|
83
|
+
fail "Resource #{api_resource.name} is not namespaced" unless api_resource.namespaced || !namespace
|
82
84
|
end
|
83
85
|
|
84
86
|
# @return [String]
|
@@ -92,14 +94,10 @@ module K8s
|
|
92
94
|
end
|
93
95
|
|
94
96
|
# @return [String, nil]
|
95
|
-
|
96
|
-
@namespace
|
97
|
-
end
|
97
|
+
attr_reader :namespace
|
98
98
|
|
99
99
|
# @return [String]
|
100
|
-
|
101
|
-
@resource
|
102
|
-
end
|
100
|
+
attr_reader :resource
|
103
101
|
|
104
102
|
# @return [Boolean]
|
105
103
|
def subresource?
|
@@ -107,9 +105,7 @@ module K8s
|
|
107
105
|
end
|
108
106
|
|
109
107
|
# @return [String, nil]
|
110
|
-
|
111
|
-
@subresource
|
112
|
-
end
|
108
|
+
attr_reader :subresource
|
113
109
|
|
114
110
|
# @return [String]
|
115
111
|
def kind
|
@@ -117,9 +113,7 @@ module K8s
|
|
117
113
|
end
|
118
114
|
|
119
115
|
# @return [class] K8s::Resource
|
120
|
-
|
121
|
-
@resource_class
|
122
|
-
end
|
116
|
+
attr_reader :resource_class
|
123
117
|
|
124
118
|
# @return [Bool]
|
125
119
|
def namespaced?
|
@@ -149,9 +143,9 @@ module K8s
|
|
149
143
|
def create_resource(resource)
|
150
144
|
@transport.request(
|
151
145
|
method: 'POST',
|
152
|
-
path:
|
146
|
+
path: path(namespace: resource.metadata.namespace),
|
153
147
|
request_object: resource,
|
154
|
-
response_class: @resource_class
|
148
|
+
response_class: @resource_class
|
155
149
|
)
|
156
150
|
end
|
157
151
|
|
@@ -164,8 +158,8 @@ module K8s
|
|
164
158
|
def get(name, namespace: @namespace)
|
165
159
|
@transport.request(
|
166
160
|
method: 'GET',
|
167
|
-
path:
|
168
|
-
response_class: @resource_class
|
161
|
+
path: path(name, namespace: namespace),
|
162
|
+
response_class: @resource_class
|
169
163
|
)
|
170
164
|
end
|
171
165
|
|
@@ -174,8 +168,8 @@ module K8s
|
|
174
168
|
def get_resource(resource)
|
175
169
|
@transport.request(
|
176
170
|
method: 'GET',
|
177
|
-
path:
|
178
|
-
response_class: @resource_class
|
171
|
+
path: path(resource.metadata.name, namespace: resource.metadata.namespace),
|
172
|
+
response_class: @resource_class
|
179
173
|
)
|
180
174
|
end
|
181
175
|
|
@@ -187,7 +181,7 @@ module K8s
|
|
187
181
|
# @param list [K8s::API::MetaV1::List]
|
188
182
|
# @return [Array<resource_class>]
|
189
183
|
def process_list(list)
|
190
|
-
list.items.map {|item|
|
184
|
+
list.items.map { |item|
|
191
185
|
# list items omit kind/apiVersion
|
192
186
|
@resource_class.new(item.merge('apiVersion' => list.apiVersion, 'kind' => @api_resource.kind))
|
193
187
|
}
|
@@ -199,12 +193,12 @@ module K8s
|
|
199
193
|
def list(labelSelector: nil, fieldSelector: nil, namespace: @namespace)
|
200
194
|
list = @transport.request(
|
201
195
|
method: 'GET',
|
202
|
-
path:
|
196
|
+
path: path(namespace: namespace),
|
203
197
|
response_class: K8s::API::MetaV1::List,
|
204
198
|
query: make_query(
|
205
199
|
'labelSelector' => selector_query(labelSelector),
|
206
|
-
'fieldSelector' => selector_query(fieldSelector)
|
207
|
-
)
|
200
|
+
'fieldSelector' => selector_query(fieldSelector)
|
201
|
+
)
|
208
202
|
)
|
209
203
|
process_list(list)
|
210
204
|
end
|
@@ -219,9 +213,9 @@ module K8s
|
|
219
213
|
def update_resource(resource)
|
220
214
|
@transport.request(
|
221
215
|
method: 'PUT',
|
222
|
-
path:
|
216
|
+
path: path(resource.metadata.name, namespace: resource.metadata.namespace),
|
223
217
|
request_object: resource,
|
224
|
-
response_class: @resource_class
|
218
|
+
response_class: @resource_class
|
225
219
|
)
|
226
220
|
end
|
227
221
|
|
@@ -238,10 +232,10 @@ module K8s
|
|
238
232
|
def merge_patch(name, obj, namespace: @namespace, strategic_merge: true)
|
239
233
|
@transport.request(
|
240
234
|
method: 'PATCH',
|
241
|
-
path:
|
235
|
+
path: path(name, namespace: namespace),
|
242
236
|
content_type: strategic_merge ? 'application/strategic-merge-patch+json' : 'application/merge-patch+json',
|
243
237
|
request_object: obj,
|
244
|
-
response_class: @resource_class
|
238
|
+
response_class: @resource_class
|
245
239
|
)
|
246
240
|
end
|
247
241
|
|
@@ -252,10 +246,10 @@ module K8s
|
|
252
246
|
def json_patch(name, ops, namespace: @namespace)
|
253
247
|
@transport.request(
|
254
248
|
method: 'PATCH',
|
255
|
-
path:
|
249
|
+
path: path(name, namespace: namespace),
|
256
250
|
content_type: 'application/json-patch+json',
|
257
251
|
request_object: ops,
|
258
|
-
response_class: @resource_class
|
252
|
+
response_class: @resource_class
|
259
253
|
)
|
260
254
|
end
|
261
255
|
|
@@ -271,7 +265,7 @@ module K8s
|
|
271
265
|
def delete(name, namespace: @namespace, propagationPolicy: nil)
|
272
266
|
@transport.request(
|
273
267
|
method: 'DELETE',
|
274
|
-
path:
|
268
|
+
path: path(name, namespace: namespace),
|
275
269
|
query: make_query(
|
276
270
|
'propagationPolicy' => propagationPolicy
|
277
271
|
),
|
@@ -286,11 +280,11 @@ module K8s
|
|
286
280
|
def delete_collection(namespace: @namespace, labelSelector: nil, fieldSelector: nil, propagationPolicy: nil)
|
287
281
|
list = @transport.request(
|
288
282
|
method: 'DELETE',
|
289
|
-
path:
|
283
|
+
path: path(namespace: namespace),
|
290
284
|
query: make_query(
|
291
285
|
'labelSelector' => selector_query(labelSelector),
|
292
286
|
'fieldSelector' => selector_query(fieldSelector),
|
293
|
-
'propagationPolicy' => propagationPolicy
|
287
|
+
'propagationPolicy' => propagationPolicy
|
294
288
|
),
|
295
289
|
response_class: K8s::API::MetaV1::List, # XXX: documented as returning Status
|
296
290
|
)
|
data/lib/k8s/stack.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'securerandom'
|
2
4
|
|
3
5
|
module K8s
|
@@ -19,7 +21,7 @@ module K8s
|
|
19
21
|
PRUNE_IGNORE = [
|
20
22
|
'v1:ComponentStatus', # apiserver ignores GET /v1/componentstatuses?labelSelector=... and returns all resources
|
21
23
|
'v1:Endpoints', # inherits stack label from service, but not checksum annotation
|
22
|
-
]
|
24
|
+
].freeze
|
23
25
|
|
24
26
|
# @param name [String] unique name for stack
|
25
27
|
# @param path [String] load resources from YAML files
|
@@ -59,23 +61,27 @@ module K8s
|
|
59
61
|
end
|
60
62
|
|
61
63
|
# @param resource [K8s::Resource] to apply
|
62
|
-
# @param base_resource [K8s::Resource]
|
64
|
+
# @param base_resource [K8s::Resource] DEPRECATED
|
63
65
|
# @return [K8s::Resource]
|
66
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
64
67
|
def prepare_resource(resource, base_resource: nil)
|
65
|
-
#
|
68
|
+
# TODO: base_resource is not used anymore, kept for backwards compatibility for a while
|
66
69
|
|
67
70
|
# calculate checksum only from the "local" source
|
68
71
|
checksum = resource.checksum
|
69
72
|
|
70
73
|
# add stack metadata
|
71
|
-
resource.merge(
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
resource.merge(
|
75
|
+
metadata: {
|
76
|
+
labels: { @label => name },
|
77
|
+
annotations: {
|
78
|
+
@checksum_annotation => checksum,
|
79
|
+
@last_config_annotation => resource.to_json
|
80
|
+
}
|
81
|
+
}
|
82
|
+
)
|
78
83
|
end
|
84
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
79
85
|
|
80
86
|
# @return [Array<K8s::Resource>]
|
81
87
|
def apply(client, prune: true)
|
@@ -107,14 +113,15 @@ module K8s
|
|
107
113
|
def keep_resource!(resource)
|
108
114
|
@keep_resources["#{resource.kind}:#{resource.metadata.name}@#{resource.metadata.namespace}"] = resource.metadata.annotations[@checksum_annotation]
|
109
115
|
end
|
116
|
+
|
110
117
|
def keep_resource?(resource)
|
111
118
|
@keep_resources["#{resource.kind}:#{resource.metadata.name}@#{resource.metadata.namespace}"] == resource.metadata.annotations[@checksum_annotation]
|
112
119
|
end
|
113
120
|
|
114
121
|
# Delete all stack resources that were not applied
|
115
|
-
def prune(client, keep_resources
|
122
|
+
def prune(client, keep_resources:, skip_forbidden: true)
|
116
123
|
# using skip_forbidden: assume we can't create resource types that we are forbidden to list, so we don't need to prune them either
|
117
|
-
client.list_resources(labelSelector: {@label => name}, skip_forbidden: skip_forbidden).sort
|
124
|
+
client.list_resources(labelSelector: { @label => name }, skip_forbidden: skip_forbidden).sort do |a, b|
|
118
125
|
# Sort resources so that namespaced objects are deleted first
|
119
126
|
if a.metadata.namespace == b.metadata.namespace
|
120
127
|
0
|
@@ -123,7 +130,7 @@ module K8s
|
|
123
130
|
else
|
124
131
|
-1
|
125
132
|
end
|
126
|
-
|
133
|
+
end.each do |resource|
|
127
134
|
next if PRUNE_IGNORE.include? "#{resource.apiVersion}:#{resource.kind}"
|
128
135
|
|
129
136
|
resource_label = resource.metadata.labels ? resource.metadata.labels[@label] : nil
|
@@ -139,9 +146,10 @@ module K8s
|
|
139
146
|
logger.info "Delete resource #{resource.apiVersion}:#{resource.kind}/#{resource.metadata.name} in namespace #{resource.metadata.namespace}"
|
140
147
|
begin
|
141
148
|
client.delete_resource(resource, propagationPolicy: 'Background')
|
142
|
-
rescue K8s::Error::NotFound
|
149
|
+
rescue K8s::Error::NotFound => ex
|
143
150
|
# assume aliased objects in multiple API groups, like for Deployments
|
144
151
|
# alternatively, a custom resource whose definition was already deleted earlier
|
152
|
+
logger.debug { "Ignoring #{ex} : #{ex.message}" }
|
145
153
|
end
|
146
154
|
end
|
147
155
|
end
|
data/lib/k8s/transport.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'excon'
|
2
4
|
require 'json'
|
5
|
+
require 'jsonpath'
|
3
6
|
|
4
7
|
module K8s
|
5
8
|
# Excon-based HTTP transport handling request/response body JSON encoding
|
@@ -11,13 +14,13 @@ module K8s
|
|
11
14
|
# Excon middlewares for requests
|
12
15
|
EXCON_MIDDLEWARES = [
|
13
16
|
# XXX: necessary? redirected requests omit authz headers?
|
14
|
-
Excon::Middleware::RedirectFollower
|
17
|
+
Excon::Middleware::RedirectFollower
|
15
18
|
] + Excon.defaults[:middlewares]
|
16
19
|
|
17
20
|
# Default request headers
|
18
21
|
REQUEST_HEADERS = {
|
19
|
-
'Accept' => 'application/json'
|
20
|
-
}
|
22
|
+
'Accept' => 'application/json'
|
23
|
+
}.freeze
|
21
24
|
|
22
25
|
# Construct transport from kubeconfig
|
23
26
|
#
|
@@ -67,6 +70,16 @@ module K8s
|
|
67
70
|
logger.debug "Using config with .user.token=..."
|
68
71
|
|
69
72
|
options[:auth_token] = token
|
73
|
+
elsif config.user.auth_provider && auth_provider = config.user.auth_provider.config
|
74
|
+
logger.debug "Using config with .user.auth-provider.name=#{config.user.auth_provider.name}"
|
75
|
+
|
76
|
+
auth_data = `#{auth_provider['cmd-path']} #{auth_provider['cmd-args']}`.strip
|
77
|
+
if auth_provider['token-key']
|
78
|
+
json_path = JsonPath.new(auth_provider['token-key'][1...-1])
|
79
|
+
options[:auth_token] = json_path.first(auth_data)
|
80
|
+
else
|
81
|
+
options[:auth_token] = auth_data
|
82
|
+
end
|
70
83
|
end
|
71
84
|
|
72
85
|
logger.info "Using config with server=#{server}"
|
@@ -81,10 +94,11 @@ module K8s
|
|
81
94
|
host = ENV['KUBERNETES_SERVICE_HOST']
|
82
95
|
port = ENV['KUBERNETES_SERVICE_PORT_HTTPS']
|
83
96
|
|
84
|
-
new(
|
97
|
+
new(
|
98
|
+
"https://#{host}:#{port}",
|
85
99
|
ssl_verify_peer: true,
|
86
100
|
ssl_ca_file: '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt',
|
87
|
-
auth_token: File.read('/var/run/secrets/kubernetes.io/serviceaccount/token')
|
101
|
+
auth_token: File.read('/var/run/secrets/kubernetes.io/serviceaccount/token')
|
88
102
|
)
|
89
103
|
end
|
90
104
|
|
@@ -103,7 +117,8 @@ module K8s
|
|
103
117
|
|
104
118
|
# @return [Excon::Connection]
|
105
119
|
def excon
|
106
|
-
@excon ||= Excon.new(
|
120
|
+
@excon ||= Excon.new(
|
121
|
+
@server,
|
107
122
|
persistent: true,
|
108
123
|
middlewares: EXCON_MIDDLEWARES,
|
109
124
|
headers: REQUEST_HEADERS,
|
@@ -145,6 +160,7 @@ module K8s
|
|
145
160
|
if options[:query]
|
146
161
|
path += Excon::Utils.query_string(options)
|
147
162
|
end
|
163
|
+
|
148
164
|
if obj = options[:request_object]
|
149
165
|
body = "<#{obj.class.name}>"
|
150
166
|
end
|
@@ -178,11 +194,9 @@ module K8s
|
|
178
194
|
raise K8s::Error::API.new(method, path, response.status, "Invalid JSON response: #{response_data.inspect}")
|
179
195
|
end
|
180
196
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
return response_data # Hash
|
185
|
-
end
|
197
|
+
return response_data unless response_class
|
198
|
+
|
199
|
+
response_class.from_json(response_data)
|
186
200
|
else
|
187
201
|
error_class = K8s::Error::HTTP_STATUS_ERRORS[response.status] || K8s::Error::API
|
188
202
|
|
@@ -208,18 +222,17 @@ module K8s
|
|
208
222
|
t = Time.now - start
|
209
223
|
|
210
224
|
obj = parse_response(response, options,
|
211
|
-
|
212
|
-
)
|
225
|
+
response_class: response_class)
|
213
226
|
rescue K8s::Error::API => exc
|
214
|
-
logger.warn { "#{format_request(options)} => HTTP #{exc.code} #{exc.reason} in #{'%.3f' % t}s"}
|
215
|
-
logger.debug { "Request: #{excon_options[:body]}"} if excon_options[:body]
|
216
|
-
logger.debug { "Response: #{response.body}"}
|
227
|
+
logger.warn { "#{format_request(options)} => HTTP #{exc.code} #{exc.reason} in #{'%.3f' % t}s" }
|
228
|
+
logger.debug { "Request: #{excon_options[:body]}" } if excon_options[:body]
|
229
|
+
logger.debug { "Response: #{response.body}" }
|
217
230
|
raise
|
218
231
|
else
|
219
|
-
logger.info { "#{format_request(options)} => HTTP #{response.status}: <#{obj.class}> in #{'%.3f' % t}s"}
|
220
|
-
logger.debug { "Request: #{excon_options[:body]}"} if excon_options[:body]
|
221
|
-
logger.debug { "Response: #{response.body}"}
|
222
|
-
|
232
|
+
logger.info { "#{format_request(options)} => HTTP #{response.status}: <#{obj.class}> in #{'%.3f' % t}s" }
|
233
|
+
logger.debug { "Request: #{excon_options[:body]}" } if excon_options[:body]
|
234
|
+
logger.debug { "Response: #{response.body}" }
|
235
|
+
obj
|
223
236
|
end
|
224
237
|
|
225
238
|
# @param options [Array<Hash>] @see #request
|
@@ -233,46 +246,39 @@ module K8s
|
|
233
246
|
|
234
247
|
start = Time.now
|
235
248
|
responses = excon.requests(
|
236
|
-
options.map{|
|
249
|
+
options.map{ |opts| request_options(**common_options.merge(opts)) }
|
237
250
|
)
|
238
251
|
t = Time.now - start
|
239
252
|
|
240
|
-
objects = responses.zip(options).map{|response, request_options|
|
253
|
+
objects = responses.zip(options).map{ |response, request_options|
|
241
254
|
response_class = request_options[:response_class] || common_options[:response_class]
|
242
255
|
|
243
256
|
begin
|
244
257
|
parse_response(response, request_options,
|
245
|
-
|
246
|
-
)
|
258
|
+
response_class: response_class)
|
247
259
|
rescue K8s::Error::NotFound
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
raise
|
252
|
-
end
|
260
|
+
raise unless skip_missing
|
261
|
+
|
262
|
+
nil
|
253
263
|
rescue K8s::Error::Forbidden
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
raise
|
258
|
-
end
|
264
|
+
raise unless skip_forbidden
|
265
|
+
|
266
|
+
nil
|
259
267
|
rescue K8s::Error::ServiceUnavailable => exc
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
raise
|
267
|
-
end
|
268
|
+
raise unless retry_errors
|
269
|
+
|
270
|
+
logger.warn { "Retry #{format_request(request_options)} => HTTP #{exc.code} #{exc.reason} in #{'%.3f' % t}s" }
|
271
|
+
|
272
|
+
# only retry the failed request, not the entire pipeline
|
273
|
+
request(response_class: response_class, **common_options.merge(request_options))
|
268
274
|
end
|
269
275
|
}
|
270
276
|
rescue K8s::Error => exc
|
271
|
-
logger.warn { "[#{options.map{|o| format_request(o)}.join ', '}] => HTTP #{exc.code} #{exc.reason} in #{'%.3f' % t}s"}
|
277
|
+
logger.warn { "[#{options.map{ |o| format_request(o) }.join ', '}] => HTTP #{exc.code} #{exc.reason} in #{'%.3f' % t}s" }
|
272
278
|
raise
|
273
279
|
else
|
274
|
-
logger.info { "[#{options.map{|o| format_request(o)}.join ', '}] => HTTP [#{responses.map
|
275
|
-
|
280
|
+
logger.info { "[#{options.map{ |o| format_request(o) }.join ', '}] => HTTP [#{responses.map(&:status).join ', '}] in #{'%.3f' % t}s" }
|
281
|
+
objects
|
276
282
|
end
|
277
283
|
|
278
284
|
# @param path [Array<String>] @see #path
|
@@ -281,17 +287,20 @@ module K8s
|
|
281
287
|
request(
|
282
288
|
method: 'GET',
|
283
289
|
path: self.path(*path),
|
284
|
-
**options
|
290
|
+
**options
|
285
291
|
)
|
286
292
|
end
|
287
293
|
|
288
294
|
# @param paths [Array<String>]
|
289
295
|
# @param options [Hash] @see #request
|
290
296
|
def gets(*paths, **options)
|
291
|
-
requests(
|
292
|
-
|
293
|
-
|
294
|
-
|
297
|
+
requests(
|
298
|
+
*paths.map do |path|
|
299
|
+
{
|
300
|
+
method: 'GET',
|
301
|
+
path: self.path(path)
|
302
|
+
}
|
303
|
+
end,
|
295
304
|
**options
|
296
305
|
)
|
297
306
|
end
|