kubernetes-deploy 0.4.2 → 0.5.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: 847d8896587cd1782e7fcda36d6f73afb9f52989
4
- data.tar.gz: 359192fb71943bd4dd916436b12225c798ae2e70
3
+ metadata.gz: d1e66d28cabf4ab39da9f1418817e5466ea2e73f
4
+ data.tar.gz: 69967786f96afae72c074a49c44763e1e0377d1e
5
5
  SHA512:
6
- metadata.gz: 061ddcbfd5009eac42f43c489b1d6ab471e53eea281666d76919d1de91f4fb8ad20e15b9e8c2d7bb1e2c2dd242640d69926355a0efe941a1556fb70ee32fa867
7
- data.tar.gz: 8f7d8be0baa2d6b265cbffdc269fc8b532cbc27a6825a6eda0f039a3f5355347a2a7d17258bae629e1904ed3451c7e47c48e27fd68f8e5df8fe82f4493e2a275
6
+ metadata.gz: 1aba4d518ff8cb0afad70fd9d5a2710ee6696f02c06fe497907a1ac593fc94191e970b1afabe119032dbf5ad361dd9fc06255d1ca817748e36d9c66b581037e9
7
+ data.tar.gz: d4213a7380ac484572837a8723713ebfb8cc93448cab122e89a80c55d36bd0399397f8d93e8a27b858fae34dcaa6a4783a65d220e1b561079f5c323eb890105d
data/.gitignore CHANGED
@@ -10,3 +10,5 @@
10
10
 
11
11
  # Ignore RuboCop cache
12
12
  .rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml
13
+
14
+ .byebug_history
data/Gemfile CHANGED
@@ -6,3 +6,4 @@ gemspec
6
6
  gem 'pry'
7
7
  gem 'rubocop'
8
8
  gem 'timecop'
9
+ gem 'byebug'
@@ -9,7 +9,18 @@ skip_wait = false
9
9
  template_dir = nil
10
10
  allow_protected_ns = false
11
11
  prune = true
12
+ bindings = {}
13
+
12
14
  ARGV.options do |opts|
15
+ opts.on("--bindings=BINDINGS", Array, "k1=v1,k2=v2") do |binds|
16
+ bindings = binds.each_with_object({}) do |bind, acc|
17
+ k,v = bind.split('=')
18
+ raise "key for value #{v} is blank!" if k.blank?
19
+ raise "value for key #{k} is blank!" if v.blank?
20
+ acc[k] = v
21
+ end
22
+ end
23
+
13
24
  opts.on("--skip-wait") { skip_wait = true }
14
25
  opts.on("--allow-protected-ns") { allow_protected_ns = true }
15
26
  opts.on("--no-prune") { prune = false }
@@ -41,6 +52,7 @@ KubernetesDeploy::Runner.with_friendly_errors do
41
52
  wait_for_completion: !skip_wait,
42
53
  allow_protected_ns: allow_protected_ns,
43
54
  prune: prune,
55
+ bindings: bindings
44
56
  )
45
57
  runner.run
46
58
  end
@@ -5,42 +5,10 @@ require 'active_support/core_ext/numeric/time'
5
5
  require 'active_support/core_ext/string/inflections'
6
6
  require 'active_support/core_ext/string/strip'
7
7
 
8
- require 'logger'
8
+ require 'kubernetes-deploy/logger'
9
9
  require 'kubernetes-deploy/runner'
10
10
 
11
11
  module KubernetesDeploy
12
12
  class FatalDeploymentError < StandardError; end
