Class: KubernetesDeploy::KubernetesResource
- Inherits:
-
Object
- Object
- KubernetesDeploy::KubernetesResource
show all
- Defined in:
- lib/kubernetes-deploy/kubernetes_resource.rb
Direct Known Subclasses
Bucket, Bugsnag, 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:) ⇒ KubernetesResource
Returns a new instance of KubernetesResource.
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 63
def initialize(namespace:, context:, definition:, logger:)
@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
@namespace = namespace
@context = context
@logger = logger
@definition = definition
@statsd_report_done = false
@validation_errors = []
end
|
Instance Attribute Details
#context ⇒ Object
Returns the value of attribute context.
9
10
11
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 9
def context
@context
end
|
#deploy_started_at=(value) ⇒ Object
Sets the attribute deploy_started_at
10
11
12
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 10
def deploy_started_at=(value)
@deploy_started_at = value
end
|
#name ⇒ Object
Returns the value of attribute name.
9
10
11
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 9
def name
@name
end
|
#namespace ⇒ Object
Returns the value of attribute namespace.
9
10
11
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 9
def namespace
@namespace
end
|
#type ⇒ Object
133
134
135
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 133
def type
@type || self.class.name.demodulize
end
|
Class Method Details
.build(namespace:, context:, definition:, logger:) ⇒ Object
31
32
33
34
35
36
37
38
39
40
41
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 31
def self.build(namespace:, context:, definition:, logger:)
opts = { namespace: namespace, context: context, definition: definition, logger: logger }
if 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
|
.timeout ⇒ Object
43
44
45
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 43
def self.timeout
self::TIMEOUT
end
|
Instance Method Details
#debug_message ⇒ Object
153
154
155
156
157
158
159
160
161
162
163
164
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
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 153
def debug_message
sync_debug_info unless @debug_info_synced
helpful_info = []
if 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
109
110
111
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 109
def deploy_failed?
false
end
|
#deploy_method ⇒ Object
Expected values: :apply, :replace, :replace_force
143
144
145
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 143
def deploy_method
:apply
end
|
#deploy_started? ⇒ Boolean
113
114
115
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 113
def deploy_started?
@deploy_started_at.present?
end
|
#deploy_succeeded? ⇒ Boolean
117
118
119
120
121
122
123
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 117
def deploy_succeeded?
if deploy_started? && !@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
137
138
139
140
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 137
def deploy_timed_out?
return false unless deploy_started?
!deploy_succeeded? && !deploy_failed? && (Time.now.utc - @deploy_started_at > timeout)
end
|
#exists? ⇒ Boolean
125
126
127
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 125
def exists?
nil
end
|
#failure_message ⇒ Object
228
229
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 228
def failure_message
end
|
#fetch_events ⇒ 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)"
]
213
214
215
216
217
218
219
220
221
222
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 213
def fetch_events
return {} unless exists?
out, _err, st = kubectl.run("get", "events", "--output=go-template=#{Event.go_template_for(type, name)}")
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
102
103
104
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 102
def file_path
file.path
end
|
#id ⇒ Object
98
99
100
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 98
def id
"#{type}/#{name}"
end
|
#kubectl ⇒ Object
237
238
239
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 237
def kubectl
@kubectl ||= Kubectl.new(namespace: @namespace, context: @context, logger: @logger, log_failure_by_default: false)
end
|
#pretty_status ⇒ Object
231
232
233
234
235
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 231
def pretty_status
padding = " " * [50 - id.length, 1].max
msg = exists? ? status : "not found"
"#{id}#{padding}#{msg}"
end
|
#pretty_timeout_type ⇒ Object
59
60
61
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 59
def pretty_timeout_type
"timeout: #{timeout}s"
end
|
#report_status_to_statsd(watch_time) ⇒ Object
241
242
243
244
245
246
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 241
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
129
130
131
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 129
def status
@status ||= "Unknown"
end
|
#sync ⇒ Object
106
107
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 106
def sync
end
|
#sync_debug_info ⇒ Object
147
148
149
150
151
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 147
def sync_debug_info
@events = fetch_events unless ENV[DISABLE_FETCHING_EVENT_INFO]
@logs = fetch_logs if supports_logs? && !ENV[DISABLE_FETCHING_EVENT_INFO]
@debug_info_synced = true
end
|
#timeout ⇒ Object
47
48
49
50
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 47
def timeout
return timeout_override if timeout_override.present?
self.class.timeout
end
|
#timeout_message ⇒ Object
224
225
226
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 224
def timeout_message
STANDARD_TIMEOUT_MESSAGE
end
|
#timeout_override ⇒ Object
52
53
54
55
56
57
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 52
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 ⇒ Object
79
80
81
82
83
84
85
86
87
88
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 79
def validate_definition
@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
90
91
92
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 90
def validation_error_msg
@validation_errors.join("\n")
end
|
#validation_failed? ⇒ Boolean
94
95
96
|
# File 'lib/kubernetes-deploy/kubernetes_resource.rb', line 94
def validation_failed?
@validation_errors.present?
end
|