Skip to content

Commit f23cd62

Browse files
Capture mutated resource state for reapply
1 parent 334b639 commit f23cd62

File tree

2 files changed

+40
-36
lines changed

2 files changed

+40
-36
lines changed

lib/krane/kubernetes_resource.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,20 @@ def use_generated_name(instance_data)
381381
@file = create_definition_tempfile
382382
end
383383

384+
# In the presence of admission controllers, an object may be modified by the server. If Krane tries to reapply its
385+
# local definition (for example, pruning a Pod), it will fail because the server has already modified the object.
386+
# To work around this, we update the local definition with the server's modified version.
387+
def update_definition!(definition)
388+
@definition = sanitized_definition(definition)
389+
@file = create_definition_tempfile
390+
end
391+
392+
def sanitized_definition(definition)
393+
definition.delete("status")
394+
definition["metadata"].delete("resourceVersion")
395+
definition
396+
end
397+
384398
class Event
385399
EVENT_SEPARATOR = "ENDEVENT--BEGINEVENT"
386400
FIELD_SEPARATOR = "ENDFIELD--BEGINFIELD"

lib/krane/resource_deployer.rb

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,15 @@ def deploy_resources(resources, prune: false, verify:, record_summary: true)
9999

100100
# Admission hooks can mutate resources, so we need to track those changes, otherwise the subsquent pruning
101101
# may fail if we present a document that no longer matches the resource in the cluster.
102-
updated_individuals = []
103102
individuals.each do |individual_resource|
104103
individual_resource.deploy_started_at = Time.now.utc
105104
case individual_resource.deploy_method
106105
when :create
107-
updated_resource, err, status = create_resource(individual_resource)
108-
updated_individuals << updated_resource if status.success?
106+
err, status = create_resource(individual_resource)
109107
when :replace
110-
updated_resource, err, status = replace_or_create_resource(individual_resource)
111-
updated_individuals << updated_resource if status.success?
108+
err, status = replace_or_create_resource(individual_resource)
112109
when :replace_force
113110
updated_resource, err, status = replace_or_create_resource(individual_resource, force: true)
114-
updated_individuals << updated_resource if status.success?
115111
else
116112
# Fail Fast! This is a programmer mistake.
117113
raise ArgumentError, "Unexpected deploy method! (#{individual_resource.deploy_method.inspect})"
@@ -124,11 +120,10 @@ def deploy_resources(resources, prune: false, verify:, record_summary: true)
124120
MSG
125121
end
126122

127-
applyables += updated_individuals.select { |r| pruneable_types.include?(r.type) && !r.deploy_method_override }
123+
applyables += individuals.select { |r| pruneable_types.include?(r.type) && !r.deploy_method_override }
128124
apply_all(applyables, prune)
129-
watchables = ((applyables + updated_individuals)).uniq
130125
if verify
131-
watcher = Krane::ResourceWatcher.new(resources: watchables, deploy_started_at: deploy_started_at,
126+
watcher = Krane::ResourceWatcher.new(resources: resources, deploy_started_at: deploy_started_at,
132127
timeout: @global_timeout, task_config: @task_config, sha: @current_sha)
133128
watcher.run(record_summary: record_summary)
134129
end
@@ -234,44 +229,39 @@ def replace_or_create_resource(resource, force: false)
234229
["replace", "-f", resource.file_path]
235230
end
236231

237-
updated_resource_definition, err, status = kubectl.run(*args, log_failure: false, output_is_sensitive: resource.sensitive_template_content?,
232+
out, err, status = kubectl.run(*args, log_failure: false, output_is_sensitive: resource.sensitive_template_content?,
238233
raise_if_not_found: true, output: 'json', use_namespace: !resource.global?)
239234

240-
updated_resources = KubernetesResource.build(
241-
namespace: @task_config.namespace,
242-
context: @task_config.context,
243-
definition: updated_resource_definition,
244-
logger:,
245-
statsd_tags:,
246-
crd: resource.is_a?(CustomResource) ? resource.crd : nil,
247-
global_names: []
248-
)
249-
[updated_resource, err, status]
235+
updated_resource_definition = MultiJson.load(out)
236+
# For resources that rely on a generateName attribute, we get the `name` from the result of the call to `create`
237+
# We must explicitly set this name value so that the `apply` step for pruning can run successfully
238+
if status.success? && resource.uses_generate_name?
239+
resource.use_generated_name(updated_resource_definition)
240+
end
241+
242+
resource.update_definition!(updated_resource_definition)
243+
244+
[err, status]
250245
rescue Krane::Kubectl::ResourceNotFoundError
251246
# it doesn't exist so we can't replace it, we try to create it
252247
create_resource(resource)
253248
end
254249

255250
def create_resource(resource)
256-
updated_resource_definition, err, status = kubectl.run("create", "-f", resource.file_path, log_failure: false,
251+
out, err, status = kubectl.run("create", "-f", resource.file_path, log_failure: false,
257252
output: 'json', output_is_sensitive: resource.sensitive_template_content?,
258253
use_namespace: !resource.global?)
259254

260-
updated_resource_hash = JSON.parse(updated_resource_definition)
261-
updated_resource_hash.delete("status")
262-
updated_resource_hash["metadata"].delete("resourceVersion")
263-
updated_resource_hash["metadata"].delete("generateName")
264-
265-
updated_resource = KubernetesResource.build(
266-
namespace: @task_config.namespace,
267-
context: @task_config.context,
268-
definition: updated_resource_hash,
269-
logger:,
270-
statsd_tags:,
271-
crd: resource.is_a?(CustomResource) ? resource.crd : nil,
272-
global_names: []
273-
)
274-
[updated_resource, err, status]
255+
updated_resource_definition = MultiJson.load(out)
256+
# For resources that rely on a generateName attribute, we get the `name` from the result of the call to `create`
257+
# We must explicitly set this name value so that the `apply` step for pruning can run successfully
258+
if status.success? && resource.uses_generate_name?
259+
resource.use_generated_name(updated_resource_definition)
260+
end
261+
262+
resource.update_definition!(updated_resource_definition)
263+
264+
[err, status]
275265
end
276266

277267
# Inspect the file referenced in the kubectl stderr

0 commit comments

Comments
 (0)