kubernetes-deploy 0.18.0 → 0.18.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -1
  3. data/ISSUE_TEMPLATE.md +25 -0
  4. data/README.md +2 -3
  5. data/lib/kubernetes-deploy.rb +1 -0
  6. data/lib/kubernetes-deploy/deploy_task.rb +8 -5
  7. data/lib/kubernetes-deploy/kubernetes_resource.rb +34 -32
  8. data/lib/kubernetes-deploy/kubernetes_resource/bucket.rb +2 -7
  9. data/lib/kubernetes-deploy/kubernetes_resource/cloudsql.rb +20 -49
  10. data/lib/kubernetes-deploy/kubernetes_resource/config_map.rb +4 -10
  11. data/lib/kubernetes-deploy/kubernetes_resource/cron_job.rb +0 -10
  12. data/lib/kubernetes-deploy/kubernetes_resource/daemon_set.rb +36 -32
  13. data/lib/kubernetes-deploy/kubernetes_resource/deployment.rb +69 -66
  14. data/lib/kubernetes-deploy/kubernetes_resource/elasticsearch.rb +1 -16
  15. data/lib/kubernetes-deploy/kubernetes_resource/ingress.rb +2 -8
  16. data/lib/kubernetes-deploy/kubernetes_resource/memcached.rb +18 -31
  17. data/lib/kubernetes-deploy/kubernetes_resource/persistent_volume_claim.rb +4 -10
  18. data/lib/kubernetes-deploy/kubernetes_resource/pod.rb +28 -31
  19. data/lib/kubernetes-deploy/kubernetes_resource/pod_disruption_budget.rb +2 -8
  20. data/lib/kubernetes-deploy/kubernetes_resource/pod_set_base.rb +10 -12
  21. data/lib/kubernetes-deploy/kubernetes_resource/pod_template.rb +2 -8
  22. data/lib/kubernetes-deploy/kubernetes_resource/redis.rb +19 -48
  23. data/lib/kubernetes-deploy/kubernetes_resource/replica_set.rb +33 -35
  24. data/lib/kubernetes-deploy/kubernetes_resource/resource_quota.rb +3 -14
  25. data/lib/kubernetes-deploy/kubernetes_resource/service.rb +20 -34
  26. data/lib/kubernetes-deploy/kubernetes_resource/service_account.rb +2 -8
  27. data/lib/kubernetes-deploy/kubernetes_resource/stateful_set.rb +37 -31
  28. data/lib/kubernetes-deploy/kubernetes_resource/statefulservice.rb +1 -16
  29. data/lib/kubernetes-deploy/kubernetes_resource/topic.rb +1 -16
  30. data/lib/kubernetes-deploy/resource_watcher.rb +6 -3
  31. data/lib/kubernetes-deploy/restart_task.rb +3 -1
  32. data/lib/kubernetes-deploy/sync_mediator.rb +66 -0
  33. data/lib/kubernetes-deploy/version.rb +1 -1
  34. metadata +4 -3
  35. data/lib/kubernetes-deploy/kubernetes_resource/bugsnag.rb +0 -33
@@ -17,33 +17,29 @@ module KubernetesDeploy
17
17
  super(namespace: namespace, context: context, definition: definition, logger: logger)
18
18
  end
19
19
 
20
- def sync(pod_data = nil)
21
- if pod_data.blank?
22
- raw_json, _err, st = kubectl.run("get", type, @name, "-a", "--output=json")
23
- pod_data = JSON.parse(raw_json) if st.success?
24
- raise_predates_deploy_error if pod_data.present? && unmanaged? && !deploy_started?
25
- end
26
-
27
- if pod_data.present?
28
- @found = true
29
- @phase = @status = pod_data["status"]["phase"]
30
- @status += " (Reason: #{pod_data['status']['reason']})" if pod_data['status']['reason'].present?
31
- @ready = ready?(pod_data["status"])
32
- update_container_statuses(pod_data["status"])
20
+ def sync(mediator)
21
+ super
22
+ raise_predates_deploy_error if exists? && unmanaged? && !deploy_started?
23
+
24
+ if exists?
25
+ update_container_statuses(@instance_data["status"])
33
26
  else # reset
