kubernetes-deploy 0.12.12 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1824eeb996c6d82344af3296b3000706ea6f3516
4
- data.tar.gz: bd6786d3454f52ac5ac8edf332c62a3a5f7f7164
3
+ metadata.gz: 6f9ccce3d57089a13afd20e62c5bba0b49c08b92
4
+ data.tar.gz: 58f63b9dbda402ec6cee2833ec0beb209aa3d51c
5
5
  SHA512:
6
- metadata.gz: 07442fae42d0982d81f9d48a684afb59330124a62680ccb00e9b51b93422f60340e8a48b799f43b7b048dcb084f5d3a8daba93229818e00e9d71f7bafb765731
7
- data.tar.gz: f3acaaa7d676ee0c286d800232b96f6232e04fbb2ee2187a31156f217008705a7b87d6152a030a6d524e548905d3dd7bb93d012232dc375137c1e2b0d27936a2
6
+ metadata.gz: 7969d60b7979ef48b82cb717a22a153203ce495bd8c94c6c8fb1a0c711ed229b7bb32de359b7f71d0b812c64c53caf7afaafab362295bcf8ace6bc090649ef7b
7
+ data.tar.gz: 214dfbc251bcb0a121ec49ca8e37a803793e8f6aa7bcc1fdac8b5069d149abbdd0154b24f58324e5c9eb3c4cb6e43b705946862db5963b523887285aeb5d31ad
@@ -1,3 +1,13 @@
1
+ ### 0.13.0
2
+ *Features*
3
+ - Added support for StatefulSets for kubernetes 1.7+ using RollingUpdate
4
+
5
+ *Bug Fixes*
6
+ - Explicitly require the minimum rest-client version required by kubeclient ([#202](https://github.com/Shopify/kubernetes-deploy/pull/202))
7
+
8
+ *Enhancements*
9
+ - Begin official support for Kubernetes v1.8 ([#198](https://github.com/Shopify/kubernetes-deploy/pull/198), [#200](https://github.com/Shopify/kubernetes-deploy/pull/200))
10
+
1
11
  ### 0.12.12
2
12
  *Bug Fixes*
3
13
  - Fix an issue deploying Shopify's internal custom resources.
data/README.md CHANGED
@@ -298,9 +298,13 @@ If you push your commit and the tag separately, Shipit usually fails with `You n
298
298
 
299
299
  ## CI (External contributors)
300
300
 
301
- Please make sure you run the tests locally before submitting your PR (see [Running the test suite locally](#running-the-test-suite-locally)). After reviewing your PR, a Shopify employee will trigger CI for you from the [Buildkite UI](https://buildkite.com/shopify/kubernetes-deploy-gem) (just specify the branch; SHA is not required).
301
+ Please make sure you run the tests locally before submitting your PR (see [Running the test suite locally](#running-the-test-suite-locally)). After reviewing your PR, a Shopify employee will trigger CI for you.
302
302
 
303
- <img width="464" alt="screen shot 2017-02-21 at 10 55 33" src="https://cloud.githubusercontent.com/assets/522155/23172610/52771a3a-f824-11e6-8c8e-3d59c45e7ff8.png">
303
+ #### Employees: Triggering CI for a contributed PR
304
+
305
+ Go to the [kubernetes-deploy-gem pipeline](https://buildkite.com/shopify/kubernetes-deploy-gem) and click "New Build". Use branch `external_contrib_ci` and the specific sha of the commit you want to build. Add `BUILDKITE_REFSPEC="refs/pull/${PR_NUM}/head"` in the Environment Variables section.
306
+
307
+ <img width="350" alt="build external contrib PR" src="https://screenshot.click/2017-11-07--163728_7ovek-wrpwq.png">
304
308
 
305
309
  # Contributing
306
310
 
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.required_ruby_version = '>= 2.3.0'
26
26
  spec.add_dependency "activesupport", ">= 4.2"
27
27
  spec.add_dependency "kubeclient", "~> 2.4"
28
+ spec.add_dependency "rest-client", ">= 1.7" # Minimum required by kubeclient. Remove when kubeclient releases v3.0.
28
29
  spec.add_dependency "googleauth", ">= 0.5"
29
30
  spec.add_dependency "ejson", "1.0.1"
30
31
  spec.add_dependency "colorize", "~> 0.8"
@@ -35,6 +35,14 @@ module KubernetesDeploy
35
35
  )
36
36
  end
37
37
 
38
+ def build_apps_v1beta1_kubeclient(context)
39
+ _build_kubeclient(
40
+ api_version: "v1beta1",
41
+ context: context,
42
+ endpoint_path: "/apis/apps"
43
+ )
44
+ end
45
+
38
46
  def _build_kubeclient(api_version:, context:, endpoint_path: nil)
39
47
  config = GoogleFriendlyConfig.read(ENV.fetch("KUBECONFIG"))
40
48
  unless config.contexts.include?(context)
@@ -31,5 +31,35 @@ module KubernetesDeploy
31
31
  end
32
32
  [out.chomp, err.chomp, st]
33
33
  end
34
+
35
+ def version_info
36
+ @version_info ||=
37
+ begin
38
+ response, _, status = run("version", use_namespace: false, log_failure: true)
39
+ raise KubectlError, "Could not retrieve kubectl version info" unless status.success?
40
+ extract_version_info_from_kubectl_response(response)
41
+ end
42
+ end
43
+
44
+ def client_version
45
+ version_info[:client]
46
+ end
47
+
48
+ def server_version
49
+ version_info[:server]
50
+ end
51
+
52
+ private
53
+
54
+ def extract_version_info_from_kubectl_response(response)
55
+ info = {}
56
+ response.each_line do |l|
57
+ match = l.match(/^(?<kind>Client|Server).* GitVersion:"v(?<version>\d+\.\d+\.\d+)/)
58
+ if match
59
+ info[match[:kind].downcase.to_sym] = Gem::Version.new(match[:version])
60
+ end
61
+ end
62
+ info
63
+ end
34
64
  end
35
65
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
+ require 'kubernetes-deploy/kubernetes_resource/pod_set_base'
2
3
  module KubernetesDeploy
3
- class DaemonSet < KubernetesResource
4
+ class DaemonSet < PodSetBase
4
5
  TIMEOUT = 5.minutes
6
+ attr_reader :pods
5
7
 
6
8
  def sync
7
9
  raw_json, _err, st = kubectl.run("get", type, @name, "--output=json")
@@ -31,66 +33,24 @@ module KubernetesDeploy
31
33
  end
32
34
 
33
35
  def deploy_failed?
34
- @pods.present? && @pods.any?(&:deploy_failed?)
36
+ pods.present? && pods.any?(&:deploy_failed?)
35
37
  end
36
38
 
37
- def failure_message
38
- @pods.map(&:failure_message).compact.uniq.join("\n")
39
- end
40
-
41
- def timeout_message
42
- @pods.map(&:timeout_message).compact.uniq.join("\n")
43
- end
44
-
45
- def deploy_timed_out?
46
- super || @pods.present? && @pods.any?(&:deploy_timed_out?)
39
+ def fetch_logs
40
+ most_useful_pod = @pods.find(&:deploy_failed?) || @pods.find(&:deploy_timed_out?) || @pods.first
41
+ most_useful_pod.fetch_logs
47
42
  end
48
43
 
49
44
  def exists?
50
45
  @found
51
46
  end
52
47
 
53
- def fetch_events
54
- own_events = super
55
- return own_events unless @pods.present?
56
- most_useful_pod = @pods.find(&:deploy_failed?) || @pods.find(&:deploy_timed_out?) || @pods.first
57
- own_events.merge(most_useful_pod.fetch_events)
58
- end
59
-
60
- def fetch_logs
61
- most_useful_pod = @pods.find(&:deploy_failed?) || @pods.find(&:deploy_timed_out?) || @pods.first
62
- most_useful_pod.fetch_logs
63
- end
64
-
65
48
  private
66
49
 
67
- def find_pods(ds_data)
68
- label_string = ds_data["spec"]["selector"]["matchLabels"].map { |k, v| "#{k}=#{v}" }.join(",")
69
- raw_json, _err, st = kubectl.run("get", "pods", "-a", "--output=json", "--selector=#{label_string}")
70
- return [] unless st.success?
71
-
72
- all_pods = JSON.parse(raw_json)["items"]
73
- template_generation = ds_data["spec"]["templateGeneration"]
74
-
75
- latest_pods = all_pods.find_all do |pod|
76
- next unless owners = pod.dig("metadata", "ownerReferences")
77
- owners.any? { |ref| ref["uid"] == ds_data["metadata"]["uid"] } &&
78
- pod["metadata"]["labels"]["pod-template-generation"].to_i == template_generation.to_i
79
- end
80
- return [] unless latest_pods.present?
81
-
82
- latest_pods.each_with_object([]) do |pod_data, relevant_pods|
83
- pod = Pod.new(
84
- namespace: namespace,
85
- context: context,
86
- definition: pod_data,
87
- logger: @logger,
88
- parent: "#{@name.capitalize} daemon set",
89
- deploy_started: @deploy_started
90
- )
91
- pod.sync(pod_data)
92
- relevant_pods << pod
93
- end
50
+ def parent_of_pod?(set_data, pod_data)
51
+ return false unless pod_data.dig("metadata", "ownerReferences")
52
+ pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == set_data["metadata"]["uid"] } &&
53
+ pod_data["metadata"]["labels"]["pod-template-generation"].to_i == set_data["spec"]["templateGeneration"].to_i
94
54
  end
95
55
  end
96
56
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+ module KubernetesDeploy
3
+ class PodSetBase < KubernetesResource
4
+ def failure_message
5
+ pods.map(&:failure_message).compact.uniq.join("\n")
6
+ end
7
+
8
+ def timeout_message
9
+ pods.map(&:timeout_message).compact.uniq.join("\n")
10
+ end
11
+
12
+ def fetch_events
13
+ own_events = super
14
+ return own_events unless pods.present?
15
+ most_useful_pod = pods.find(&:deploy_failed?) || pods.find(&:deploy_timed_out?) || pods.first
16
+ own_events.merge(most_useful_pod.fetch_events)
17
+ end
18
+
19
+ def fetch_logs
20
+ return {} unless pods.present? # the kubectl command times out if no pods exist
21
+ container_names.each_with_object({}) do |container_name, container_logs|
22
+ out, _err, _st = kubectl.run(
23
+ "logs",
24
+ id,
25
+ "--container=#{container_name}",
26
+ "--since-time=#{@deploy_started.to_datetime.rfc3339}",
27
+ "--tail=#{LOG_LINE_COUNT}"
28
+ )
29
+ container_logs[container_name] = out.split("\n")
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def pods
36
+ raise NotImplementedError, "Subclasses must define a `pods` accessor"
37
+ end
38
+
39
+ def parent_of_pod?(_, _)
40
+ raise NotImplementedError, "Subclasses must define a `parent_of_pod?` method"
41
+ end
42
+
43
+ def container_names
44
+ regular_containers = @definition["spec"]["template"]["spec"]["containers"].map { |c| c["name"] }
45
+ init_containers = @definition["spec"]["template"]["spec"].fetch("initContainers", {}).map { |c| c["name"] }
46
+ regular_containers + init_containers
47
+ end
48
+
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?
53
+
54
+ all_pods = JSON.parse(raw_json)["items"]
55
+ all_pods.each_with_object([]) do |pod_data, relevant_pods|
56
+ next unless parent_of_pod?(pod_controller_data, pod_data)
57
+ pod = Pod.new(
58
+ namespace: namespace,
59
+ context: context,
60
+ definition: pod_data,
61
+ logger: @logger,
62
+ parent: "#{name.capitalize} #{self.class.name}",
63
+ deploy_started: @deploy_started
64
+ )
65
+ pod.sync(pod_data)
66
+ relevant_pods << pod
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
+ require 'kubernetes-deploy/kubernetes_resource/pod_set_base'
2
3
  module KubernetesDeploy
3
- class ReplicaSet < KubernetesResource
4
+ class ReplicaSet < PodSetBase
4
5
  TIMEOUT = 5.minutes
5
- attr_reader :desired_replicas
6
+ attr_reader :desired_replicas, :pods
6
7
 
7
8
  def initialize(namespace:, context:, definition:, logger:, parent: nil, deploy_started: nil)
8
9
  @parent = parent
@@ -39,74 +40,22 @@ module KubernetesDeploy
39
40
  end
40
41
 
41
42
  def deploy_failed?
42
- @pods.present? && @pods.all?(&:deploy_failed?)
43
- end
44
-
45
- def failure_message
46
- @pods.map(&:failure_message).compact.uniq.join("\n")
47
- end
48
-
49
- def timeout_message
50
- @pods.map(&:timeout_message).compact.uniq.join("\n")
43
+ pods.present? && pods.all?(&:deploy_failed?)
51
44
  end
52
45
 
53
46
  def exists?
54
47
  @found
55
48
  end
56
49
 
57
- def fetch_events
58
- own_events = super
59
- return own_events unless @pods.present?
60
- most_useful_pod = @pods.find(&:deploy_failed?) || @pods.find(&:deploy_timed_out?) || @pods.first
61
- own_events.merge(most_useful_pod.fetch_events)
62
- end
50
+ private
63
51
 
64
- def fetch_logs
65
- return {} unless @pods.present? # the kubectl command times out if no pods exist
66
- container_names.each_with_object({}) do |container_name, container_logs|
67
- out, _err, _st = kubectl.run(
68
- "logs",
69
- id,
70
- "--container=#{container_name}",
71
- "--since-time=#{@deploy_started.to_datetime.rfc3339}",
72
- "--tail=#{LOG_LINE_COUNT}"
73
- )
74
- container_logs[container_name] = out.split("\n")
75
- end
52
+ def parent_of_pod?(set_data, pod_data)
53
+ return false unless pod_data.dig("metadata", "ownerReferences")
54
+ pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == set_data["metadata"]["uid"] }
76
55
  end
77
56
 
78
- private
79
-
80
57
  def unmanaged?
81
58
  @parent.blank?
82
59
  end
83
-
84
- def container_names
85
- regular_containers = @definition["spec"]["template"]["spec"]["containers"].map { |c| c["name"] }
86
- init_containers = @definition["spec"]["template"]["spec"].fetch("initContainers", []).map { |c| c["name"] }
87
- regular_containers + init_containers
88
- end
89
-
90
- def find_pods(rs_data)
91
- label_string = rs_data["spec"]["selector"]["matchLabels"].map { |k, v| "#{k}=#{v}" }.join(",")
92
- raw_json, _err, st = kubectl.run("get", "pods", "-a", "--output=json", "--selector=#{label_string}")
93
- return [] unless st.success?
94
-
95
- all_pods = JSON.parse(raw_json)["items"]
96
- all_pods.each_with_object([]) do |pod_data, relevant_pods|
97
- next unless owners = pod_data.dig("metadata", "ownerReferences")
98
- next unless owners.any? { |ref| ref["uid"] == rs_data["metadata"]["uid"] }
99
- pod = Pod.new(
100
- namespace: namespace,
101
- context: context,
102
- definition: pod_data,
103
- logger: @logger,
104
- parent: "#{@name.capitalize} replica set",
105
- deploy_started: @deploy_started
106
- )
107
- pod.sync(pod_data)
108
- relevant_pods << pod
109
- end
110
- end
111
60
  end
112
61
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ require 'kubernetes-deploy/kubernetes_resource/pod_set_base'
3
+ module KubernetesDeploy
4
+ class StatefulSet < PodSetBase
5
+ TIMEOUT = 10.minutes
6
+ ONDELETE = 'OnDelete'
7
+ attr_reader :pods
8
+
9
+ def sync
10
+ raw_json, _err, st = kubectl.run("get", type, @name, "--output=json")
11
+ @found = st.success?
12
+
13
+ if @found
14
+ stateful_data = JSON.parse(raw_json)
15
+ @desired_replicas = stateful_data["spec"]["replicas"].to_i
16
+ @status_data = stateful_data["status"]
17
+ rollout_data = stateful_data["status"].slice("replicas", "readyReplicas", "currentReplicas")
18
+ @update_strategy = if kubectl.server_version < Gem::Version.new("1.7.0")
19
+ ONDELETE
20
+ else
21
+ stateful_data['spec']['updateStrategy']['type']
22
+ end
23
+ @status = rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
24
+ @pods = find_pods(stateful_data)
25
+ else # reset
26
+ @status_data = { 'readyReplicas' => '-1', 'currentReplicas' => '-2' }
27
+ @status = nil
28
+ @pods = []
29
+ end
30
+ end
31
+
32
+ def deploy_succeeded?
33
+ if @update_strategy == ONDELETE
34
+ # Gem cannot monitor update since it doesn't occur until delete
35
+ unless @success_assumption_warning_shown
36
+ @logger.warn("WARNING: Your StatefulSet's updateStrategy is set to OnDelete, "\
37
+ "which means updates will not be applied until its pods are deleted. "\
38
+ "If you are using k8s 1.7+, consider switching to rollingUpdate.")
39
+ @success_assumption_warning_shown = true
40
+ end
41
+ true
42
+ else
43
+ @status_data['currentRevision'] == @status_data['updateRevision'] &&
44
+ @desired_replicas == @status_data['readyReplicas'].to_i &&
45
+ @desired_replicas == @status_data['currentReplicas'].to_i
46
+ end
47
+ end
48
+
49
+ def deploy_failed?
50
+ return false if @update_strategy == ONDELETE
51
+ pods.present? && pods.any?(&:deploy_failed?)
52
+ end
53
+
54
+ def exists?
55
+ @found
56
+ end
57
+
58
+ private
59
+
60
+ def parent_of_pod?(set_data, pod_data)
61
+ return false unless pod_data.dig("metadata", "ownerReferences")
62
+ pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == set_data["metadata"]["uid"] } &&
63
+ set_data["status"]["currentRevision"] == pod_data["metadata"]["labels"]["controller-revision-hash"]
64
+ end
65
+ end
66
+ end
@@ -27,6 +27,7 @@ require 'kubernetes-deploy/kubernetes_resource'
27
27
  statefulservice
28
28
  topic
29
29
  bucket
30
+ stateful_set
30
31
  ).each do |subresource|
31
32
  require "kubernetes-deploy/kubernetes_resource/#{subresource}"
32
33
  end
@@ -415,10 +416,12 @@ module KubernetesDeploy
415
416
  def confirm_cluster_reachable
416
417
  success = false
417
418
  with_retries(2) do
418
- _, _, st = kubectl.run("version", use_namespace: false, log_failure: true)
419
- success = st.success?
419
+ begin
420
+ success = kubectl.version_info
421
+ rescue KubectlError
422
+ success = false
423
+ end
420
424
  end
421
-
422
425
  raise FatalDeploymentError, "Failed to reach server for #{@context}" unless success
423
426
  end
424
427
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module KubernetesDeploy
3
- VERSION = "0.12.12"
3
+ VERSION = "0.13.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kubernetes-deploy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.12
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katrina Verey
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2017-11-02 00:00:00.000000000 Z
12
+ date: 2017-11-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '2.4'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rest-client
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '1.7'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '1.7'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: googleauth
44
58
  requirement: !ruby/object:Gem::Requirement
@@ -229,12 +243,14 @@ files:
229
243
  - lib/kubernetes-deploy/kubernetes_resource/persistent_volume_claim.rb
230
244
  - lib/kubernetes-deploy/kubernetes_resource/pod.rb
231
245
  - lib/kubernetes-deploy/kubernetes_resource/pod_disruption_budget.rb
246
+ - lib/kubernetes-deploy/kubernetes_resource/pod_set_base.rb
232
247
  - lib/kubernetes-deploy/kubernetes_resource/pod_template.rb
233
248
  - lib/kubernetes-deploy/kubernetes_resource/redis.rb
234
249
  - lib/kubernetes-deploy/kubernetes_resource/replica_set.rb
235
250
  - lib/kubernetes-deploy/kubernetes_resource/resource_quota.rb
236
251
  - lib/kubernetes-deploy/kubernetes_resource/service.rb
237
252
  - lib/kubernetes-deploy/kubernetes_resource/service_account.rb
253
+ - lib/kubernetes-deploy/kubernetes_resource/stateful_set.rb
238
254
  - lib/kubernetes-deploy/kubernetes_resource/statefulservice.rb
239
255
  - lib/kubernetes-deploy/kubernetes_resource/topic.rb
240
256
  - lib/kubernetes-deploy/resource_watcher.rb