class Krane::Deployment

Constants

DEFAULT_REQUIRED_ROLLOUT
REQUIRED_ROLLOUT_ANNOTATION
REQUIRED_ROLLOUT_ANNOTATION_DEPRECATED
REQUIRED_ROLLOUT_ANNOTATION_SUFFIX
REQUIRED_ROLLOUT_TYPES
TIMEOUT

Public Instance Methods

deploy_failed?() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 59
def deploy_failed?
  @latest_rs&.deploy_failed? &&
  observed_generation == current_generation
end
deploy_succeeded?() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 37
def deploy_succeeded?
  return false unless exists? && @latest_rs.present?
  return false unless observed_generation == current_generation

  if required_rollout == 'full'
    @latest_rs.deploy_succeeded? &&
    @latest_rs.desired_replicas == desired_replicas && # latest RS fully scaled up
    rollout_data["updatedReplicas"].to_i == desired_replicas &&
    rollout_data["updatedReplicas"].to_i == rollout_data["availableReplicas"].to_i
  elsif required_rollout == 'none'
    true
  elsif required_rollout == 'maxUnavailable' || percent?(required_rollout)
    minimum_needed = min_available_replicas

    @latest_rs.desired_replicas >= minimum_needed &&
    @latest_rs.ready_replicas >= minimum_needed &&
    @latest_rs.available_replicas >= minimum_needed
  else
    raise FatalDeploymentError, rollout_annotation_err_msg
  end
end
deploy_timed_out?() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 91
def deploy_timed_out?
  return false if deploy_failed?
  return super if timeout_override

  # Do not use the hard timeout if progress deadline is set
  progress_condition.present? ? deploy_failing_to_progress? : super
end
failure_message() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 64
def failure_message
  return unless @latest_rs.present?
  "Latest ReplicaSet: #{@latest_rs.name}\n\n#{@latest_rs.failure_message}"
end
fetch_debug_logs() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 33
def fetch_debug_logs
  @latest_rs.fetch_debug_logs
end
fetch_events(kubectl) click to toggle source
Calls superclass method Krane::KubernetesResource#fetch_events
# File lib/krane/kubernetes_resource/deployment.rb, line 23
def fetch_events(kubectl)
  own_events = super
  return own_events unless @latest_rs.present?
  own_events.merge(@latest_rs.fetch_events(kubectl))
end
pretty_timeout_type() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 81
def pretty_timeout_type
  if timeout_override
    "timeout override: #{timeout_override}s"
  elsif progress_deadline.present?
    "progress deadline: #{progress_deadline}s"
  else
    super
  end
end
print_debug_logs?() click to toggle source
status() click to toggle source
Calls superclass method Krane::KubernetesResource#status
# File lib/krane/kubernetes_resource/deployment.rb, line 18
def status
  return super unless exists?
  rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
end
sync(cache) click to toggle source
Calls superclass method Krane::KubernetesResource#sync
# File lib/krane/kubernetes_resource/deployment.rb, line 13
def sync(cache)
  super
  @latest_rs = exists? ? find_latest_rs(cache) : nil
end
timeout_message() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 69
def timeout_message
  reason_msg = if timeout_override
    STANDARD_TIMEOUT_MESSAGE
  elsif progress_condition.present?
    "Timeout reason: #{progress_condition['reason']}"
  else
    "Timeout reason: hard deadline for #{type}"
  end
  return reason_msg unless @latest_rs.present?
  "#{reason_msg}\nLatest ReplicaSet: #{@latest_rs.name}\n\n#{@latest_rs.timeout_message}"