34
- @found = @ready = false
35
- @status = @phase = 'Unknown'
36
27
  @containers.each(&:reset_status)
37
28
  end
38
29
 
39
- display_logs if unmanaged? && deploy_succeeded?
30
+ display_logs(mediator) if unmanaged? && deploy_succeeded?
31
+ end
32
+
33
+ def status
34
+ return phase if @instance_data.dig('status', 'reason').blank?
35
+ "#{phase} (Reason: #{@instance_data['status']['reason']})"
40
36
  end
41
37
 
42
38
  def deploy_succeeded?
43
39
  if unmanaged?
44
- @phase == "Succeeded"
40
+ phase == "Succeeded"
45
41
  else
46
- @phase == "Running" && @ready
42
+ phase == "Running" && ready?
47
43
  end
48
44
  end
49
45
 
@@ -51,10 +47,6 @@ module KubernetesDeploy
51
47
  failure_message.present?
52
48
  end
53
49
 
54
- def exists?
55
- @found
56
- end
57
-
58
50
  def timeout_message
59
51
  return STANDARD_TIMEOUT_MESSAGE unless readiness_probe_failure?
60
52
  probe_failure_msgs = @containers.map(&:readiness_fail_reason).compact
@@ -63,8 +55,8 @@ module KubernetesDeploy
63
55
  end
64
56
 
65
57
  def failure_message
66
- if @phase == FAILED_PHASE_NAME
67
- phase_problem = "Pod status: #{@status}. "
58
+ if phase == FAILED_PHASE_NAME
59
+ phase_problem = "Pod status: #{status}. "
68
60
  end
69
61
 
70
62
  doomed_containers = @containers.select(&:doomed?)
@@ -87,7 +79,7 @@ module KubernetesDeploy
87
79
  # "app" => ["array of log lines", "received from app container"],
88
80
  # "nginx" => ["array of log lines", "received from nginx container"]
89
81
  # }
90
- def fetch_logs
82
+ def fetch_logs(kubectl)
91
83
  return {} unless exists? && @containers.present?
92
84
  @containers.each_with_object({}) do |container, container_logs|
93
85
  cmd = [
@@ -97,20 +89,25 @@ module KubernetesDeploy
97
89
  "--since-time=#{@deploy_started_at.to_datetime.rfc3339}",
98
90
  ]
99
91
  cmd << "--tail=#{LOG_LINE_COUNT}" unless unmanaged?
100
- out, _err, _st = kubectl.run(*cmd)
92
+ out, _err, _st = kubectl.run(*cmd, log_failure: false)
101
93
  container_logs[container.name] = out.split("\n")
102
94
  end
103
95
  end
104
96
 
105
97
  private
106
98
 
99
+ def phase
100
+ @instance_data.dig("status", "phase") || "Unknown"
101
+ end
102
+
107
103
  def readiness_probe_failure?
108
- return false if @ready || unmanaged?
109
- return false if @phase != "Running"
104
+ return false if ready? || unmanaged?
105
+ return false if phase != "Running"
110
106
  @containers.any?(&:readiness_fail_reason)
111
107
  end
112
108
 
113
- def ready?(status_data)
109
+ def ready?
110
+ return false unless status_data = @instance_data["status"]
114
111
  ready_condition = status_data.fetch("conditions", []).find { |condition| condition["type"] == "Ready" }
115
112
  ready_condition.present? && (ready_condition["status"] == "True")
116
113
  end
@@ -131,9 +128,9 @@ module KubernetesDeploy
131
128
  @parent.blank?
132
129
  end
133
130
 
134
- def display_logs
131
+ def display_logs(mediator)
135
132
  return if @already_displayed
136
- container_logs = fetch_logs
133
+ container_logs = fetch_logs(mediator.kubectl)
137
134
 
138
135
  if container_logs.empty?
139
136
  @logger.warn("No logs found for pod #{id}")
@@ -3,10 +3,8 @@ module KubernetesDeploy
3
3
  class PodDisruptionBudget < KubernetesResource
4
4
  TIMEOUT = 10.seconds
5
5
 
6
- def sync
7
- _, _err, st = kubectl.run("get", type, @name)
8
- @found = st.success?
9
- @status = @found ? "Available" : "Unknown"
6
+ def status
7
+ exists? ? "Available" : "Unknown"
10
8
  end
11
9
 
12
10
  def deploy_succeeded?
@@ -21,9 +19,5 @@ module KubernetesDeploy
21
19
  def timeout_message
22
20
  UNUSUAL_FAILURE_MESSAGE
23
21
  end
24
-
25
- def exists?
26
- @found
27
- end
28
22
  end
29
23
  end
@@ -9,14 +9,14 @@ module KubernetesDeploy
9
9
  pods.map(&:timeout_message).compact.uniq.join("\n")
10
10
  end
11
11
 
12
- def fetch_events
12
+ def fetch_events(kubectl)
13
13
  own_events = super
14
14
  return own_events unless pods.present?
15
15
  most_useful_pod = pods.find(&:deploy_failed?) || pods.find(&:deploy_timed_out?) || pods.first
16
- own_events.merge(most_useful_pod.fetch_events)
16
+ own_events.merge(most_useful_pod.fetch_events(kubectl))
17
17
  end
18
18
 
19
- def fetch_logs
19
+ def fetch_logs(kubectl)
20
20
  return {} unless pods.present? # the kubectl command times out if no pods exist
21
21
  container_names.each_with_object({}) do |container_name, container_logs|
22
22
  out, _err, _st = kubectl.run(
@@ -24,7 +24,8 @@ module KubernetesDeploy
24
24
  id,
25
25
  "--container=#{container_name}",
26
26
  "--since-time=#{@deploy_started_at.to_datetime.rfc3339}",
27
- "--tail=#{LOG_LINE_COUNT}"
27
+ "--tail=#{LOG_LINE_COUNT}",
28
+ log_failure: false
28
29
  )
29
30
  container_logs[container_name] = out.split("\n")
30
31
  end
@@ -36,7 +37,7 @@ module KubernetesDeploy
36
37
  raise NotImplementedError, "Subclasses must define a `pods` accessor"
37
38
  end
38
39
 
39
- def parent_of_pod?(_, _)
40
+ def parent_of_pod?(_)
40
41
  raise NotImplementedError, "Subclasses must define a `parent_of_pod?` method"
41
42
  end
42
43
 
@@ -46,14 +47,11 @@ module KubernetesDeploy
46
47
  regular_containers + init_containers
47
48
  end
48
49
 
49
- def find_pods(pod_controller_data)
50
- label_string = pod_controller_data["spec"]["selector"]["matchLabels"].map { |k, v| "#{k}=#{v}" }.join(",")
51
- raw_json, _err, st = kubectl.run("get", "pods", "-a", "--output=json", "--selector=#{label_string}")
52
- return [] unless st.success?
50
+ def find_pods(mediator)
51
+ all_pods = mediator.get_all(Pod.kind, @instance_data["spec"]["selector"]["matchLabels"])
53
52
 
54
- all_pods = JSON.parse(raw_json)["items"]
55
53
  all_pods.each_with_object([]) do |pod_data, relevant_pods|
56
- next unless parent_of_pod?(pod_controller_data, pod_data)
54
+ next unless parent_of_pod?(pod_data)
57
55
  pod = Pod.new(
58
56
  namespace: namespace,
59
57
  context: context,
@@ -62,7 +60,7 @@ module KubernetesDeploy
62
60
  parent: "#{name.capitalize} #{type}",
63
61
  deploy_started_at: @deploy_started_at
64
62
  )
65
- pod.sync(pod_data)
63
+ pod.sync(mediator)
66
64
  relevant_pods << pod
67
65
  end
68
66
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  module KubernetesDeploy
3
3
  class PodTemplate < KubernetesResource
4
- def sync
5
- _, _err, st = kubectl.run("get", type, @name)
6
- @status = st.success? ? "Available" : "Unknown"
7
- @found = st.success?
4
+ def status
5
+ exists? ? "Available" : "Unknown"
8
6
  end
9
7
 
10
8
  def deploy_succeeded?
@@ -18,9 +16,5 @@ module KubernetesDeploy
18
16
  def timeout_message
19
17
  UNUSUAL_FAILURE_MESSAGE
20
18
  end
21
-
22
- def exists?
23
- @found
24
- end
25
19
  end
