kubernetes-deploy 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 55540c9fa5a002f87eba1c06883e0b237d194803
4
- data.tar.gz: 5d2e45261bb51d18d34f5337eb7006892885b99e
3
+ metadata.gz: 315b39149d1da2c636e590c936d537c2cf99efd1
4
+ data.tar.gz: 2a6a00d4408877852e7e50ac11648a5598fcdf57
5
5
  SHA512:
6
- metadata.gz: 0fb77ac54ebf8f56eb7c116a29ef373da76d694c4977a5c704f78cafacf9c97c2cad25e5137e9991eaf689661323ab889b205da8db96a87c02c0ce94d641f023
7
- data.tar.gz: 2e00d11e27cd3965e30592552adcf77ea7b599cb120a376d2c32b226b7bd07f4c0a1040da24534304b9910ed8c95cf4ad447937a3773d01cc11d5e1b5eac3e2a
6
+ metadata.gz: c0847ad6ae5468233e7223a1947a06e3f19552bf6e32d6aba0c2aa2470d5118c4e4a7f3d0957326efe9dcebaf4086e81c513bde9ad220761b76407379a22b795
7
+ data.tar.gz: 72d92f343209f183cbae64f4dc59413d17bd7883a8182e4a8f8352cb9629d89b7f6b114bb1356a278a4dd9d198040def3ec680735bbe8be39a4b36c921c71e55
data/.rubocop.yml CHANGED
@@ -2,4 +2,4 @@ inherit_from:
2
2
  - http://shopify.github.io/ruby-style-guide/rubocop.yml
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.3
5
+ TargetRubyVersion: 2.1
@@ -3,6 +3,7 @@ require 'json'
3
3
  require 'base64'
4
4
  require 'open3'
5
5
  require 'kubernetes-deploy/logger'
6
+ require 'kubernetes-deploy/kubectl'
6
7
 
7
8
  module KubernetesDeploy
8
9
  class EjsonSecretError < FatalDeploymentError
@@ -17,10 +18,13 @@ module KubernetesDeploy
17
18
  EJSON_SECRETS_FILE = "secrets.ejson"
18
19
  EJSON_KEYS_SECRET = "ejson-keys"
19
20
 
20
- def initialize(namespace:, template_dir:, client:)
21
+ def initialize(namespace:, context:, template_dir:)
21
22
  @namespace = namespace
23
+ @context = context
22
24
  @ejson_file = "#{template_dir}/#{EJSON_SECRETS_FILE}"
23
- @kubeclient = client
25
+
26
+ raise FatalDeploymentError, "Cannot create secrets without a namespace" if @namespace.blank?
27
+ raise FatalDeploymentError, "Cannot create secrets without a context" if @context.blank?
24
28
  end
25
29
 
26
30
  def secret_changes_required?
@@ -52,25 +56,27 @@ module KubernetesDeploy
52
56
 
53
57
  def prune_managed_secrets
54
58
  ejson_secret_names = encrypted_ejson.fetch(MANAGED_SECRET_EJSON_KEY, {}).keys
55
- live_secrets = @kubeclient.get_secrets(namespace: @namespace)
59
+ live_secrets = run_kubectl_json("get", "secrets")
56
60
 
57
61
  live_secrets.each do |secret|
58
- secret_name = secret.metadata.name
62
+ secret_name = secret["metadata"]["name"]
59
63
  next unless secret_managed?(secret)
60
64
  next if ejson_secret_names.include?(secret_name)
61
65
 
62
66
  KubernetesDeploy.logger.info("Pruning secret #{secret_name}")
63
- @kubeclient.delete_secret(secret_name, @namespace)
67
+ out, err, st = run_kubectl("delete", "secret", secret_name)
68
+ KubernetesDeploy.logger.debug(out)
69
+ raise EjsonSecretError, err unless st.success?
64
70
  end
65
71
  end
66
72
 
67
73
  def managed_secrets_exist?
68
- all_secrets = @kubeclient.get_secrets(namespace: @namespace)
74
+ all_secrets = run_kubectl_json("get", "secrets")
69
75
  all_secrets.any? { |secret| secret_managed?(secret) }
70
76
  end
71
77
 
72
78
  def secret_managed?(secret)
73
- secret.metadata.annotations.to_h.stringify_keys.key?(MANAGEMENT_ANNOTATION)
79
+ secret["metadata"].fetch("annotations", {}).key?(MANAGEMENT_ANNOTATION)
74
80
  end
75
81
 
76
82
  def encrypted_ejson
@@ -96,31 +102,47 @@ module KubernetesDeploy
96
102
  end
97
103
 
98
104
  def create_or_update_secret(secret_name, secret_type, data)