13
-
14
- class << self
15
- attr_writer :logger
16
-
17
- def logger
18
- @logger ||= begin
19
- l = Logger.new($stderr)
20
- l.level = level_from_env
21
- l.formatter = proc do |severity, datetime, _progname, msg|
22
- log_text = "[#{severity}][#{datetime}]\t#{msg}"
23
- case severity
24
- when "FATAL" then "\033[0;31m#{log_text}\x1b[0m\n" # red
25
- when "ERROR", "WARN" then "\033[0;33m#{log_text}\x1b[0m\n" # yellow
26
- when "INFO" then "\033[0;36m#{log_text}\x1b[0m\n" # blue
27
- else "#{log_text}\n"
28
- end
29
- end
30
- l
31
- end
32
- end
33
-
34
- private
35
-
36
- def level_from_env
37
- return Logger::DEBUG if ENV["DEBUG"]
38
-
39
- if ENV["LEVEL"]
40
- Logger.const_get(ENV["LEVEL"].upcase)
41
- else
42
- Logger::INFO
43
- end
44
- end
45
- end
13
+ include Logger
46
14
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KubernetesDeploy
4
+ module Kubectl
5
+ def self.run_kubectl(*args, namespace:, context:, log_failure: true)
6
+ args = args.unshift("kubectl")
7
+ args.push("--namespace=#{namespace}") if namespace.present?
8
+ args.push("--context=#{context}") if context.present?
9
+
10
+ KubernetesDeploy.logger.debug Shellwords.join(args)
11
+ out, err, st = Open3.capture3(*args)
12
+ KubernetesDeploy.logger.debug(out.shellescape)
13
+ if !st.success? && log_failure
14
+ KubernetesDeploy.logger.warn("The following command failed: #{Shellwords.join(args)}")
15
+ KubernetesDeploy.logger.warn(err)
16
+ end
17
+ [out.chomp, err.chomp, st]
18
+ end
19
+ end
20
+ end
@@ -1,6 +1,7 @@
1
1
  require 'json'
2
2
  require 'open3'
3
3
  require 'shellwords'
4
+ require 'kubernetes-deploy/kubectl'
4
5
 
5
6
  module KubernetesDeploy
6
7
  class KubernetesResource
@@ -10,7 +11,7 @@ module KubernetesDeploy
10
11
 
11
12
  def self.logger
12
13
  @logger ||= begin
13
- l = Logger.new($stderr)
14
+ l = ::Logger.new($stderr)
14
15
  l.formatter = proc do |_severity, datetime, _progname, msg|
15
16
  "[KUBESTATUS][#{datetime}]\t#{msg}\n"
16
17
  end
@@ -30,6 +31,7 @@ module KubernetesDeploy
30
31
  when 'deployment' then Deployment.new(name, namespace, context, file)
31
32
  when 'pod' then Pod.new(name, namespace, context, file)
32
33
  when 'redis' then Redis.new(name, namespace, context, file)
34
+ when 'bugsnag' then Bugsnag.new(name, namespace, context, file)
33
35
  when 'ingress' then Ingress.new(name, namespace, context, file)
34
36
  when 'persistentvolumeclaim' then PersistentVolumeClaim.new(name, namespace, context, file)
35
37
  when 'service' then Service.new(name, namespace, context, file)
@@ -112,20 +114,15 @@ module KubernetesDeploy
112
114
  type.downcase.pluralize
113
115
  end
114
116
 
115
- def run_kubectl(*args)
116
- raise FatalDeploymentError, "Namespace missing for namespaced command" if namespace.blank?
117
- raise FatalDeploymentError, "Explicit context is required to run this command" if context.blank?
118
- args = args.unshift("kubectl").push("--namespace=#{namespace}").push("--context=#{context}")
119
-
120
- KubernetesDeploy.logger.debug Shellwords.join(args)
121
- out, err, st = Open3.capture3(*args)
122
- KubernetesDeploy.logger.debug(out.shellescape)
123
- KubernetesDeploy.logger.debug("[ERROR] #{err.shellescape}") unless st.success?
124
- [out.chomp, st]
125
- end
126
-
127
117
  def log_status
128
118
  KubernetesResource.logger.info("[#{@context}][#{@namespace}] #{JSON.dump(status_data)}")
129
119
  end
120
+
121
+ def run_kubectl(*args)
122
+ raise FatalDeploymentError, "Namespace missing for namespaced command" if @namespace.blank?
123
+ raise KubectlError, "Explicit context is required to run this command" if @context.blank?
124
+
125
+ Kubectl.run_kubectl(*args, namespace: @namespace, context: @context, log_failure: false)
126
+ end
130
127
  end
131
128
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ module KubernetesDeploy
3
+ class Bugsnag < KubernetesResource
4
+ TIMEOUT = 1.minute
5
+
6
+ def initialize(name, namespace, context, file)
7
+ @name = name
8
+ @namespace = namespace
9
+ @context = context
10
+ @file = file
11
+ @secret_found = false
12
+ end
13
+
14
+ def sync
15
+ _, _err, st = run_kubectl("get", type, @name)
16
+ @found = st.success?
17
+ if @found
18
+ secrets, _err, _st = run_kubectl("get", "secrets", "--output=name")
19
+ @secret_found = secrets.split.any? { |s| s.end_with?("-bugsnag") }
20
+ end
21
+ @status = @secret_found ? "Available" : "Unknown"
22
+ log_status
23
+ end
24
+
25
+ def deploy_succeeded?
26
+ @secret_found
27
+ end
28
+
29
+ def deploy_failed?
30
+ false
31
+ end
32
+
33
+ def exists?
34
+ @found
35
+ end
36
+
37
+ def tpr?
38
+ true
39
+ end
40
+ end
41
+ end
@@ -11,7 +11,7 @@ module KubernetesDeploy
11
11
  end
