kubernetes-deploy 0.5.0 → 0.6.0
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 +4 -4
- data/.gitignore +1 -0
- data/README.md +21 -0
- data/exe/kubernetes-run +31 -0
- data/lib/kubernetes-deploy.rb +7 -0
- data/lib/kubernetes-deploy/kubernetes_resource.rb +1 -0
- data/lib/kubernetes-deploy/kubernetes_resource/pod_template.rb +30 -0
- data/lib/kubernetes-deploy/restart_task.rb +0 -6
- data/lib/kubernetes-deploy/runner.rb +2 -1
- data/lib/kubernetes-deploy/runner_task.rb +152 -0
- data/lib/kubernetes-deploy/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6c0635aa2dd4ca87b4cea59f90b783251f93418
|
4
|
+
data.tar.gz: 9859f840e6b082188e50e8545546b756b399e95c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f637386dec36fb619f319d22109f113b621587c0e1b6f8774d76801582a3c349355ed2f2ac3de57e7d2c1f30792e9c796abd27fde46c58b1bd558ec849290209
|
7
|
+
data.tar.gz: 57c73ae638bab026343598ae9f503f9a1af48d11a77ceed8e4c73238d3795314ff1eacf775101dcc19cdf76c2fd3ef584c13c53ca9910db8f69b61bf6a8f7cd4
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -40,6 +40,27 @@ The following command will restart all pods in the `web` and `jobs` deployments:
|
|
40
40
|
|
41
41
|
`kubernetes-restart <kube namespace> <kube context> --deployments=web,jobs`
|
42
42
|
|
43
|
+
### Running one off tasks
|
44
|
+
|
45
|
+
To trigger a one-off job such as a rake task _outside_ of a deploy, use the following command:
|
46
|
+
|
47
|
+
`kubernetes-run <kube namespace> <kube context> <arguments> --entrypoint=/bin/bash`
|
48
|
+
|
49
|
+
This command assumes that you've already deployed a `PodTemplate` named `task-runner-template`, which contains a full pod specification in its `data`. The pod specification in turn must have a container named `task-runner`. Based on this specification `kubernetes-run` will create a new pod with the entrypoint of the task-runner container overriden with the supplied arguments.
|
50
|
+
|
51
|
+
#### Creating a PodTemplate
|
52
|
+
|
53
|
+
The [`PodTemplate`](https://kubernetes.io/docs/api-reference/v1.6/#podtemplate-v1-core) object should have a field `template` containing a `Pod` specification which does not include the `apiVersion` or `kind` parameters. An example is provided in this repo in `test/fixtures/hello-cloud/template-runner.yml`.
|
54
|
+
|
55
|
+
#### Providing multiple different task-runner configurations
|
56
|
+
|
57
|
+
If your application requires task runner templates you can specify which template to use by using the `--template` option. All templates are expected to provide a container called `task-runner`.
|
58
|
+
|
59
|
+
#### Specifying environment variables for the container
|
60
|
+
|
61
|
+
If you also need to specify environment variables on top of the arguments, you can specify the `--env-vars` flag which accepts a comma separated list of environment variables like so: `--env-vars="ENV=VAL,ENV2=VAL"`
|
62
|
+
|
63
|
+
|
43
64
|
## Development
|
44
65
|
|
45
66
|
After checking out the repo, run `bin/setup` to install dependencies. You currently need to [manually install kubectl version 1.6.0 or higher](https://kubernetes.io/docs/user-guide/prereqs/) as well if you don't already have it.
|
data/exe/kubernetes-run
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'kubernetes-deploy'
|
5
|
+
require 'kubernetes-deploy/runner_task'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
template = "task-runner-template"
|
9
|
+
entrypoint = nil
|
10
|
+
env_vars = []
|
11
|
+
|
12
|
+
ARGV.options do |opts|
|
13
|
+
opts.on("--template=TEMPLATE") { |n| template = n }
|
14
|
+
opts.on("--env-vars=ENV_VARS") { |vars| env_vars = n.split(",")}
|
15
|
+
opts.on("--entrypoint=ENTRYPOINT") { |c| entrypoint = [c] }
|
16
|
+
opts.parse!
|
17
|
+
end
|
18
|
+
|
19
|
+
runner = KubernetesDeploy::RunnerTask.new(
|
20
|
+
namespace: ARGV[0],
|
21
|
+
context: ARGV[1],
|
22
|
+
)
|
23
|
+
|
24
|
+
KubernetesDeploy::Runner.with_friendly_errors do
|
25
|
+
runner.run(
|
26
|
+
task_template: template,
|
27
|
+
entrypoint: entrypoint,
|
28
|
+
args: ARGV[2..-1],
|
29
|
+
env_vars: env_vars
|
30
|
+
)
|
31
|
+
end
|
data/lib/kubernetes-deploy.rb
CHANGED
@@ -10,5 +10,12 @@ require 'kubernetes-deploy/runner'
|
|
10
10
|
|
11
11
|
module KubernetesDeploy
|
12
12
|
class FatalDeploymentError < StandardError; end
|
13
|
+
|
14
|
+
class NamespaceNotFoundError < FatalDeploymentError
|
15
|
+
def initialize(name, context)
|
16
|
+
super("Namespace `#{name}` not found in context `#{context}`. Aborting the task.")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
13
20
|
include Logger
|
14
21
|
end
|
@@ -35,6 +35,7 @@ module KubernetesDeploy
|
|
35
35
|
when 'ingress' then Ingress.new(name, namespace, context, file)
|
36
36
|
when 'persistentvolumeclaim' then PersistentVolumeClaim.new(name, namespace, context, file)
|
37
37
|
when 'service' then Service.new(name, namespace, context, file)
|
38
|
+
when 'podtemplate' then PodTemplate.new(name, namespace, context, file)
|
38
39
|
else self.new(name, namespace, context, file).tap { |r| r.type = type }
|
39
40
|
end
|
40
41
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module KubernetesDeploy
|
3
|
+
class PodTemplate < KubernetesResource
|
4
|
+
def initialize(name, namespace, context, file)
|
5
|
+
@name = name
|
6
|
+
@namespace = namespace
|
7
|
+
@context = context
|
8
|
+
@file = file
|
9
|
+
end
|
10
|
+
|
11
|
+
def sync
|
12
|
+
_, _err, st = run_kubectl("get", type, @name)
|
13
|
+
@status = st.success? ? "Available" : "Unknown"
|
14
|
+
@found = st.success?
|
15
|
+
log_status
|
16
|
+
end
|
17
|
+
|
18
|
+
def deploy_succeeded?
|
19
|
+
exists?
|
20
|
+
end
|
21
|
+
|
22
|
+
def deploy_failed?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def exists?
|
27
|
+
@found
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -14,12 +14,6 @@ module KubernetesDeploy
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
class NamespaceNotFoundError < FatalDeploymentError
|
18
|
-
def initialize(name, context)
|
19
|
-
super("Namespace `#{name}` not found in context `#{context}`. Aborting the task.")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
17
|
class RestartError < FatalDeploymentError
|
24
18
|
def initialize(deployment_name, response)
|
25
19
|
super("Failed to restart #{deployment_name}. " \
|
@@ -5,7 +5,6 @@ require 'erb'
|
|
5
5
|
require 'yaml'
|
6
6
|
require 'shellwords'
|
7
7
|
require 'tempfile'
|
8
|
-
|
9
8
|
require 'kubernetes-deploy/kubernetes_resource'
|
10
9
|
%w(
|
11
10
|
cloudsql
|
@@ -16,6 +15,7 @@ require 'kubernetes-deploy/kubernetes_resource'
|
|
16
15
|
pod
|
17
16
|
redis
|
18
17
|
service
|
18
|
+
pod_template
|
19
19
|
bugsnag
|
20
20
|
).each do |subresource|
|
21
21
|
require "kubernetes-deploy/kubernetes_resource/#{subresource}"
|
@@ -116,6 +116,7 @@ MSG
|
|
116
116
|
if PROTECTED_NAMESPACES.include?(@namespace) && @prune
|
117
117
|
raise FatalDeploymentError, "Refusing to deploy to protected namespace '#{@namespace}' with pruning enabled"
|
118
118
|
end
|
119
|
+
|
119
120
|
deploy_resources(resources, prune: @prune)
|
120
121
|
|
121
122
|
return unless wait_for_completion?
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
require 'kubernetes-deploy/kubeclient_builder'
|
5
|
+
require 'kubernetes-deploy/ui_helpers'
|
6
|
+
require 'kubernetes-deploy/kubectl'
|
7
|
+
|
8
|
+
module KubernetesDeploy
|
9
|
+
class RunnerTask
|
10
|
+
include KubeclientBuilder
|
11
|
+
include Kubectl
|
12
|
+
include UIHelpers
|
13
|
+
|
14
|
+
class FatalTaskRunError < FatalDeploymentError; end
|
15
|
+
class TaskTemplateMissingError < FatalDeploymentError
|
16
|
+
def initialize(task_template, namespace, context)
|
17
|
+
super("Pod template `#{task_template}` cannot be found in namespace: `#{namespace}`, context: `#{context}`")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(namespace:, context:, logger: KubernetesDeploy.logger)
|
22
|
+
@logger = logger
|
23
|
+
@namespace = namespace
|
24
|
+
@kubeclient = build_v1_kubeclient(context)
|
25
|
+
@context = context
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(task_template:, entrypoint:, args:, env_vars: [])
|
29
|
+
phase_heading("Validating configuration")
|
30
|
+
validate_configuration(task_template, args)
|
31
|
+
|
32
|
+
phase_heading("Fetching task template")
|
33
|
+
raw_template = get_template(task_template)
|
34
|
+
|
35
|
+
phase_heading("Constructing final pod specification")
|
36
|
+
rendered_template = build_pod_template(raw_template, entrypoint, args, env_vars)
|
37
|
+
|
38
|
+
validate_pod_spec(rendered_template)
|
39
|
+
|
40
|
+
phase_heading("Creating pod")
|
41
|
+
KubernetesDeploy.logger.info("Starting task runner pod: '#{rendered_template.metadata.name}'")
|
42
|
+
@kubeclient.create_pod(rendered_template)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def validate_configuration(task_template, args)
|
48
|
+
errors = []
|
49
|
+
|
50
|
+
if task_template.blank?
|
51
|
+
errors << "Task template name can't be nil"
|
52
|
+
end
|
53
|
+
|
54
|
+
if @namespace.blank?
|
55
|
+
errors << "Namespace can't be empty"
|
56
|
+
end
|
57
|
+
|
58
|
+
if args.blank?
|
59
|
+
errors << "Args can't be nil"
|
60
|
+
end
|
61
|
+
|
62
|
+
begin
|
63
|
+
@kubeclient.get_namespace(@namespace)
|
64
|
+
rescue KubeException => e
|
65
|
+
errors << if e.error_code == 404
|
66
|
+
"Namespace was not found"
|
67
|
+
else
|
68
|
+
"Could not connect to kubernetes cluster"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
raise FatalTaskRunError, "Configuration invalid: #{errors.join(', ')}" unless errors.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_template(template_name)
|
76
|
+
KubernetesDeploy.logger.info(
|
77
|
+
"Fetching task runner pod template: '#{template_name}' in namespace: '#{@namespace}'"
|
78
|
+
)
|
79
|
+
|
80
|
+
pod_template = @kubeclient.get_pod_template(template_name, @namespace)
|
81
|
+
|
82
|
+
pod_template.template
|
83
|
+
rescue KubeException => error
|
84
|
+
if error.error_code == 404
|
85
|
+
raise TaskTemplateMissingError.new(template_name, @namespace, @context)
|
86
|
+
else
|
87
|
+
raise
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def build_pod_template(base_template, entrypoint, args, env_vars)
|
92
|
+
KubernetesDeploy.logger.info("Rendering template for task runner pod")
|
93
|
+
|
94
|
+
rendered_template = base_template.dup
|
95
|
+
rendered_template.kind = 'Pod'
|
96
|
+
rendered_template.apiVersion = 'v1'
|
97
|
+
|
98
|
+
container = rendered_template.spec.containers.find { |cont| cont.name == 'task-runner' }
|
99
|
+
|
100
|
+
raise FatalTaskRunError, "Pod spec does not contain a template container called 'task-runner'" if container.nil?
|
101
|
+
|
102
|
+
container.command = entrypoint
|
103
|
+
container.args = args
|
104
|
+
container.env ||= []
|
105
|
+
|
106
|
+
env_args = env_vars.map do |key, value|
|
107
|
+
key, value = env.split('=', 2)
|
108
|
+
{ name: key, value: value }
|
109
|
+
end
|
110
|
+
|
111
|
+
container.env = container.env.map(&:to_h) + env_args
|
112
|
+
|
113
|
+
unique_name = rendered_template.metadata.name + "-" + SecureRandom.hex(8)
|
114
|
+
|
115
|
+
KubernetesDeploy.logger.warn("Name is too long, using '#{unique_name[0..62]}'") if unique_name.length > 63
|
116
|
+
rendered_template.metadata.name = unique_name[0..62]
|
117
|
+
rendered_template.metadata.namespace = @namespace
|
118
|
+
|
119
|
+
rendered_template
|
120
|
+
end
|
121
|
+
|
122
|
+
def validate_pod_spec(pod)
|
123
|
+
f = Tempfile.new(pod.metadata.name)
|
124
|
+
f.write recursive_to_h(pod).to_json
|
125
|
+
f.close
|
126
|
+
|
127
|
+
_out, err, status = Kubectl.run_kubectl(
|
128
|
+
"apply", "--dry-run", "-f", f.path,
|
129
|
+
namespace: @namespace,
|
130
|
+
context: @context
|
131
|
+
)
|
132
|
+
|
133
|
+
unless status.success?
|
134
|
+
raise FatalTaskRunError, "Invalid pod spec: #{err}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def recursive_to_h(struct)
|
139
|
+
if struct.is_a?(Array)
|
140
|
+
return struct.map { |v| v.is_a?(OpenStruct) || v.is_a?(Array) || v.is_a?(Hash) ? recursive_to_h(v) : v }
|
141
|
+
end
|
142
|
+
|
143
|
+
hash = {}
|
144
|
+
|
145
|
+
struct.each_pair do |k, v|
|
146
|
+
recursive_val = v.is_a?(OpenStruct) || v.is_a?(Array) || v.is_a?(Hash)
|
147
|
+
hash[k] = recursive_val ? recursive_to_h(v) : v
|
148
|
+
end
|
149
|
+
hash
|
150
|
+
end
|
151
|
+
end
|
152
|
+
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
|
+
version: 0.6.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-
|
13
|
+
date: 2017-04-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -102,6 +102,7 @@ email:
|
|
102
102
|
executables:
|
103
103
|
- kubernetes-deploy
|
104
104
|
- kubernetes-restart
|
105
|
+
- kubernetes-run
|
105
106
|
extensions: []
|
106
107
|
extra_rdoc_files: []
|
107
108
|
files:
|
@@ -115,6 +116,7 @@ files:
|
|
115
116
|
- bin/setup
|
116
117
|
- exe/kubernetes-deploy
|
117
118
|
- exe/kubernetes-restart
|
119
|
+
- exe/kubernetes-run
|
118
120
|
- kubernetes-deploy.gemspec
|
119
121
|
- lib/kubernetes-deploy.rb
|
120
122
|
- lib/kubernetes-deploy/kubeclient_builder.rb
|
@@ -127,12 +129,14 @@ files:
|
|
127
129
|
- lib/kubernetes-deploy/kubernetes_resource/ingress.rb
|
128
130
|
- lib/kubernetes-deploy/kubernetes_resource/persistent_volume_claim.rb
|
129
131
|
- lib/kubernetes-deploy/kubernetes_resource/pod.rb
|
132
|
+
- lib/kubernetes-deploy/kubernetes_resource/pod_template.rb
|
130
133
|
- lib/kubernetes-deploy/kubernetes_resource/redis.rb
|
131
134
|
- lib/kubernetes-deploy/kubernetes_resource/service.rb
|
132
135
|
- lib/kubernetes-deploy/logger.rb
|
133
136
|
- lib/kubernetes-deploy/resource_watcher.rb
|
134
137
|
- lib/kubernetes-deploy/restart_task.rb
|
135
138
|
- lib/kubernetes-deploy/runner.rb
|
139
|
+
- lib/kubernetes-deploy/runner_task.rb
|
136
140
|
- lib/kubernetes-deploy/ui_helpers.rb
|
137
141
|
- lib/kubernetes-deploy/version.rb
|
138
142
|
homepage: https://github.com/Shopify/kubernetes-deploy
|
@@ -155,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
159
|
version: '0'
|
156
160
|
requirements: []
|
157
161
|
rubyforge_project:
|
158
|
-
rubygems_version: 2.
|
162
|
+
rubygems_version: 2.6.10
|
159
163
|
signing_key:
|
160
164
|
specification_version: 4
|
161
165
|
summary: Kubernetes deploy scripts
|