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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 80b326de6d0dd57558007807fe7441ea72f75c2e
4
- data.tar.gz: 7788d551ece1c5bdafd05c99ea05a4a16a5d4b88
3
+ metadata.gz: 88f4ea27998f9a2958de3c9e2bdf989de7d99110
4
+ data.tar.gz: 2fd156270734c5064cd94d47e4f79d8a734e8658
5
5
  SHA512:
6
- metadata.gz: 3ff01e43e12fa0db1a2649c5f9b410572012f512c59a41445cb106be7b0902c406a3e3a26146fdc2b539ff465f6e2104576ab96ccf27a04d73a1efb2c4ed4384
7
- data.tar.gz: 8614bfdae5d0aab41b98c4eedfb3668f54106dde827429f0397fc894042616e3ea3bb81305d317612aa8a09464b911b55aa34b551c6403ea97c95b4f1ab71ef5
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 typical 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:
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
@@ -30,17 +30,29 @@ module KY
30
30
  end
31
31
  end
32
32
 
33
- desc "from_proc path/to/Procfile output_dir", "generate kubernetes deployment base configs from Procfile"
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
@@ -10,7 +10,7 @@ module KY
10
10
  end
11
11
 
12
12
  def self.generate_env(input1, input2)
13
- new(input1, input2).to_yaml
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 to_yaml
25
- output_hash(config_hsh[data].map {|key, _| config_env(project, key) } + secret_hsh[data].map {|key, _| secret_env(project, key) }).to_yaml
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module KY
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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
 
@@ -0,0 +1,3 @@
1
+ web: bundle exec puma -C ./config/puma.rb
2
+ jobs: bundle exec rake jobs:work
3
+ worker: bundle exec toiler --rails -C config/toiler.yml
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.1.3
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