12
12
 
13
13
  def sync
14
- _, st = run_kubectl("get", type, @name)
14
+ _, _err, st = run_kubectl("get", type, @name)
15
15
  @found = st.success?
16
16
  @status = if cloudsql_proxy_deployment_exists? && mysql_service_exists?
17
17
  "Provisioned"
@@ -41,7 +41,7 @@ module KubernetesDeploy
41
41
  private
42
42
 
43
43
  def cloudsql_proxy_deployment_exists?
44
- deployment, st = run_kubectl("get", "deployments", "cloudsql-proxy", "-o=json")
44
+ deployment, _err, st = run_kubectl("get", "deployments", "cloudsql-proxy", "-o=json")
45
45
 
46
46
  if st.success?
47
47
  parsed = JSON.parse(deployment)
@@ -56,7 +56,7 @@ module KubernetesDeploy
56
56
  end
57
57
 
58
58
  def mysql_service_exists?
59
- service, st = run_kubectl("get", "services", "mysql", "-o=json")
59
+ service, _err, st = run_kubectl("get", "services", "mysql", "-o=json")
60
60
 
61
61
  if st.success?
62
62
  parsed = JSON.parse(service)
@@ -11,7 +11,7 @@ module KubernetesDeploy
11
11
  end
12
12
 
13
13
  def sync
14
- _, st = run_kubectl("get", type, @name)
14
+ _, _err, st = run_kubectl("get", type, @name)
15
15
  @status = st.success? ? "Available" : "Unknown"
16
16
  @found = st.success?
17
17
  log_status
@@ -11,7 +11,7 @@ module KubernetesDeploy
11
11
  end
12
12
 
13
13
  def sync
14
- json_data, st = run_kubectl("get", type, @name, "--output=json")
14
+ json_data, _err, st = run_kubectl("get", type, @name, "--output=json")
15
15
  @found = st.success?
16
16
  @rollout_data = {}
17
17
  @status = nil
@@ -20,9 +20,9 @@ module KubernetesDeploy
20
20
  if @found
21
21
  @rollout_data = JSON.parse(json_data)["status"]
22
22
  .slice("updatedReplicas", "replicas", "availableReplicas", "unavailableReplicas")
23
- @status, _ = run_kubectl("rollout", "status", type, @name, "--watch=false") if @deploy_started
23
+ @status, _err, _ = run_kubectl("rollout", "status", type, @name, "--watch=false") if @deploy_started
24
24
 
25
- pod_list, st = run_kubectl("get", "pods", "-a", "-l", "name=#{name}", "--output=json")
25
+ pod_list, _err, st = run_kubectl("get", "pods", "-a", "-l", "name=#{name}", "--output=json")
26
26
  if st.success?
27
27
  pods_json = JSON.parse(pod_list)["items"]
28
28
  pods_json.each do |pod_json|
@@ -11,7 +11,7 @@ module KubernetesDeploy
11
11
  end
12
12
 
13
13
  def sync
14
- _, st = run_kubectl("get", type, @name)
14
+ _, _err, st = run_kubectl("get", type, @name)
15
15
  @status = st.success? ? "Created" : "Unknown"
16
16
  @found = st.success?
17
17
  log_status
@@ -11,7 +11,7 @@ module KubernetesDeploy
11
11
  end
12
12
 
13
13
  def sync
14
- @status, st = run_kubectl("get", type, @name, "--output=jsonpath={.status.phase}")
14
+ @status, _err, st = run_kubectl("get", type, @name, "--output=jsonpath={.status.phase}")
15
15
  @found = st.success?
16
16
  log_status
17
17
  end
@@ -14,7 +14,7 @@ module KubernetesDeploy
14
14
  end
15
15
 
16
16
  def sync
17
- out, st = run_kubectl("get", type, @name, "-a", "--output=json")
17
+ out, _err, st = run_kubectl("get", type, @name, "-a", "--output=json")
18
18
  if @found = st.success?
19
19
  pod_data = JSON.parse(out)
20
20
  interpret_json_data(pod_data)
@@ -77,7 +77,12 @@ module KubernetesDeploy
77
77
  return {} unless exists? && @containers.present? && !@already_displayed
78
78
 