end
validate_definition(*) click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 99
def validate_definition(*)
  super

  unless REQUIRED_ROLLOUT_TYPES.include?(required_rollout) || percent?(required_rollout)
    @validation_errors << rollout_annotation_err_msg
  end

  strategy = @definition.dig('spec', 'strategy', 'type').to_s
  if required_rollout.downcase == 'maxunavailable' && strategy.present? && strategy.downcase != 'rollingupdate'
    @validation_errors << "'#{krane_annotation_key(REQUIRED_ROLLOUT_ANNOTATION_SUFFIX)}: #{required_rollout}' "\
      "is incompatible with strategy '#{strategy}'"
  end

  @validation_errors.empty?
end

Private Instance Methods

current_generation() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 117
def current_generation
  return -2 unless exists? # different default than observed
  @instance_data.dig('metadata', 'generation')
end
deploy_failing_to_progress?() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 157
def deploy_failing_to_progress?
  return false unless progress_condition.present?

  # This assumes that when the controller bumps the observed generation, it also updates/clears all the status
  # conditions. Specifically, it assumes the progress condition is immediately set to True if a rollout is starting.
  deploy_started? &&
  current_generation == observed_generation &&
  progress_condition["status"] == 'False'
end
desired_replicas() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 127
def desired_replicas
  return -1 unless exists?
  @instance_data["spec"]["replicas"].to_i
end
find_latest_rs(cache) click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 167
def find_latest_rs(cache)
  all_rs_data = cache.get_all(ReplicaSet.kind, @instance_data["spec"]["selector"]["matchLabels"])
  current_revision = @instance_data["metadata"]["annotations"]["deployment.kubernetes.io/revision"]

  latest_rs_data = all_rs_data.find do |rs|
    rs["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] } &&
    rs["metadata"]["annotations"]["deployment.kubernetes.io/revision"] == current_revision
  end

  return unless latest_rs_data.present?

  rs = ReplicaSet.new(
    namespace: namespace,
    context: context,
    definition: latest_rs_data,
    logger: @logger,
    parent: "#{@name.capitalize} deployment",
    deploy_started_at: @deploy_started_at
  )
  rs.sync(cache)
  rs
end
max_unavailable() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 200
def max_unavailable
  source = exists? ? @instance_data : @definition
  source.dig('spec', 'strategy', 'rollingUpdate', 'maxUnavailable')
end
min_available_replicas() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 190
def min_available_replicas
  if percent?(required_rollout)
    (desired_replicas * required_rollout.to_i / 100.0).ceil
  elsif max_unavailable =~ /%/
    (desired_replicas * (100 - max_unavailable.to_i) / 100.0).ceil
  else
    desired_replicas - max_unavailable.to_i
  end
end
observed_generation() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 122
def observed_generation
  return -1 unless exists? # different default than current
  @instance_data.dig('status', 'observedGeneration')
end
percent?(value) click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 209
def percent?(value)
  value =~ /\d+%/
end
progress_condition() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 138
def progress_condition
  return unless exists?
  conditions = @instance_data.fetch("status", {}).fetch("conditions", [])
  conditions.find { |condition| condition['type'] == 'Progressing' }
end
progress_deadline() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 144
def progress_deadline
  if exists?
    @instance_data['spec']['progressDeadlineSeconds']
  else
    @definition['spec']['progressDeadlineSeconds']
  end
end
required_rollout() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 205
def required_rollout
  krane_annotation_value("required-rollout") || DEFAULT_REQUIRED_ROLLOUT
end
rollout_annotation_err_msg() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 152
def rollout_annotation_err_msg
  "'#{krane_annotation_key(REQUIRED_ROLLOUT_ANNOTATION_SUFFIX)}: #{required_rollout}' is invalid. "\
    "Acceptable values: #{REQUIRED_ROLLOUT_TYPES.join(', ')}"
end
rollout_data() click to toggle source
# File lib/krane/kubernetes_resource/deployment.rb, line 132
def rollout_data
  return { "replicas" => 0 } unless exists?
  { "replicas" => 0 }.merge(@instance_data["status"]
    .slice("replicas", "updatedReplicas", "availableReplicas", "unavailableReplicas"))
end