Class: KubernetesDeploy::Deployment

Inherits:
KubernetesResource show all
Defined in:
lib/kubernetes-deploy/kubernetes_resource/deployment.rb

Constant Summary collapse

TIMEOUT =
7.minutes
REQUIRED_ROLLOUT_ANNOTATION =
'kubernetes-deploy.shopify.io/required-rollout'
REQUIRED_ROLLOUT_TYPES =
%w(maxUnavailable full none).freeze
DEFAULT_REQUIRED_ROLLOUT =
'full'

Constants inherited from KubernetesResource

KubernetesResource::DEBUG_RESOURCE_NOT_FOUND_MESSAGE, KubernetesResource::DISABLED_EVENT_INFO_MESSAGE, KubernetesResource::DISABLED_LOG_INFO_MESSAGE, KubernetesResource::DISABLE_FETCHING_EVENT_INFO, KubernetesResource::DISABLE_FETCHING_LOG_INFO, KubernetesResource::LOG_LINE_COUNT, KubernetesResource::STANDARD_TIMEOUT_MESSAGE, KubernetesResource::TIMEOUT_OVERRIDE_ANNOTATION, KubernetesResource::UNUSUAL_FAILURE_MESSAGE

Instance Attribute Summary

Attributes inherited from KubernetesResource

#context, #deploy_started_at, #name, #namespace, #type

Instance Method Summary collapse

Methods inherited from KubernetesResource

build, #debug_message, #deploy_method, #deploy_started?, #file_path, #id, #initialize, #kubectl, #pretty_status, #report_status_to_statsd, #status, #sync_debug_info, timeout, #timeout, #timeout_override, #validation_error_msg, #validation_failed?

Constructor Details

This class inherits a constructor from KubernetesDeploy::KubernetesResource

Instance Method Details

#deploy_failed?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 69

def deploy_failed?
  @latest_rs&.deploy_failed?
end

#deploy_succeeded?Boolean

Returns:

  • (Boolean)


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 48

def deploy_succeeded?
  return false unless @latest_rs.present?

  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?Boolean

Returns:

  • (Boolean)


92
93
94
95
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 92

def deploy_timed_out?
  # Do not use the hard timeout if progress deadline is set
  @progress_condition.present? ? deploy_failing_to_progress? : super
end

#exists?Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 97

def exists?
  @found
end

#failure_messageObject



73
74
75
76
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 73

def failure_message
  return unless @latest_rs.present?
  "Latest ReplicaSet: #{@latest_rs.name}\n\n#{@latest_rs.failure_message}"
end

#fetch_eventsObject



37
38
39
40
41
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 37

def fetch_events
  own_events = super
  return own_events unless @latest_rs.present?
  own_events.merge(@latest_rs.fetch_events)
end

#fetch_logsObject



43
44
45
46
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 43

def fetch_logs
  return {} unless @latest_rs.present?
  @latest_rs.fetch_logs
end

#pretty_timeout_typeObject



88
89
90
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 88

def pretty_timeout_type
  @progress_deadline.present? ? "progress deadline: #{@progress_deadline}s" : super
end

#syncObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 9

def sync
  raw_json, _err, st = kubectl.run("get", type, @name, "--output=json")
  @found = st.success?

  if @found
    deployment_data = JSON.parse(raw_json)
    @desired_replicas = deployment_data["spec"]["replicas"].to_i
    @latest_rs = find_latest_rs(deployment_data)

    @rollout_data = { "replicas" => 0 }.merge(deployment_data["status"]
      .slice("replicas", "updatedReplicas", "availableReplicas", "unavailableReplicas"))
    @status = @rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")

    conditions = deployment_data.fetch("status", {}).fetch("conditions", [])
    @progress_condition = conditions.find { |condition| condition['type'] == 'Progressing' }
    @progress_deadline = deployment_data['spec']['progressDeadlineSeconds']
    @max_unavailable = deployment_data.dig('spec', 'strategy', 'rollingUpdate', 'maxUnavailable')
  else # reset
    @latest_rs = nil
    @rollout_data = { "replicas" => 0 }
    @status = nil
    @progress_condition = nil
    @progress_deadline = @definition['spec']['progressDeadlineSeconds']
    @desired_replicas = -1
    @max_unavailable = @definition.dig('spec', 'strategy', 'rollingUpdate', 'maxUnavailable')
  end
end

#timeout_messageObject



78
79
80
81
82
83
84
85
86
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 78

def timeout_message
  reason_msg = if @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_definitionObject



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/kubernetes-deploy/kubernetes_resource/deployment.rb', line 101

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.downcase != 'rollingupdate'
    @validation_errors << "'#{REQUIRED_ROLLOUT_ANNOTATION}: #{required_rollout}' is incompatible "\
    "with strategy '#{strategy}'"
  end

  @validation_errors.empty?
end