79
79
  @containers.each do |container_name|
80
- out, st = run_kubectl("logs", @name, "--timestamps=true", "--since-time=#{@deploy_started.to_datetime.rfc3339}")
80
+ out, _err, st = run_kubectl(
81
+ "logs",
82
+ @name,
83
+ "--timestamps=true",
84
+ "--since-time=#{@deploy_started.to_datetime.rfc3339}"
85
+ )
81
86
  next unless st.success? && out.present?
82
87
 
83
88
  KubernetesDeploy.logger.info "Logs from #{id} container #{container_name}:\x1b[0m \n#{out}\n"
@@ -12,7 +12,7 @@ module KubernetesDeploy
12
12
  end
13
13
 
14
14
  def sync
15
- _, st = run_kubectl("get", type, @name)
15
+ _, _err, st = run_kubectl("get", type, @name)
16
16
  @found = st.success?
17
17
  @status = if redis_deployment_exists? && redis_service_exists?
18
18
  "Provisioned"
@@ -42,7 +42,7 @@ module KubernetesDeploy
42
42
  private
43
43
 
44
44
  def redis_deployment_exists?
45
- deployment, st = run_kubectl("get", "deployments", "redis-#{redis_resource_uuid}", "-o=json")
45
+ deployment, _err, st = run_kubectl("get", "deployments", "redis-#{redis_resource_uuid}", "-o=json")
46
46
 
47
47
  if st.success?
48
48
  parsed = JSON.parse(deployment)
@@ -57,7 +57,7 @@ module KubernetesDeploy
57
57
  end
58
58
 
59
59
  def redis_service_exists?
60
- service, st = run_kubectl("get", "services", "redis-#{redis_resource_uuid}", "-o=json")
60
+ service, _err, st = run_kubectl("get", "services", "redis-#{redis_resource_uuid}", "-o=json")
61
61
 
62
62
  if st.success?
63
63
  parsed = JSON.parse(service)
@@ -73,7 +73,7 @@ module KubernetesDeploy
73
73
  def redis_resource_uuid
74
74
  return @redis_resource_uuid if defined?(@redis_resource_uuid) && @redis_resource_uuid
75
75
 
76
- redis, st = run_kubectl("get", "redises", @name, "-o=json")
76
+ redis, _err, st = run_kubectl("get", "redises", @name, "-o=json")
77
77
  if st.success?
78
78
  parsed = JSON.parse(redis)
79
79
 
@@ -11,10 +11,10 @@ module KubernetesDeploy
11
11
  end
12
12
 
13
13
  def sync
14
- _, st = run_kubectl("get", type, @name)
14
+ _, _err, st = run_kubectl("get", type, @name)
15
15
  @found = st.success?
16
16
  if @found
17
- endpoints, st = run_kubectl("get", "endpoints", @name, "--output=jsonpath={.subsets[*].addresses[*].ip}")
17
+ endpoints, _err, st = run_kubectl("get", "endpoints", @name, "--output=jsonpath={.subsets[*].addresses[*].ip}")
18
18
  @num_endpoints = (st.success? ? endpoints.split.length : 0)
19
19
  else
20
20
  @num_endpoints = 0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ require 'logger'
3
+
4
+ module KubernetesDeploy
5
+ module Logger
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def logger=(other)
12
+ @logger = other
13
+ end
14
+
15
+ def logger
16
+ @logger ||= begin
17
+ l = ::Logger.new($stderr)
18
+ l.level = level_from_env
19
+ l.formatter = proc do |severity, datetime, _progname, msg|
20
+ log_text = "[#{severity}][#{datetime}]\t#{msg}"
21
+ case severity
22
+ when "FATAL" then "\033[0;31m#{log_text}\x1b[0m\n" # red
23
+ when "ERROR", "WARN" then "\033[0;33m#{log_text}\x1b[0m\n" # yellow
24
+ when "INFO" then "\033[0;36m#{log_text}\x1b[0m\n" # blue
25
+ else "#{log_text}\n"
26
+ end
27
+ end
28
+ l
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def level_from_env
35
+ return ::Logger::DEBUG if ENV["DEBUG"]
36
+
37
+ if ENV["LEVEL"]
38
+ ::Logger.const_get(ENV["LEVEL"].upcase)
39
+ else
40
+ ::Logger::INFO
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -16,11 +16,13 @@ require 'kubernetes-deploy/kubernetes_resource'
16
16
  pod
17
17
  redis
18
18
  service
19
+ bugsnag
19
20
  ).each do |subresource|