99
- metadata = {
100
- name: secret_name,
101
- labels: { "name" => secret_name },
102
- namespace: @namespace,
103
- annotations: { MANAGEMENT_ANNOTATION => "true" }
104
- }
105
- secret = Kubeclient::Secret.new(type: secret_type, stringData: data, metadata: metadata)
106
- if secret_exists?(secret)
107
- KubernetesDeploy.logger.info("Updating secret #{secret_name}")
108
- @kubeclient.update_secret(secret)
109
- else
110
- KubernetesDeploy.logger.info("Creating secret #{secret_name}")
111
- @kubeclient.create_secret(secret)
105
+ msg = secret_exists?(secret_name) ? "Updating secret #{secret_name}" : "Creating secret #{secret_name}"
106
+ KubernetesDeploy.logger.info(msg)
107
+
108
+ secret_yaml = generate_secret_yaml(secret_name, secret_type, data)
109
+ file = Tempfile.new(secret_name)
110
+ file.write(secret_yaml)
111
+ file.close
112
+
113
+ out, err, st = run_kubectl("apply", "--filename=#{file.path}")
114
+ KubernetesDeploy.logger.debug(out)
115
+ raise EjsonSecretError, err unless st.success?
116
+ ensure
117
+ file.unlink if file
118
+ end
119
+
120
+ def generate_secret_yaml(secret_name, secret_type, data)
121
+ unless data.is_a?(Hash) && data.values.all? { |v| v.is_a?(String) } # Secret data is map[string]string
122
+ raise EjsonSecretError, "Data for secret #{secret_name} was invalid. Only key-value pairs are permitted."
123
+ end
124
+ encoded_data = data.each_with_object({}) do |(key, value), encoded|
125
+ encoded[key] = Base64.encode64(value)
112
126
  end
113
- rescue KubeException => e
114
- raise unless e.error_code == 400
115
- raise EjsonSecretError, "Data for secret #{secret_name} was invalid: #{e}"
127
+
128
+ secret = {
129
+ 'kind' => 'Secret',
130
+ 'apiVersion' => 'v1',
131
+ 'type' => secret_type,
132
+ 'metadata' => {
133
+ "name" => secret_name,
134
+ "labels" => { "name" => secret_name },
135
+ "namespace" => @namespace,
136
+ "annotations" => { MANAGEMENT_ANNOTATION => "true" }
137
+ },
138
+ "data" => encoded_data
139
+ }
140
+ secret.to_yaml
116
141
  end
117
142
 
118
- def secret_exists?(secret)
119
- @kubeclient.get_secret(secret.metadata.name, @namespace)
120
- true
121
- rescue KubeException => error
122
- raise unless error.error_code == 404
123
- false
143
+ def secret_exists?(secret_name)
144
+ _out, _err, st = run_kubectl("get", "secret", secret_name)
145
+ st.success?
124
146
  end
125
147
 
126
148
  def load_ejson_from_file
@@ -152,17 +174,26 @@ module KubernetesDeploy
152
174
 
153
175
  def fetch_private_key_from_secret
154
176
  KubernetesDeploy.logger.info("Fetching ejson private key from secret #{EJSON_KEYS_SECRET}")
155
- secret = @kubeclient.get_secret(EJSON_KEYS_SECRET, @namespace)
177
+
178
+ secret = run_kubectl_json("get", "secret", EJSON_KEYS_SECRET)
156
179
  encoded_private_key = secret["data"][public_key]
157
180
  unless encoded_private_key
158
181
  raise EjsonSecretError, "Private key for #{public_key} not found in #{EJSON_KEYS_SECRET} secret"
159
182
  end
160
183
 
161
184
  Base64.decode64(encoded_private_key)
162
- rescue KubeException => error
163
- raise unless error.error_code == 404
164
- secret_missing_err = "Failed to decrypt ejson: secret #{EJSON_KEYS_SECRET} not found in namespace #{@namespace}."
165
- raise EjsonSecretError, secret_missing_err
185
+ end
186
+
187
+ def run_kubectl_json(*args)
188
+ args += ["--output=json"]
189
+ out, err, st = run_kubectl(*args)
190
+ raise EjsonSecretError, err unless st.success?
191
+ result = JSON.parse(out)
192
+ result.fetch('items', result)
193
+ end
194
+
195
+ def run_kubectl(*args)
196
+ Kubectl.run_kubectl(*args, namespace: @namespace, context: @context)
166
197
  end
167
198
  end
168
199
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module KubernetesDeploy
3
3
  class FatalDeploymentError < StandardError; end
4
+ class KubectlError < StandardError; end
4
5
 
5
6
  class NamespaceNotFoundError < FatalDeploymentError
6
7
  def initialize(name, context)
@@ -113,11 +113,7 @@ MSG
113
113
  phase_heading("Checking initial resource statuses")
114
114
  resources.each(&:sync)
115
115
 
116
- ejson = EjsonSecretProvisioner.new(
117
- namespace: @namespace,
118
- template_dir: @template_dir,
119
- client: build_v1_kubeclient(@context)
120
- )
116
+ ejson = EjsonSecretProvisioner.new(namespace: @namespace, context: @context, template_dir: @template_dir)
121
117
  if ejson.secret_changes_required?
122
118
  phase_heading("Deploying kubernetes secrets from #{EjsonSecretProvisioner::EJSON_SECRETS_FILE}")
123
119
  ejson.run
@@ -1,3 +1,3 @@
1
1
  module KubernetesDeploy
2
- VERSION = "0.6.2"
2
+ VERSION = "0.6.3"
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.6.2
4
+ version: 0.6.3
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-05-08 00:00:00.000000000 Z
13
+ date: 2017-05-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport