Class: KubernetesDeploy::KubernetesResource
- Inherits:
-
Object
- Object
- KubernetesDeploy::KubernetesResource
show all
- Defined in:
- lib/kubernetes-deploy/kubernetes_resource.rb
Direct Known Subclasses
Bucket, Cloudsql, ConfigMap, CronJob, Deployment, Elasticsearch, Ingress, Memcached, PersistentVolumeClaim, Pod, PodDisruptionBudget, PodSetBase, PodTemplate, Redis, ResourceQuota, Service, ServiceAccount, Statefulservice, Topic
Defined Under Namespace
Classes: Event
Constant Summary
collapse
- TIMEOUT =
5.minutes
- LOG_LINE_COUNT =
250
- DISABLE_FETCHING_LOG_INFO =
'DISABLE_FETCHING_LOG_INFO'
- DISABLE_FETCHING_EVENT_INFO =
'DISABLE_FETCHING_EVENT_INFO'
- DISABLED_LOG_INFO_MESSAGE =
"collection is disabled by the #{DISABLE_FETCHING_LOG_INFO} env var."
- DISABLED_EVENT_INFO_MESSAGE =
"collection is disabled by the #{DISABLE_FETCHING_EVENT_INFO} env var."
- DEBUG_RESOURCE_NOT_FOUND_MESSAGE =
"None found. Please check your usual logging service (e.g. Splunk)."
- UNUSUAL_FAILURE_MESSAGE =
<<~MSG
It is very unusual for this resource type to fail to deploy. Please try the deploy again.
If that new deploy also fails, contact your cluster administrator.
MSG
- STANDARD_TIMEOUT_MESSAGE =
<<~MSG
Kubernetes will continue to attempt to deploy this resource in the cluster, but at this point it is considered unlikely that it will succeed.
If you have reason to believe it will succeed, retry the deploy to continue to monitor the rollout.
MSG
- TIMEOUT_OVERRIDE_ANNOTATION =
"kubernetes-deploy.shopify.io/timeout-override"
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Constructor Details
#initialize(namespace:, context:, definition:, logger:, statsd_tags: []) ⇒ KubernetesResource
Returns a new instance of KubernetesResource.
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 71
def initialize(namespace:, context:, definition:, logger:, statsd_tags: [])
@name = definition.dig("metadata", "name")
unless @name.present?
logger.summary.add_paragraph("Rendered template content:\n#{definition.to_yaml}")
raise FatalDeploymentError, "Template is missing required field metadata.name"
end
@optional_statsd_tags = statsd_tags
@namespace = namespace
@context = context
@logger = logger
@definition = definition
@statsd_report_done = false
@validation_errors = []
@instance_data = {}
end
|
Instance Attribute Details
#context ⇒ Object
Returns the value of attribute context.
8
9
10
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 8
def context
@context
end
|
#deploy_started_at=(value) ⇒ Object
Sets the attribute deploy_started_at
9
10
11
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 9
def deploy_started_at=(value)
@deploy_started_at = value
end
|
#name ⇒ Object
Returns the value of attribute name.
8
9
10
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 8
def name
@name
end
|
#namespace ⇒ Object
Returns the value of attribute namespace.
8
9
10
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 8
def namespace
@namespace
end
|
#type ⇒ Object
145
146
147
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 145
def type
@type || self.class.kind
end
|
Class Method Details
.build(namespace:, context:, definition:, logger:, statsd_tags:) ⇒ Object
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 31
def build(namespace:, context:, definition:, logger:, statsd_tags:)
opts = { namespace: namespace, context: context, definition: definition, logger: logger,
statsd_tags: statsd_tags }
if definition["kind"].blank?
raise InvalidTemplateError.new("Template missing 'Kind'", content: definition.to_yaml)
elsif KubernetesDeploy.const_defined?(definition["kind"])
klass = KubernetesDeploy.const_get(definition["kind"])
klass.new(**opts)
else
inst = new(**opts)
inst.type = definition["kind"]
inst
end
end
|
.kind ⇒ Object
50
51
52
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 50
def kind
name.demodulize
end
|
.timeout ⇒ Object
46
47
48
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 46
def timeout
self::TIMEOUT
end
|
Instance Method Details
#debug_message(cause = nil, info_hash = {}) ⇒ Object
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 165
def debug_message(cause = nil, info_hash = {})
helpful_info = []
if cause == :gave_up
helpful_info << ColorizedString.new("#{id}: GLOBAL WATCH TIMEOUT (#{info_hash[:timeout]} seconds)").yellow
helpful_info << "If you expected it to take longer than #{info_hash[:timeout]} seconds for your deploy"\
" to roll out, increase --max-watch-seconds."
elsif deploy_failed?
helpful_info << ColorizedString.new("#{id}: FAILED").red
helpful_info << failure_message if failure_message.present?
elsif deploy_timed_out?
helpful_info << ColorizedString.new("#{id}: TIMED OUT (#{pretty_timeout_type})").yellow
helpful_info << timeout_message if timeout_message.present?
else
helpful_info << ColorizedString.new("#{id}: MONITORING ERROR").red
helpful_info << failure_message if failure_message.present?
helpful_info << timeout_message if timeout_message.present? && timeout_message != STANDARD_TIMEOUT_MESSAGE
end
helpful_info << " - Final status: #{status}"
if @events.present?
helpful_info << " - Events (common success events excluded):"
@events.each do |identifier, event_hashes|
event_hashes.each { |event| helpful_info << " [#{identifier}]\t#{event}" }
end
elsif ENV[DISABLE_FETCHING_EVENT_INFO]
helpful_info << " - Events: #{DISABLED_EVENT_INFO_MESSAGE}"
else
helpful_info << " - Events: #{DEBUG_RESOURCE_NOT_FOUND_MESSAGE}"
end
if supports_logs?
if ENV[DISABLE_FETCHING_LOG_INFO]
helpful_info << " - Logs: #{DISABLED_LOG_INFO_MESSAGE}"
elsif @logs.blank? || @logs.values.all?(&:blank?)
helpful_info << " - Logs: #{DEBUG_RESOURCE_NOT_FOUND_MESSAGE}"
else
sorted_logs = @logs.sort_by { |_, log_lines| log_lines.length }
sorted_logs.each do |identifier, log_lines|
if log_lines.empty?
helpful_info << " - Logs from container '#{identifier}': #{DEBUG_RESOURCE_NOT_FOUND_MESSAGE}"
next
end
helpful_info << " - Logs from container '#{identifier}' (last #{LOG_LINE_COUNT} lines shown):"
log_lines.each do |line|
helpful_info << " #{line}"
end
end
end
end
helpful_info.join("\n")
end
|
#deploy_failed? ⇒ Boolean
120
121
122
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 120
def deploy_failed?
false
end
|
#deploy_method ⇒ Object
Expected values: :apply, :replace, :replace_force
155
156
157
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 155
def deploy_method
:apply
end
|
#deploy_started? ⇒ Boolean
124
125
126
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 124
def deploy_started?
@deploy_started_at.present?
end
|
#deploy_succeeded? ⇒ Boolean
128
129
130
131
132
133
134
135
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 128
def deploy_succeeded?
return false unless deploy_started?
unless @success_assumption_warning_shown
@logger.warn("Don't know how to monitor resources of type #{type}. Assuming #{id} deployed successfully.")
@success_assumption_warning_shown = true
end
true
end
|
#deploy_timed_out? ⇒ Boolean
149
150
151
152
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 149
def deploy_timed_out?
return false unless deploy_started?
!deploy_succeeded? && !deploy_failed? && (Time.now.utc - @deploy_started_at > timeout)
end
|
#exists? ⇒ Boolean
137
138
139
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 137
def exists?
@instance_data.present?
end
|
#failure_message ⇒ Object
243
244
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 243
def failure_message
end
|
#fetch_events(kubectl) ⇒ Object
Returns a hash in the following format:
"pod/web-1" => [
"Pulling: pulling image "hello-world:latest" (1 events)",
"Pulled: Successfully pulled image "hello-world:latest" (1 events)"
]
227
228
229
230
231
232
233
234
235
236
237
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 227
def fetch_events(kubectl)
return {} unless exists?
out, _err, st = kubectl.run("get", "events", "--output=go-template=#{Event.go_template_for(type, name)}",
log_failure: false)
return {} unless st.success?
event_collector = Hash.new { |hash, key| hash[key] = [] }
Event.(out).each_with_object(event_collector) do |candidate, events|
events[id] << candidate.to_s if candidate.seen_since?(@deploy_started_at - 5.seconds)
end
end
|
#file_path ⇒ Object
112
113
114
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 112
def file_path
file.path
end
|
#id ⇒ Object
108
109
110
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 108
def id
"#{type}/#{name}"
end
|
#pretty_status ⇒ Object
246
247
248
249
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 246
def pretty_status
padding = " " * [50 - id.length, 1].max
"#{id}#{padding}#{status}"
end
|
#pretty_timeout_type ⇒ Object
67
68
69
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 67
def pretty_timeout_type
"timeout: #{timeout}s"
end
|
#report_status_to_statsd(watch_time) ⇒ Object
251
252
253
254
255
256
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 251
def report_status_to_statsd(watch_time)
unless @statsd_report_done
::StatsD.measure('resource.duration', watch_time, tags: statsd_tags)
@statsd_report_done = true
end
end
|
#status ⇒ Object
141
142
143
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 141
def status
exists? ? "Exists" : "Unknown"
end
|
#sync(mediator) ⇒ Object
116
117
118
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 116
def sync(mediator)
@instance_data = mediator.get_instance(type, name)
end
|
#sync_debug_info(kubectl) ⇒ Object
159
160
161
162
163
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 159
def sync_debug_info(kubectl)
@events = fetch_events(kubectl) unless ENV[DISABLE_FETCHING_EVENT_INFO]
@logs = fetch_logs(kubectl) if supports_logs? && !ENV[DISABLE_FETCHING_EVENT_INFO]
@debug_info_synced = true
end
|
#timeout ⇒ Object
55
56
57
58
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 55
def timeout
return timeout_override if timeout_override.present?
self.class.timeout
end
|
#timeout_message ⇒ Object
239
240
241
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 239
def timeout_message
STANDARD_TIMEOUT_MESSAGE
end
|
#timeout_override ⇒ Object
60
61
62
63
64
65
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 60
def timeout_override
return @timeout_override if defined?(@timeout_override)
@timeout_override = DurationParser.new(timeout_annotation).parse!.to_i
rescue DurationParser::ParsingError
@timeout_override = nil
end
|
#validate_definition(kubectl) ⇒ Object
89
90
91
92
93
94
95
96
97
98
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 89
def validate_definition(kubectl)
@validation_errors = []
validate_timeout_annotation
command = ["create", "-f", file_path, "--dry-run", "--output=name"]
_, err, st = kubectl.run(*command, log_failure: false)
return true if st.success?
@validation_errors << err
false
end
|
#validation_error_msg ⇒ Object
100
101
102
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 100
def validation_error_msg
@validation_errors.join("\n")
end
|
#validation_failed? ⇒ Boolean
104
105
106
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 104
def validation_failed?
@validation_errors.present?
end
|