20
21
  require "kubernetes-deploy/kubernetes_resource/#{subresource}"
21
22
  end
22
23
  require 'kubernetes-deploy/resource_watcher'
23
24
  require "kubernetes-deploy/ui_helpers"
25
+ require 'kubernetes-deploy/kubectl'
24
26
 
25
27
  module KubernetesDeploy
26
28
  class Runner
@@ -29,6 +31,7 @@ module KubernetesDeploy
29
31
  PREDEPLOY_SEQUENCE = %w(
30
32
  Cloudsql
31
33
  Redis
34
+ Bugsnag
32
35
  ConfigMap
33
36
  PersistentVolumeClaim
34
37
  Pod
@@ -71,7 +74,7 @@ MSG
71
74
  end
72
75
 
73
76
  def initialize(namespace:, current_sha:, context:, template_dir:,
74
- wait_for_completion:, allow_protected_ns: false, prune: true)
77
+ wait_for_completion:, allow_protected_ns: false, prune: true, bindings: {})
75
78
  @namespace = namespace
76
79
  @context = context
77
80
  @current_sha = current_sha
@@ -81,6 +84,7 @@ MSG
81
84
  @wait_for_completion = wait_for_completion
82
85
  @allow_protected_ns = allow_protected_ns
83
86
  @prune = prune
87
+ @bindings = bindings
84
88
  end
85
89
 
86
90
  def wait_for_completion?
@@ -123,7 +127,7 @@ MSG
123
127
  {
124
128
  'current_sha' => @current_sha,
125
129
  'deployment_id' => @id,
126
- }
130
+ }.merge(@bindings)
127
131
  end
128
132
 
129
133
  private
@@ -341,24 +345,15 @@ MSG
341
345
  end
342
346
 
343
347
  def run_kubectl(*args, namespaced: true, with_context: true)
344
- args = args.unshift("kubectl")
345
348
  if namespaced
346
- raise FatalDeploymentError, "Namespace missing for namespaced command" if @namespace.blank?
347
- args.push("--namespace=#{@namespace}")
349
+ raise KubectlError, "Namespace missing for namespaced command" if @namespace.blank?
348
350
  end
349
351
 
350
352
  if with_context
351
- raise FatalDeploymentError, "Explicit context is required to run this command" if @context.blank?
352
- args.push("--context=#{@context}")
353
+ raise KubectlError, "Explicit context is required to run this command" if @context.blank?
353
354
  end
354
- KubernetesDeploy.logger.debug Shellwords.join(args)
355
- out, err, st = Open3.capture3(*args)
356
- KubernetesDeploy.logger.debug(out.shellescape)
357
- unless st.success?
358
- KubernetesDeploy.logger.warn("The following command failed: #{Shellwords.join(args)}")
359
- KubernetesDeploy.logger.warn(err)
360
- end
361
- [out.chomp, err.chomp, st]
355
+
356
+ Kubectl.run_kubectl(*args, namespace: @namespace, context: @context)
362
357
  end
363
358
  end
364
359
  end
@@ -1,3 +1,3 @@
1
1
  module KubernetesDeploy
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5.0"
3
3
  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.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kir Shatrov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2017-04-11 00:00:00.000000000 Z
13
+ date: 2017-04-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -118,7 +118,9 @@ files:
118
118
  - kubernetes-deploy.gemspec
119
119
  - lib/kubernetes-deploy.rb
120
120
  - lib/kubernetes-deploy/kubeclient_builder.rb
121
+ - lib/kubernetes-deploy/kubectl.rb
121
122
  - lib/kubernetes-deploy/kubernetes_resource.rb
123
+ - lib/kubernetes-deploy/kubernetes_resource/bugsnag.rb
122
124
  - lib/kubernetes-deploy/kubernetes_resource/cloudsql.rb
123
125
  - lib/kubernetes-deploy/kubernetes_resource/config_map.rb
124
126
  - lib/kubernetes-deploy/kubernetes_resource/deployment.rb
@@ -127,6 +129,7 @@ files:
127
129
  - lib/kubernetes-deploy/kubernetes_resource/pod.rb
128
130
  - lib/kubernetes-deploy/kubernetes_resource/redis.rb
129
131
  - lib/kubernetes-deploy/kubernetes_resource/service.rb
132
+ - lib/kubernetes-deploy/logger.rb
130
133
  - lib/kubernetes-deploy/resource_watcher.rb
131
134
  - lib/kubernetes-deploy/restart_task.rb
132
135
  - lib/kubernetes-deploy/runner.rb