26
20
  end
@@ -4,75 +4,46 @@ module KubernetesDeploy
4
4
  TIMEOUT = 5.minutes
5
5
  UUID_ANNOTATION = "redis.stable.shopify.io/owner_uid"
6
6
 
7
- def sync
8
- _, _err, st = kubectl.run("get", type, @name)
9
- @found = st.success?
10
- @deployment_exists = redis_deployment_exists?
11
- @service_exists = redis_service_exists?
7
+ SYNC_DEPENDENCIES = %w(Deployment Service)
8
+ def sync(mediator)
9
+ super
10
+ @deployment = mediator.get_instance(Deployment.kind, "redis-#{redis_resource_uuid}")
11
+ @service = mediator.get_instance(Service.kind, "redis-#{redis_resource_uuid}")
12
+ end
12
13
 
13
- @status = if @deployment_exists && @service_exists
14
- "Provisioned"
15
- else
16
- "Unknown"
17
- end
14
+ def status
15
+ deploy_succeeded? ? "Provisioned" : "Unknown"
18
16
  end
19
17
 
20
18
  def deploy_succeeded?
21
- @deployment_exists && @service_exists
19
+ deployment_ready? && service_ready?
22
20
  end
23
21
 
24
22
  def deploy_failed?
25
23
  false
26
24
  end
27
25
 
28
- def exists?
29
- @found
30
- end
31
-
32
26
  def deploy_method
33
27
  :replace
34
28
  end
35
29
 
36
30
  private
37
31
 
38
- def redis_deployment_exists?
39
- deployment, _err, st = kubectl.run("get", "deployments", "redis-#{redis_resource_uuid}", "-o=json")
40
-
41
- if st.success?
42
- parsed = JSON.parse(deployment)
43
-
44
- if parsed.fetch("status", {}).fetch("availableReplicas", -1) == parsed.fetch("status", {}).fetch("replicas", 0)
45
- # all redis pods are running
46
- return true
47
- end
48
- end
49
-
50
- false
32
+ def deployment_ready?
33
+ return false unless status = @deployment["status"]
34
+ # all redis pods are running
35
+ status.fetch("availableReplicas", -1) == status.fetch("replicas", 0)
51
36
  end
52
37
 
53
- def redis_service_exists?
54
- service, _err, st = kubectl.run("get", "services", "redis-#{redis_resource_uuid}", "-o=json")
55
-
56
- if st.success?
57
- parsed = JSON.parse(service)
58
-
59
- if parsed.dig("spec", "clusterIP").present?
60
- return true
61
- end
62
- end
63
-
64
- false
38
+ def service_ready?
39
+ return false unless @service.present?
40
+ # the service has an assigned cluster IP and is therefore functioning
41
+ @service.dig("spec", "clusterIP").present?
65
42
  end
66
43
 
67
44
  def redis_resource_uuid
68
- return @redis_resource_uuid if defined?(@redis_resource_uuid) && @redis_resource_uuid
69
-
70
- redis, _err, st = kubectl.run("get", "redises", @name, "-o=json")
71
- if st.success?
72
- parsed = JSON.parse(redis)
73
-
74
- @redis_resource_uuid = parsed.dig("metadata", "uid")
75
- end
45
+ return unless @instance_data.present?
46
+ @instance_data.dig("metadata", "uid")
76
47
  end
77
48
  end
78
49
  end
@@ -3,64 +3,62 @@ require 'kubernetes-deploy/kubernetes_resource/pod_set_base'
3
3
  module KubernetesDeploy
4
4
  class ReplicaSet < PodSetBase
5
5
  TIMEOUT = 5.minutes
6
- attr_reader :desired_replicas, :ready_replicas, :available_replicas, :pods
6
+ attr_reader :pods
7
7
 
8
8
  def initialize(namespace:, context:, definition:, logger:, parent: nil, deploy_started_at: nil)
9
9
  @parent = parent
10
10
  @deploy_started_at = deploy_started_at
11
- @rollout_data = { "replicas" => 0 }
12
- @desired_replicas = -1
13
- @ready_replicas = -1
14
- @available_replicas = -1
15
11
  @pods = []
16
12
  super(namespace: namespace, context: context, definition: definition, logger: logger)
17
13
  end
18
14
 
19
- def sync(rs_data = nil)
20
- if rs_data.blank?
21
- raw_json, _err, st = kubectl.run("get", type, @name, "--output=json")
22
- rs_data = JSON.parse(raw_json) if st.success?
23
- end
15
+ SYNC_DEPENDENCIES = %w(Pod)
16
+ def sync(mediator)
17
+ super
18
+ @pods = exists? ? find_pods(mediator) : []
19
+ end
24
20
 
25
- if rs_data.present?
26
- @found = true
27
- @desired_replicas = rs_data["spec"]["replicas"].to_i
28
- @rollout_data = { "replicas" => 0 }.merge(
29
- rs_data["status"].slice("replicas", "availableReplicas", "readyReplicas")
30
- )
31
- @ready_replicas = @rollout_data['readyReplicas'].to_i
32
- @available_replicas = @rollout_data["availableReplicas"].to_i
33
- @status = @rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
34
- @pods = find_pods(rs_data)
35
- else # reset
36
- @found = false
37
- @rollout_data = { "replicas" => 0 }
38
- @status = nil
39
- @pods = []
40
- @desired_replicas = -1
41
- @ready_replicas = -1
42
- @available_replicas = -1
43
- end
21
+ def status
22
+ return super unless rollout_data.present?
23
+ rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
44
24
  end
45
25
 
46
26
  def deploy_succeeded?
47
- @desired_replicas == @rollout_data["availableReplicas"].to_i &&
48
- @desired_replicas == @rollout_data["readyReplicas"].to_i
27
+ desired_replicas == rollout_data["availableReplicas"].to_i &&
28
+ desired_replicas == rollout_data["readyReplicas"].to_i
49
29
  end
50
30
 
51
31
  def deploy_failed?
52
32
  pods.present? && pods.all?(&:deploy_failed?)
53
33
  end
54
34
 
55
- def exists?
56
- @found
35
+ def desired_replicas
36
+ return -1 unless exists?
37
+ @instance_data["spec"]["replicas"].to_i
38
+ end
39
+
40
+ def ready_replicas
41
+ return -1 unless exists?
42
+ rollout_data['readyReplicas'].to_i
43
+ end
44
+
45
+ def available_replicas
46
+ return -1 unless exists?
47
+ rollout_data["availableReplicas"].to_i
57
48
  end
58
49
 
59
50
  private
60
51
 
61
- def parent_of_pod?(set_data, pod_data)
52
+ def rollout_data
53
+ return { "replicas" => 0 } unless exists?
54
+ { "replicas" => 0 }.merge(
55
+ @instance_data["status"].slice("replicas", "availableReplicas", "readyReplicas")
56
+ )
57
+ end
58
+
59
+ def parent_of_pod?(pod_data)
62
60
  return false unless pod_data.dig("metadata", "ownerReferences")
63
- pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == set_data["metadata"]["uid"] }
61
+ pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] }
64
62
  end
65
63
 
66
64
  def unmanaged?
@@ -3,19 +3,12 @@ module KubernetesDeploy
3
3
  class ResourceQuota < KubernetesResource
4
4
  TIMEOUT = 30.seconds
5
5
 
6
- def sync
7
- raw_json, _err, st = kubectl.run("get", type, @name, "--output=json")
8
- @status = st.success? ? "Available" : "Unknown"
9
- @found = st.success?
10
- @rollout_data = if @found
11
- JSON.parse(raw_json)
12
- else
13
- {}
14
- end
6
+ def status
7
+ exists? ? "In effect" : "Unknown"
15
8
  end
16
9
 
17
10
  def deploy_succeeded?
18
- @rollout_data.dig("spec", "hard") == @rollout_data.dig("status", "hard")
11
+ @instance_data.dig("spec", "hard") == @instance_data.dig("status", "hard")
19
12
  end
20
13
 
21
14
  def deploy_failed?
@@ -25,9 +18,5 @@ module KubernetesDeploy
25
18
  def timeout_message
26
19
  UNUSUAL_FAILURE_MESSAGE
27
20
  end
28
-
29
- def exists?
30
- @found
31
- end
32
21
  end
33
22
  end