ky 0.1.3 → 0.2.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/README.md +3 -1
- data/lib/ky.rb +13 -1
- data/lib/ky/cli.rb +13 -1
- data/lib/ky/deploy_generation.rb +78 -0
- data/lib/ky/env_generation.rb +6 -5
- data/lib/ky/manipulation.rb +11 -1
- data/lib/ky/version.rb +1 -1
- data/spec/ky_bin_spec.rb +12 -0
- data/spec/support/Procfile +3 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88f4ea27998f9a2958de3c9e2bdf989de7d99110
|
4
|
+
data.tar.gz: 2fd156270734c5064cd94d47e4f79d8a734e8658
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb7b698da20ddb79a6aba30ee1e4fdba2983cd319bd08dae0325cbacabf4c60c50cfed7aafe5f074a80f3bd1af3d880755d6851eb30821c89adaa6bbaf9abf0e
|
7
|
+
data.tar.gz: 5da3f95409421d0f5b3974283a8cb6187b9174cf043e35268ce979878b942dd2565218fea461d8f43855c982fae8af7bd3f4a3316c69515f644b2a4932588c21
|
data/README.md
CHANGED
@@ -4,8 +4,10 @@
|
|
4
4
|
|
5
5
|
The primary purpose is to automate/DRY up duplication and agreement between multiple deployment YAML files and config/secret yaml files that we saw emerging as we built our kubernetes configuration.
|
6
6
|
|
7
|
+
The full/hopeful use of the tool may be enabled with the new `compile` command which is very much a rough draft at present, but which takes a crack at generating a complete deployment yaml file for every line of a Procfile such as used for Heroku, and a pair of config and secrets files. The secret file can be non-base64 encoded, and compile will generate deployments and a base64 encoded secrets file to a target directory specified. This command uses all the below commands in combination, plus the undocumented `from_proc` command. The command is invoked as:
|
8
|
+
`ky compile Procfile.file config.yml secrets.yml output_dir` and the output directory will be created if necessary. You may pass a namespace to compile which will be reflected in the deployments (and should agree with the config and secrets, though it's not checking they agree at present).
|
7
9
|
|
8
|
-
The
|
10
|
+
The less automated workflow for the tool might start with generating a yaml file of env mappings from a secrets.yml file and a config.yml file, like so:
|
9
11
|
###Example usage
|
10
12
|
Assuming config.yml such as:
|
11
13
|
```
|
data/lib/ky.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'base64'
|
3
3
|
require 'open-uri'
|
4
|
+
require 'fileutils'
|
4
5
|
require_relative 'ky/manipulation'
|
5
6
|
require_relative 'ky/env_generation'
|
6
7
|
require_relative 'ky/deploy_generation'
|
@@ -22,15 +23,26 @@ module KY
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def env(output, input1, input2)
|
25
|
-
output << EnvGeneration.generate_env(input1, input2)
|
26
|
+
output << EnvGeneration.generate_env(input1, input2).to_yaml
|
26
27
|
rescue KY::EnvGeneration::ConflictingProjectError => e
|
27
28
|
$stderr << "Error processing yml files, please provide a config and a secrets file from the same kubernetes project/name"
|
28
29
|
exit(1)
|
29
30
|
end
|
30
31
|
|
31
32
|
def from_proc(proc_path, output_dir)
|
33
|
+
FileUtils.mkdir_p(output_dir)
|
32
34
|
DeployGeneration.new(proc_path, output_dir).call
|
33
35
|
end
|
34
36
|
|
37
|
+
def compile(proc_path, env1path, env2path, output_dir, namespace=DeployGeneration::DEFAULT_NAMESPACE)
|
38
|
+
FileUtils.mkdir_p(output_dir)
|
39
|
+
env_obj = EnvGeneration.new(env1path, env2path)
|
40
|
+
deploys_hash = DeployGeneration.new(proc_path, output_dir, env_obj.project, namespace).to_h
|
41
|
+
deploys_hash.each do |file_path, deploy_hash|
|
42
|
+
File.write(file_path, deploy_hash.merge(env_obj.to_h).to_yaml)
|
43
|
+
end
|
44
|
+
Manipulation.write_configs_encode_if_needed(env_obj.config_hsh, env_obj.secret_hsh, output_dir)
|
45
|
+
end
|
46
|
+
|
35
47
|
|
36
48
|
end
|
data/lib/ky/cli.rb
CHANGED
@@ -30,17 +30,29 @@ module KY
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
desc "from_proc
|
33
|
+
desc "from_proc Procfile.file output_dir", "generate kubernetes deployment base configs from Procfile to output_dir"
|
34
34
|
def from_proc(procfile_path, output_dir)
|
35
35
|
KY.from_proc(procfile_path, output_dir)
|
36
36
|
end
|
37
37
|
|
38
|
+
desc "compile Procfile.file config.yml secrets.yml output_dir", "generate kubernetes deployment configs from Procfile and env files to output_dir, encode secrets if unencoded"
|
39
|
+
method_option :namespace, type: :string, aliases: "-n"
|
40
|
+
def compile(procfile_path, config_or_secrets_path, secrets_or_config_path, output_dir)
|
41
|
+
input_input(config_or_secrets_path, secrets_or_config_path) do |input1, input2|
|
42
|
+
KY.compile(procfile_path, input1, input2, output_dir, options[:namespace])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
38
46
|
private
|
39
47
|
|
40
48
|
def input_output(input1, output1)
|
41
49
|
with(input1, 'r') {|input_object| with(output1, 'w+') { |output_object| yield(input_object, output_object) } }
|
42
50
|
end
|
43
51
|
|
52
|
+
def input_input(input1, input2)
|
53
|
+
with(input1, 'r') {|input_object1| with(input2, 'r') { |input_object2| yield(input_object1, input_object2) } }
|
54
|
+
end
|
55
|
+
|
44
56
|
def with(output, mode)
|
45
57
|
if output.kind_of?(IO)
|
46
58
|
yield output
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module KY
|
2
|
+
class DeployGeneration
|
3
|
+
API_VERSION = "extensions/v1beta1"
|
4
|
+
IMAGE_TYPE = "docker/image"
|
5
|
+
DEFAULT_PULL_POLICY = "Always"
|
6
|
+
DEFAULT_PROJECT_NAME = "global"
|
7
|
+
DEFAULT_NAMESPACE = "default"
|
8
|
+
DEFAULT_REPLICA_COUNT = 1
|
9
|
+
|
10
|
+
#string array meta-trick to avoid naked strings everywhere below, yaml doesn't to_s symbols as desired
|
11
|
+
%w(apiVersion Deployment imagePullPolicy namespace command
|
12
|
+
replicas template labels app kind containers image
|
13
|
+
data metadata name key valueFrom spec template containers env).each do |raw_string|
|
14
|
+
define_method(raw_string.underscore) { raw_string }
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(proc_path, output_dir, project_name=DEFAULT_PROJECT_NAME, current_namespace=DEFAULT_NAMESPACE)
|
18
|
+
@proc_commands = File.read(proc_path).split("\n")
|
19
|
+
.map {|line| line.split(':', 2) }
|
20
|
+
.map {|k, v| [k, ["/bin/bash","-c", v]] }
|
21
|
+
.to_h
|
22
|
+
@output_dir = output_dir
|
23
|
+
@project = project_name
|
24
|
+
@current_namespace = current_namespace
|
25
|
+
end
|
26
|
+
|
27
|
+
def call
|
28
|
+
to_h.each do |file_path, deploy_hash|
|
29
|
+
File.write(file_path, deploy_hash.to_yaml)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_h
|
34
|
+
proc_commands.map do |id, command_array|
|
35
|
+
["#{output_dir}/#{id}.yml", template_hash(id, command_array)]
|
36
|
+
end.to_h
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
attr_reader :proc_commands, :output_dir, :project, :current_namespace
|
41
|
+
|
42
|
+
def template_hash(id, command_array)
|
43
|
+
{api_version => API_VERSION,
|
44
|
+
kind => deployment,
|
45
|
+
metadata =>{ name => id, namespace => current_namespace},
|
46
|
+
spec =>
|
47
|
+
{replicas => replica_count,
|
48
|
+
template =>
|
49
|
+
{
|
50
|
+
metadata => {
|
51
|
+
labels =>{ app =>"#{project}-#{id}"}
|
52
|
+
},
|
53
|
+
spec => {
|
54
|
+
containers =>
|
55
|
+
[
|
56
|
+
{
|
57
|
+
name => id,
|
58
|
+
image => IMAGE_TYPE,
|
59
|
+
image_pull_policy => pull_policy,
|
60
|
+
command => command_array
|
61
|
+
}
|
62
|
+
]
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def replica_count
|
70
|
+
ENV['REPLICA_COUNT'] || DEFAULT_REPLICA_COUNT
|
71
|
+
end
|
72
|
+
|
73
|
+
def pull_policy
|
74
|
+
ENV['PULL_POLICY'] || DEFAULT_PULL_POLICY
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/lib/ky/env_generation.rb
CHANGED
@@ -10,7 +10,7 @@ module KY
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.generate_env(input1, input2)
|
13
|
-
new(input1, input2).
|
13
|
+
new(input1, input2).to_h
|
14
14
|
end
|
15
15
|
|
16
16
|
attr_reader :config_hsh, :secret_hsh
|
@@ -21,16 +21,17 @@ module KY
|
|
21
21
|
raise ConflictingProjectError.new("Config and Secret metadata names do not agree") unless secret_hsh[metadata][name] == project
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
output_hash(config_hsh[data].map {|key, _| config_env(project, key) } + secret_hsh[data].map {|key, _| secret_env(project, key) })
|
24
|
+
def to_h
|
25
|
+
output_hash(config_hsh[data].map {|key, _| config_env(project, key) } + secret_hsh[data].map {|key, _| secret_env(project, key) })
|
26
26
|
end
|
27
27
|
|
28
|
-
private
|
29
|
-
|
30
28
|
def project
|
31
29
|
config_hsh[metadata][name]
|
32
30
|
end
|
33
31
|
|
32
|
+
private
|
33
|
+
|
34
|
+
|
34
35
|
def config_env(project, kebab_version)
|
35
36
|
env_map(config_map_key_ref, project, kebab_version)
|
36
37
|
end
|
data/lib/ky/manipulation.rb
CHANGED
@@ -3,7 +3,7 @@ module KY
|
|
3
3
|
module Manipulation
|
4
4
|
DEFAULT_DATA_KEY = 'data'
|
5
5
|
MAGIC_DELIMITER = '@'
|
6
|
-
|
6
|
+
BASE_64_DETECTION_REGEX = /^([A-Za-z0-9+]{4})*([A-Za-z0-9+]{4}|[A-Za-z0-9+]{3}=|[A-Za-z0-9+]{2}==)$/
|
7
7
|
class << self
|
8
8
|
def merge_yaml(input1, input2)
|
9
9
|
combined = {}
|
@@ -41,6 +41,16 @@ module KY
|
|
41
41
|
def obscured_data_key
|
42
42
|
ENV['DATA_KEY'] || DEFAULT_DATA_KEY
|
43
43
|
end
|
44
|
+
|
45
|
+
def write_configs_encode_if_needed(config_hsh, secret_hsh, output_path)
|
46
|
+
if secret_hsh[obscured_data_key].values.detect {|value| BASE_64_DETECTION_REGEX =~ value }
|
47
|
+
File.write("#{output_path}/secret.yml", secret_hsh.to_yaml)
|
48
|
+
else
|
49
|
+
File.write("#{output_path}/secret.yml", code_yaml(StringIO.new(secret_hsh.to_yaml), :encode))
|
50
|
+
end
|
51
|
+
File.write("#{output_path}/config.yml", config_hsh.to_yaml)
|
52
|
+
end
|
53
|
+
|
44
54
|
end
|
45
55
|
end
|
46
56
|
end
|
data/lib/ky/version.rb
CHANGED
data/spec/ky_bin_spec.rb
CHANGED
@@ -65,6 +65,18 @@ describe "cli commands" do
|
|
65
65
|
expect(File.exists?("#{tmpdir}/web.yml")).to be true
|
66
66
|
expect(File.exists?("#{tmpdir}/worker.yml")).to be true
|
67
67
|
expect(File.exists?("#{tmpdir}/jobs.yml")).to be true
|
68
|
+
`rm -r #{tmpdir}`
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "compiles Procfile and env secrets/configs into entire deployments" do
|
73
|
+
let(:tmpdir) { 'spec/support/tmpdir' }
|
74
|
+
it "to directory" do
|
75
|
+
KY::Cli.new.compile('spec/support/Procfile', 'spec/support/config.yml', 'spec/support/decoded.yml', tmpdir)
|
76
|
+
expect(File.exists?("#{tmpdir}/web.yml")).to be true
|
77
|
+
expect(File.exists?("#{tmpdir}/worker.yml")).to be true
|
78
|
+
expect(File.exists?("#{tmpdir}/jobs.yml")).to be true
|
79
|
+
`rm -r #{tmpdir}`
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ky
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Glusman
|
@@ -104,11 +104,13 @@ files:
|
|
104
104
|
- ky.gemspec
|
105
105
|
- lib/ky.rb
|
106
106
|
- lib/ky/cli.rb
|
107
|
+
- lib/ky/deploy_generation.rb
|
107
108
|
- lib/ky/env_generation.rb
|
108
109
|
- lib/ky/manipulation.rb
|
109
110
|
- lib/ky/version.rb
|
110
111
|
- spec/ky_bin_spec.rb
|
111
112
|
- spec/spec_helper.rb
|
113
|
+
- spec/support/Procfile
|
112
114
|
- spec/support/config.yml
|
113
115
|
- spec/support/decoded.yml
|
114
116
|
- spec/support/encoded.yml
|