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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f0ae7839357ce28f77a544c170be1e439515205c
4
- data.tar.gz: 945483435a69d88450f060d2a544cb046f93a17d
3
+ metadata.gz: 820602ac9a434949fb86a0f1cb85996962d4ecb2
4
+ data.tar.gz: 492b43ccf6a0683b93cc3b187c6711e9bdb176b1
5
5
  SHA512:
6
- metadata.gz: cc67d2dbc4ea9563f84bd11ae7c991144a0153223f21d07d14e6ad632396167d978c7a9a881386dc1a9a55756a5e06e4e9e60a1b1534b20b4705b4f38fb4d0a4
7
- data.tar.gz: df05433e91bc432c9e7412c4806dbdfe4d5ba494763a0e4dfaf6ea6231ed1b1596c6e9dc4157e40f8dcf8857db828cbec9b098244964d147bcc84a2ce74c254e
6
+ metadata.gz: 23706dd66b3844c4b6fece858621bf4b9cea5c0c65568b972cb6afdfab64a1f8d45957c7095a3ca8af96f9c488f383f2f1fd69d99d5affdbf9545d9c0c7ac948
7
+ data.tar.gz: 64bf0d15a51c97be05e80a3b64b0e3f985f0f6a0bd137fbd1e6353b13bec404e6dd150a66f34c15f0f24cfee66a1838efba2d6260bf6ece9bf5c4c0903e55e12
@@ -1,4 +1,8 @@
1
- ### 0.17.1
1
+ ### 0.18.1
2
+ *Enhancements*
3
+ - Change the way the resource watcher fetches resources to make it more efficient for large deploys. Deploys with hundreds of resources are expected to see a measurable performance improvement from this change. ([#251](https://github.com/Shopify/kubernetes-deploy/pull/251))
4
+
5
+ ### 0.18.0
2
6
  *Features*
3
7
  - kubernetes-restart and kubernetes-deploy use exit code 70 when a
4
8
  deploy fails due to one or more resources failing to deploy in time.
@@ -0,0 +1,25 @@
1
+ <!-- Please erase any parts of this template not applicable to your issue. -->
2
+
3
+ # Bug report
4
+
5
+ [Description of the bug]
6
+
7
+ **Expected behavior:** [What you expected to happen]
8
+
9
+ **Actual behavior:** [What actually happened]
10
+
11
+ **Version(s) affected:** [run `kubernetes-deploy --version`]
12
+
13
+ ### Steps to Reproduce
14
+
15
+ 1. [First Step]
16
+ 2. [Second Step]
17
+ 3. [and so on...]
18
+
19
+ # Feature request
20
+
21
+ - [ ] If the maintainers agree with the feature as described here, I intend to submit a Pull Request myself.<sup>1</sup>
22
+
23
+ **Proposal:** [provide details on the behaviour you'd like to see and why it would be useful]
24
+
25
+ <sup><small>1</small></sup> <sub>This is the quickest way to get a new feature! We reserve the right to close feature requests, even ones we like, if the proposer does not intend to contribute to the feature and it doesn't fit in our current roadmap.</sub>
data/README.md CHANGED
@@ -121,7 +121,7 @@ All templates must be YAML formatted. You can also use ERB. The following local
121
121
  * `current_sha`: The value of `$REVISION`
122
122
  * `deployment_id`: A randomly generated identifier for the deploy. Useful for creating unique names for task-runner pods (e.g. a pod that runs rails migrations at the beginning of deploys).
123
123
 
124
- You can add additional variables using the `--bindings=BINDINGS` option. For example, `kubernetes-deploy my-app cluster1 --bindings=color=blue,size=large` will expose `color` and `size` in your templates.
124
+ You can add additional variables using the `--bindings=BINDINGS` option which can be formated as comma separated or as JSON. For example, `kubernetes-deploy my-app cluster1 --bindings=color=blue,size=large` or `kubernetes-deploy my-app cluster1 --bindings='{"color":"blue","size":"large"}'` will expose `color` and `size` in your templates. Complex JSON data will be converted to a Hash for use in templates.
125
125
 
126
126
  #### Using partials
127
127
 
@@ -427,10 +427,9 @@ This gem uses subclasses of `KubernetesResource` to implement custom success/fai
427
427
 
428
428
  1. Create a the file for your type in `lib/kubernetes-deploy/kubernetes_resource/`
429
429
  2. Create a new class that inherits from `KubernetesResource`. Minimally, it should implement the following methods:
430
- * `sync` -- gather/update the data you'll need to determine `deploy_succeeded?` and `deploy_failed?`
430
+ * `sync` -- Gather the data you'll need to determine `deploy_succeeded?` and `deploy_failed?`. The superclass's implementation fetches the corresponding resource, parses it and stores it in `@instance_data`. You can define your own implementation if you need something else.
431
431
  * `deploy_succeeded?`
432
432
  * `deploy_failed?`
433
- * `exists?`
434
433
  3. Adjust the `TIMEOUT` constant to an appropriate value for this type.
435
434
  4. Add the a basic example of the type to the hello-cloud [fixture set](https://github.com/Shopify/kubernetes-deploy/tree/master/test/fixtures/hello-cloud) and appropriate assertions to `#assert_all_up` in [`hello_cloud.rb`](https://github.com/Shopify/kubernetes-deploy/blob/master/test/helpers/fixture_sets/hello_cloud.rb). This will get you coverage in several existing tests, such as `test_full_hello_cloud_set_deploy_succeeds`.
436
435
  5. Add tests for any edge cases you foresee.
@@ -19,6 +19,7 @@ require 'kubernetes-deploy/statsd'
19
19
  require 'kubernetes-deploy/concurrency'
20
20
  require 'kubernetes-deploy/bindings_parser'
21
21
  require 'kubernetes-deploy/duration_parser'
22
+ require 'kubernetes-deploy/sync_mediator'
22
23
 
23
24
  module KubernetesDeploy
24
25
  MIN_KUBE_VERSION = '1.7.0'
@@ -16,7 +16,6 @@ require 'kubernetes-deploy/kubernetes_resource'
16
16
  memcached
17
17
  service
18
18
  pod_template
19
- bugsnag
20
19
  pod_disruption_budget
21
20
  replica_set
22
21
  service_account
@@ -46,7 +45,6 @@ module KubernetesDeploy
46
45
  Cloudsql
47
46
  Redis
48
47
  Memcached
49
- Bugsnag
50
48
  ConfigMap
51
49
  PersistentVolumeClaim
52
50
  ServiceAccount
@@ -103,6 +101,7 @@ module KubernetesDeploy
103
101
  logger: @logger,
104
102
  bindings: bindings,
105
103
  )
104
+ @sync_mediator = SyncMediator.new(namespace: @namespace, context: @context, logger: @logger)
106
105
  end
107
106
 
108
107
  def run(*args)
@@ -124,7 +123,7 @@ module KubernetesDeploy
124
123
  validate_definitions(resources)
125
124
 
126
125
  @logger.phase_heading("Checking initial resource statuses")
127
- KubernetesDeploy::Concurrency.split_across_threads(resources, &:sync)
126
+ @sync_mediator.sync(resources)
128
127
  resources.each { |r| @logger.info(r.pretty_status) }
129
128
 
130
129
  ejson = EjsonSecretProvisioner.new(
@@ -207,6 +206,9 @@ module KubernetesDeploy
207
206
  failed_resources = matching_resources.reject(&:deploy_succeeded?)
208
207
  fail_count = failed_resources.length
209
208
  if fail_count > 0
209
+ KubernetesDeploy::Concurrency.split_across_threads(failed_resources) do |r|
210
+ r.sync_debug_info(@sync_mediator.kubectl)
211
+ end
210
212
  failed_resources.each { |r| @logger.summary.add_paragraph(r.debug_message) }
211
213
  raise FatalDeploymentError, "Failed to deploy #{fail_count} priority #{'resource'.pluralize(fail_count)}"
212
214
  end
@@ -215,7 +217,7 @@ module KubernetesDeploy
215
217
  end
216
218
 
217
219
  def validate_definitions(resources)
218
- KubernetesDeploy::Concurrency.split_across_threads(resources, &:validate_definition)
220
+ KubernetesDeploy::Concurrency.split_across_threads(resources) { |r| r.validate_definition(kubectl) }
219
221
  failed_resources = resources.select(&:validation_failed?)
220
222
  return unless failed_resources.present?
221
223
 
@@ -373,7 +375,8 @@ module KubernetesDeploy
373
375
  apply_all(applyables, prune)
374
376
 
375
377
  if verify
376
- watcher = ResourceWatcher.new(resources, logger: @logger, deploy_started_at: deploy_started_at)
378
+ watcher = ResourceWatcher.new(resources: resources, sync_mediator: @sync_mediator,
379
+ logger: @logger, deploy_started_at: deploy_started_at)
377
380
  watcher.run(record_summary: record_summary)
378
381
  end
379
382
  end
@@ -2,7 +2,6 @@
2
2
  require 'json'
3
3
  require 'open3'
4
4
  require 'shellwords'
5
- require 'kubernetes-deploy/kubectl'
6
5
 
7
6
  module KubernetesDeploy
8
7
  class KubernetesResource
@@ -28,20 +27,26 @@ module KubernetesDeploy
28
27
 
29
28
  TIMEOUT_OVERRIDE_ANNOTATION = "kubernetes-deploy.shopify.io/timeout-override"
30
29
 
31
- def self.build(namespace:, context:, definition:, logger:)
32
- opts = { namespace: namespace, context: context, definition: definition, logger: logger }
33
- if KubernetesDeploy.const_defined?(definition["kind"])
34
- klass = KubernetesDeploy.const_get(definition["kind"])
35
- klass.new(**opts)
36
- else
37
- inst = new(**opts)
38
- inst.type = definition["kind"]
39
- inst
30
+ class << self
31
+ def build(namespace:, context:, definition:, logger:)
32
+ opts = { namespace: namespace, context: context, definition: definition, logger: logger }
33
+ if KubernetesDeploy.const_defined?(definition["kind"])
34
+ klass = KubernetesDeploy.const_get(definition["kind"])
35
+ klass.new(**opts)
36
+ else
37
+ inst = new(**opts)
38
+ inst.type = definition["kind"]
39
+ inst
40
+ end
41
+ end
42
+
43
+ def timeout
44
+ self::TIMEOUT
40
45
  end
41
- end
42
46
 
43
- def self.timeout
44
- self::TIMEOUT
47
+ def kind
48
+ name.demodulize
49
+ end
45
50
  end
46
51
 
47
52
  def timeout
@@ -74,9 +79,10 @@ module KubernetesDeploy
74
79
  @definition = definition
75
80
  @statsd_report_done = false
76
81
  @validation_errors = []
82
+ @instance_data = {}
77
83
  end
78
84
 
79
- def validate_definition
85
+ def validate_definition(kubectl)
80
86
  @validation_errors = []
81
87
  validate_timeout_annotation
82
88
 
@@ -103,7 +109,8 @@ module KubernetesDeploy
103
109
  file.path
104
110
  end
105
111
 
106
- def sync
112
+ def sync(mediator)
113
+ @instance_data = mediator.get_instance(type, name)
107
114
  end
108
115
 
109
116
  def deploy_failed?
@@ -115,7 +122,8 @@ module KubernetesDeploy
115
122
  end
116
123
 
117
124
  def deploy_succeeded?
118
- if deploy_started? && !@success_assumption_warning_shown
125
+ return false unless deploy_started?
126
+ unless @success_assumption_warning_shown
119
127
  @logger.warn("Don't know how to monitor resources of type #{type}. Assuming #{id} deployed successfully.")
120
128
  @success_assumption_warning_shown = true
121
129
  end
@@ -123,15 +131,15 @@ module KubernetesDeploy
123
131
  end
124
132
 
125
133
  def exists?
126
- nil
134
+ @instance_data.present?
127
135
  end
128
136
 
129
137
  def status
130
- @status ||= "Unknown"
138
+ exists? ? "Exists" : "Unknown"
131
139
  end
132
140
 
133
141
  def type
134
- @type || self.class.name.demodulize
142
+ @type || self.class.kind
135
143
  end
136
144
 
137
145
  def deploy_timed_out?
@@ -144,15 +152,13 @@ module KubernetesDeploy
144
152
  :apply
145
153
  end
146
154
 
147
- def sync_debug_info
148
- @events = fetch_events unless ENV[DISABLE_FETCHING_EVENT_INFO]
149
- @logs = fetch_logs if supports_logs? && !ENV[DISABLE_FETCHING_EVENT_INFO]
155
+ def sync_debug_info(kubectl)
156
+ @events = fetch_events(kubectl) unless ENV[DISABLE_FETCHING_EVENT_INFO]
157
+ @logs = fetch_logs(kubectl) if supports_logs? && !ENV[DISABLE_FETCHING_EVENT_INFO]
150
158
  @debug_info_synced = true
151
159
  end
152
160
 
153
161
  def debug_message
154
- sync_debug_info unless @debug_info_synced
155
-
156
162
  helpful_info = []
157
163
  if deploy_failed?
158
164
  helpful_info << ColorizedString.new("#{id}: FAILED").red
@@ -210,9 +216,10 @@ module KubernetesDeploy
210
216
  # "Pulled: Successfully pulled image "hello-world:latest" (1 events)"
211
217
  # ]
212
218
  # }
213
- def fetch_events
219
+ def fetch_events(kubectl)
214
220
  return {} unless exists?
215
- out, _err, st = kubectl.run("get", "events", "--output=go-template=#{Event.go_template_for(type, name)}")
221
+ out, _err, st = kubectl.run("get", "events", "--output=go-template=#{Event.go_template_for(type, name)}",
222
+ log_failure: false)
216
223
  return {} unless st.success?
217
224
 
218
225
  event_collector = Hash.new { |hash, key| hash[key] = [] }
@@ -230,12 +237,7 @@ module KubernetesDeploy
230
237
 
231
238
  def pretty_status
232
239
  padding = " " * [50 - id.length, 1].max
233
- msg = exists? ? status : "not found"
234
- "#{id}#{padding}#{msg}"
235
- end
236
-
237
- def kubectl
238
- @kubectl ||= Kubectl.new(namespace: @namespace, context: @context, logger: @logger, log_failure_by_default: false)
240
+ "#{id}#{padding}#{status}"
239
241
  end
240
242
 
241
243
  def report_status_to_statsd(watch_time)
@@ -1,11 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module KubernetesDeploy
3
3
  class Bucket < KubernetesResource
4
- def sync
5
- _, _err, st = kubectl.run("get", type, @name)
6
- @found = st.success?
7
- end
8
-
9
4
  def deploy_succeeded?
10
5
  return false unless deploy_started?
11
6
 
@@ -16,8 +11,8 @@ module KubernetesDeploy
16
11
  true
17
12
  end
18
13
 
19
- def exists?
20
- @found
14
+ def status
15
+ exists? ? "Available" : "Unknown"
21
16
  end
22
17
 
23
18
  def deploy_failed?
@@ -3,75 +3,46 @@ module KubernetesDeploy
3
3
  class Cloudsql < KubernetesResource
4
4
  TIMEOUT = 10.minutes
5
5
 
6
- def sync
7
- _, _err, st = kubectl.run("get", type, @name)
8
- @found = st.success?
9
- @deployment_exists = cloudsql_proxy_deployment_exists?
10
- @service_exists = mysql_service_exists?
11
- @status = if @deployment_exists && @service_exists
12
- "Provisioned"
13
- else
14
- "Unknown"
15
- end
6
+ SYNC_DEPENDENCIES = %w(Deployment Service)
7
+ def sync(mediator)
8
+ super
9
+ @proxy_deployment = mediator.get_instance(Deployment.kind, "cloudsql-#{cloudsql_resource_uuid}")
10
+ @proxy_service = mediator.get_instance(Service.kind, "cloudsql-#{@name}")
11
+ end
12
+
13
+ def status
14
+ deploy_succeeded? ? "Provisioned" : "Unknown"
16
15
  end
17
16
 
18
17
  def deploy_succeeded?
19
- @service_exists && @deployment_exists
18
+ proxy_deployment_ready? && proxy_service_ready?
20
19
  end
21
20
 
22
21
  def deploy_failed?
23
22
  false
24
23
  end
25
24
 
26
- def exists?
27
- @found
28
- end
29
-
30
25
  def deploy_method
31
26
  :replace
32
27
  end
33
28
 
34
29
  private
35
30
 
36
- def cloudsql_proxy_deployment_exists?
37
- deployment, _err, st = kubectl.run("get", "deployments", "cloudsql-#{cloudsql_resource_uuid}", "-o=json")
38
-
39
- if st.success?
40
- parsed = JSON.parse(deployment)
41
-
42
- if parsed.fetch("status", {}).fetch("availableReplicas", -1) == parsed.fetch("status", {}).fetch("replicas", 0)
43
- # all cloudsql-proxy pods are running
44
- return true
45
- end
46
- end
47
-
48
- false
31
+ def proxy_deployment_ready?
32
+ return false unless status = @proxy_deployment["status"]
33
+ # all cloudsql-proxy pods are running
34
+ status.fetch("availableReplicas", -1) == status.fetch("replicas", 0)
49
35
  end
50
36
 
51
- def mysql_service_exists?
52
- service, _err, st = kubectl.run("get", "services", "cloudsql-#{@name}", "-o=json")
53
-
54
- if st.success?
55
- parsed = JSON.parse(service)
56
-
57
- if parsed.dig("spec", "clusterIP").present?
58
- # the service has an assigned cluster IP and is therefore functioning
59
- return true
60
- end
61
- end
62
-
63
- false
37
+ def proxy_service_ready?
38
+ return false unless @proxy_service.present?
39
+ # the service has an assigned cluster IP and is therefore functioning
40
+ @proxy_service.dig("spec", "clusterIP").present?
64
41
  end
65
42
 
66
43
  def cloudsql_resource_uuid
67
- return @cloudsql_resource_uuid if defined?(@cloudsql_resource_uuid) && @cloudsql_resource_uuid
68
-
69
- cloudsql, _err, st = kubectl.run("get", "cloudsqls", @name, "-o=json")
70
- if st.success?
71
- parsed = JSON.parse(cloudsql)
72
-
73
- @cloudsql_resource_uuid = parsed.dig("metadata", "uid")
74
- end
44
+ return unless @instance_data
45
+ @instance_data.dig("metadata", "uid")
75
46
  end
76
47
  end
77
48
  end
@@ -3,16 +3,14 @@ module KubernetesDeploy
3
3
  class ConfigMap < KubernetesResource
4
4
  TIMEOUT = 30.seconds
5
5
 
6
- def sync
7
- _, _err, st = kubectl.run("get", type, @name)
8
- @status = st.success? ? "Available" : "Unknown"
9
- @found = st.success?
10
- end
11
-
12
6
  def deploy_succeeded?
13
7
  exists?
14
8
  end
15
9
 
10
+ def status
11
+ exists? ? "Available" : "Unknown"
12
+ end
13
+
16
14
  def deploy_failed?
17
15
  false
18
16
  end
@@ -20,9 +18,5 @@ module KubernetesDeploy
20
18
  def timeout_message
21
19
  UNUSUAL_FAILURE_MESSAGE
22
20
  end
23
-
24
- def exists?
25
- @found
26
- end
27
21
  end
28
22
  end
@@ -3,12 +3,6 @@ module KubernetesDeploy
3
3
  class CronJob < KubernetesResource
4
4
  TIMEOUT = 30.seconds
5
5
 
6
- def sync
7
- _, _err, st = kubectl.run("get", type, @name)
8
- @status = st.success? ? "Available" : "Unknown"
9
- @found = st.success?
10
- end
11
-
12
6
  def deploy_succeeded?
13
7
  exists?
14
8
  end
@@ -20,9 +14,5 @@ module KubernetesDeploy
20
14
  def timeout_message
21
15
  UNUSUAL_FAILURE_MESSAGE
22
16
  end
23
-
24
- def exists?
25
- @found
26
- end
27
17
  end
28
18
  end