ky 0.3.3 → 0.4.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: 2533ddef5c84d343f63a450906ff204af9d8fba3
4
- data.tar.gz: fa69c9106ed34e68b7d37b0ae42d6a038133967d
3
+ metadata.gz: b1de2efb87f781886061141962646b113a33da52
4
+ data.tar.gz: c51ee92b728200441a515f09953a2a25ac71d494
5
5
  SHA512:
6
- metadata.gz: 56c897b7045750693622757feeac1920b395a17258d92096151811a5df8f6dc59dcd1f2447a83c5733a92a630c84410174fe65216ee6d68c06b654d27e5d5254
7
- data.tar.gz: 3bd1693ec6df03cd29733abdda68369b85e6ee5442402a2a2331e14a666f46df874d9ad7655974753287698fed631ada2d5968601dd6fc1cdfab0d9551ef39a0
6
+ metadata.gz: 435791205d319456386a1b81774768ef80618225a15a55edca06839cdd1905bf4ad91cc0ee8e654ef5ed6d8a4bd032d95b4c878b103e4213aa96280325479064
7
+ data.tar.gz: 5b41cec9ba39bbf3b67cc5e7bfd102d5dfebc45992550031032500d164270fd8bf8053dbf7ee18576e1affab37c07c590f55dd4748a1bb4069714ed6f708af7b
data/README.md CHANGED
@@ -9,7 +9,7 @@ The full/hopeful use of the tool may be enabled with the new `compile` command w
9
9
  The command is invoked as:
10
10
  `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). The arguments are all configurable via the configuration file as well, so it can in practice be invoked as `ky compile` or `ky compile --envioronment stg` if you have a configuration file correctly present as described below.
11
11
 
12
- Configuration begins with a config file in the project working directory, or in your home directory if you wish to share across several projects. Unfortunately there are several competing conventions for configuration files, the traditional dot-file configuration convention and newer, more visible Capitalfile configuration. KY is a lubricant, and has no opinion, and therefore currently supports naming your configuration file `.ky.yml`, `.ky.yaml`, or `Lubefile` or `Kyfile`. The default configuration, if this file is not found, is as follows:
12
+ Configuration begins with a config file in the project working directory, or in your home directory if you wish to share across several projects. Unfortunately there are several competing conventions for configuration files, the traditional dot-file configuration convention and newer, more visible Capitalfile configuration. KY is a lubricant, and has no opinion, and therefore currently supports naming your configuration file `.ky.yml`, `.ky.yaml`, or `Lubefile`. The default configuration, if this file is not found, is as follows:
13
13
  ```
14
14
  environments: []
15
15
  replica_count: 1
data/lib/ky.rb CHANGED
@@ -7,10 +7,11 @@ require_relative 'ky/manipulation'
7
7
  require_relative 'ky/env_generation'
8
8
  require_relative 'ky/template'
9
9
  require_relative 'ky/deploy_generation'
10
+ require_relative 'ky/hash'
10
11
 
11
12
 
12
13
  class KY
13
- CONFIG_FILE_NAMES = [".ky.yml", ".ky.yaml", "Lubefile", "Kyfile"]
14
+ CONFIG_FILE_NAMES = [".ky.yml", ".ky.yaml", "Lubefile"]
14
15
  CONFIG_LOCATIONS = ["#{Dir.pwd}/", "#{Dir.home}/"]
15
16
  DEFAULT_CONFIG = {
16
17
  environments: [],
@@ -22,10 +23,17 @@ class KY
22
23
  api_version: "extensions/v1beta1",
23
24
  inline_config: true,
24
25
  inline_secret: false,
25
- project_name: "global"
26
+ project_name: "global",
27
+ force_configmap_apply: false
26
28
  }.with_indifferent_access
27
29
 
28
- attr_accessor :environment, :image_tag
30
+ attr_reader :opts, :configuration
31
+
32
+ def initialize(opts={})
33
+ @opts=opts
34
+ @configuration = build_configuration
35
+ define_methods_from_config(configuration)
36
+ end
29
37
 
30
38
  def decode(output, input)
31
39
  output << Manipulation.code_yaml(input, :decode)
@@ -40,44 +48,47 @@ class KY
40
48
  end
41
49
 
42
50
  def env(output, input1, input2)
43
- output << EnvGeneration.generate_env(self, input1, input2).to_yaml
51
+ output << EnvGeneration.generate_env(self, input1, input2).to_plain_yaml
44
52
  rescue KY::EnvGeneration::ConflictingProjectError => e
45
53
  $stderr << "Error processing yml files, please provide a config and a secrets file from the same kubernetes project/name"
46
54
  exit(1)
47
55
  end
48
56
 
49
- def compile(proc_path, env1path, env2path, base_output_dir, namespace=DeployGeneration::DEFAULT_NAMESPACE)
50
- full_output_dir = Pathname.new(base_output_dir).join(environment.to_s).to_s
57
+ def compile(proc_path, env1path, env2path, base_output_dir)
58
+ full_output_dir = Pathname.new(base_output_dir).join(configuration[:environment].to_s).to_s
51
59
  FileUtils.mkdir_p(full_output_dir)
52
60
  env_obj = EnvGeneration.new(self, env1path, env2path)
53
- deploys_hash = DeployGeneration.new(self, proc_path, full_output_dir, env_obj.project, namespace).to_h
61
+ deploys_hash = DeployGeneration.new(self, proc_path, full_output_dir, env_obj.project, configuration[:namespace]).to_h
54
62
  deploys_hash.each do |file_path, deploy_hash|
55
- File.write(file_path, Manipulation.merge_hash(deploy_hash, env_obj.to_h).to_yaml)
63
+ File.write(file_path, Manipulation.merge_hash(deploy_hash, env_obj.to_h).to_plain_yaml)
56
64
  end
57
65
  Manipulation.write_configs_encode_if_needed(env_obj.config_hsh, env_obj.secret_hsh, full_output_dir, configuration[:project_name])
58
66
  end
59
67
 
60
- def configuration
61
- @config ||= begin
62
- config = DEFAULT_CONFIG.merge(config_file_location ? YAML.load(File.read(config_file_location)) : {})
63
- config = config.merge(current_environment_hash(config)["configuration"] || {})
64
- define_methods_from_config(config)
65
- config
66
- end
68
+ def build_configuration
69
+ config = if config_file_location
70
+ YAML.load(File.read(config_file_location))
71
+ else
72
+ DEFAULT_CONFIG
73
+ end.with_indifferent_access
74
+ config.merge!(current_environment_hash(config))
75
+ config
67
76
  end
68
77
 
69
78
  def deploy_merge(id)
70
- return {} unless configuration["merge"]
71
- configuration["merge"][id].to_h
79
+ return {} unless configuration[:merge]
80
+ configuration[:merge][id].to_h
72
81
  end
73
82
 
74
- def current_environment_hash(partial_config=nil)
75
- hsh = YAML.load(File.read(KY.environment_files(partial_config).find {|file| file.match(KY.environment) })) rescue {} # ugh, this find is accident waiting to happen, REFACTOR/RETHINK!
76
- image_tag ? hsh.merge("configuration" => {"image_tag" => image_tag}) : hsh
83
+ def current_environment_hash(partial_config)
84
+ current_config = partial_config || configuration
85
+ env_file_path = environment_files(current_config).find {|file| file.match(opts[:environment] || current_config[:environment]) } if opts[:environment] || current_config[:environment] # ugh, this find is accident waiting to happen, REFACTOR/RETHINK!
86
+ hsh = env_file_path ? YAML.load(File.read(env_file_path)).with_indifferent_access : {}
87
+ (hsh[:configuration] ? hsh[:configuration].merge(opts) : hsh.merge(opts)).with_indifferent_access
77
88
  end
78
89
 
79
- def environment_files(partial_config=nil)
80
- environments = (partial_config || configuration)['environments'].flat_map {|env| ["#{env}.yml", "#{env}.yaml"]}
90
+ def environment_files(partial_config)
91
+ environments = (partial_config || configuration)[:environments].flat_map {|env| ["#{env}.yml", "#{env}.yaml"]}
81
92
  (CONFIG_LOCATIONS * environments.count).zip(environments).map(&:join).select {|path| File.exist?(path) && !File.directory?(path) }
82
93
  end
83
94
 
data/lib/ky/cli.rb CHANGED
@@ -43,16 +43,14 @@ class KY
43
43
  method_option :environment, type: :string, aliases: "-e"
44
44
  method_option :image_tag, type: :string, aliases: "-t"
45
45
  def compile(procfile_path=nil, config_or_secrets_path=nil, secrets_or_config_path=nil, output_dir=nil)
46
- instance = KY.new
47
- instance.environment = options[:environment]
48
- instance.image_tag = options[:image_tag]
46
+ instance = KY.new(options.with_indifferent_access)
49
47
  procfile_path ||= instance.configuration['procfile_path']
50
48
  config_or_secrets_path ||= instance.configuration['config_path'] || instance.configuration['secret_path']
51
49
  secrets_or_config_path ||= instance.configuration['secret_path'] || instance.configuration['config_path']
52
50
  output_dir ||= instance.configuration['output_dir']
53
51
  raise MissingParametersError unless procfile_path && config_or_secrets_path && secrets_or_config_path && output_dir
54
52
  input_input(config_or_secrets_path, secrets_or_config_path) do |input1, input2|
55
- instance.compile(procfile_path, input1, input2, output_dir, options[:namespace])
53
+ instance.compile(procfile_path, input1, input2, output_dir)
56
54
  end
57
55
  end
58
56
 
@@ -14,7 +14,7 @@ class KY
14
14
 
15
15
  def call
16
16
  to_h.each do |file_path, deploy_hash|
17
- File.write(file_path, deploy_hash.to_yaml)
17
+ File.write(file_path, deploy_hash.to_plain_yaml)
18
18
  end
19
19
  end
20
20
 
@@ -42,7 +42,7 @@ class KY
42
42
  def template_hash(id, command_array)
43
43
  app_name = instance.configuration['app_name'] || "#{project_name}-#{id}"
44
44
  template_context = Template.context(app_name: app_name, id: id, command_array: command_array)
45
- Manipulation.merge_hash(
45
+ tmp = Manipulation.merge_hash(
46
46
  YAML.load(
47
47
  ERB.new(deployment_yaml).result(template_context)
48
48
  ),
@@ -1,5 +1,6 @@
1
1
  require 'active_support'
2
2
  require 'active_support/core_ext'
3
+ require 'securerandom'
3
4
  class KY
4
5
  class EnvGeneration
5
6
  ConflictingProjectError = Class.new(StandardError)
@@ -23,7 +24,7 @@ class KY
23
24
  end
24
25
 
25
26
  def to_h
26
- output_hash(config_hsh[data].map {|key, value| config_env(key, value) } + secret_hsh[data].map {|key, value| secret_env(key, value) })
27
+ output_hash(config_hsh[data].map {|key, value| config_env(key, value) } + secret_hsh[data].map {|key, value| secret_env(key, value) } + force_config)
27
28
  end
28
29
 
29
30
  def project
@@ -32,6 +33,10 @@ class KY
32
33
 
33
34
  private
34
35
 
36
+ def force_config
37
+ return [] unless instance.configuration[:force_configmap_apply]
38
+ [inline_env_map(config_map_key_ref, "force-configmap-apply", SecureRandom.hex)]
39
+ end
35
40
 
36
41
  def config_env(kebab_version, value)
37
42
  inline_config? ? inline_env_map(config_map_key_ref, kebab_version, value) : env_map(config_map_key_ref, kebab_version)
data/lib/ky/hash.rb ADDED
@@ -0,0 +1,17 @@
1
+ class Hash # specifically for HashWithIndifferentAccess < Hash, instead of plain to_yaml
2
+ def to_plain_yaml(opts = {}) # which yields ugly !map:ActiveSupport::HashWithIndifferentAccess
3
+ self.to_hash_recursive.to_yaml(opts)
4
+ end
5
+
6
+ def to_hash_recursive
7
+ result = self.to_h
8
+ result.each do |key, value|
9
+ if(value.kind_of? Hash)
10
+ result[key] = value.to_hash_recursive.to_h
11
+ elsif (value.kind_of? Array)
12
+ result[key] = value.map { |item| item.kind_of?(Hash) ? item.to_hash_recursive : item }
13
+ end
14
+ end
15
+ result
16
+ end
17
+ end
@@ -9,7 +9,7 @@ class KY
9
9
  combined = {}
10
10
  YAML.load(input1.read).tap { |hsh|
11
11
  merge_hash(hsh, YAML.load(input2.read))
12
- }.to_yaml
12
+ }.to_plain_yaml
13
13
  end
14
14
 
15
15
  def merge_hash(hsh1, hsh2)
@@ -22,7 +22,7 @@ class KY
22
22
  hsh[obscured_data_key] = data.map { |key, value|
23
23
  [key, handle_coding(direction, value)]
24
24
  }.to_h
25
- }.to_yaml
25
+ }.to_plain_yaml
26
26
  end
27
27
 
28
28
  def handle_coding(direction, value)
@@ -47,12 +47,12 @@ class KY
47
47
  end
48
48
 
49
49
  def write_configs_encode_if_needed(config_hsh, secret_hsh, output_path, project_name)
50
- if secret_hsh[obscured_data_key].values.detect {|value| BASE_64_DETECTION_REGEX =~ value }
51
- File.write("#{output_path}/#{project_name}.secret.yml", secret_hsh.to_yaml)
50
+ if secret_hsh[obscured_data_key].values.all? {|value| BASE_64_DETECTION_REGEX =~ value }
51
+ File.write("#{output_path}/#{project_name}.secret.yml", secret_hsh.to_plain_yaml)
52
52
  else
53
- File.write("#{output_path}/#{project_name}.secret.yml", code_yaml(StringIO.new(secret_hsh.to_yaml), :encode))
53
+ File.write("#{output_path}/#{project_name}.secret.yml", code_yaml(StringIO.new(secret_hsh.to_plain_yaml), :encode))
54
54
  end
55
- File.write("#{output_path}/#{project_name}.configmap.yml", config_hsh.to_yaml)
55
+ File.write("#{output_path}/#{project_name}.configmap.yml", config_hsh.to_plain_yaml)
56
56
  end
57
57
 
58
58
  end
data/lib/ky/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class KY
2
- VERSION = "0.3.3"
2
+ VERSION = "0.4.0"
3
3
  end
data/spec/ky_bin_spec.rb CHANGED
@@ -61,11 +61,11 @@ describe "ky cli" do
61
61
  end
62
62
  end
63
63
 
64
- describe "primary cli command" do
64
+ describe "primary cli command generates and" do
65
65
  let(:instance) { KY.new }
66
66
  let(:fake_tag) { 'fake_tag' }
67
67
  let(:tmpdir) { 'spec/support/tmpdir' }
68
- after { `rm -r #{tmpdir}` ; instance.environment = nil ; instance.image_tag = nil }
68
+ after { `rm -r #{tmpdir}` }
69
69
  describe "compiles Procfile and env secrets/configs into entire deployments" do
70
70
  it "to directory" do
71
71
  KY::Cli.new.compile('spec/support/Procfile', 'spec/support/config.yml', 'spec/support/decoded.yml', tmpdir)
@@ -99,7 +99,6 @@ describe "ky cli" do
99
99
  end
100
100
 
101
101
  describe "uses image_tag when passed in as option" do
102
- let(:tmpdir) { 'spec/support/tmp2dir' }
103
102
  it "to directory" do
104
103
  instance = KY::Cli.new
105
104
  instance.options = {image_tag: fake_tag}
@@ -109,6 +108,52 @@ describe "ky cli" do
109
108
  end
110
109
  end
111
110
 
111
+ describe "uses namespace when passed in as option" do
112
+ it "to directory" do
113
+ instance = KY::Cli.new
114
+ instance.options = {namespace: fake_tag}
115
+ instance.compile('spec/support/Procfile', 'spec/support/config.yml', 'spec/support/decoded.yml', tmpdir)
116
+ expect(File.exists?("#{tmpdir}/web.deployment.yml")).to be true
117
+ expect(File.read("#{tmpdir}/web.deployment.yml")).to match(fake_tag)
118
+ end
119
+ end
120
+
121
+ describe "merges yaml to named ids templates when compiling" do
122
+ before { `cp spec/support/Lubefile .`}
123
+ after { `rm Lubefile` }
124
+ it "to directory" do
125
+ instance = KY::Cli.new
126
+ instance.compile('spec/support/Procfile', 'spec/support/config.yml', 'spec/support/decoded.yml', tmpdir)
127
+ expect(File.exists?("#{tmpdir}/web.deployment.yml")).to be true
128
+ expect(File.exists?("#{tmpdir}/jobs.deployment.yml")).to be true
129
+ expect(File.read("#{tmpdir}/web.deployment.yml")).to match('port')
130
+ expect(File.read("#{tmpdir}/jobs.deployment.yml")).not_to match('port')
131
+ end
132
+ end
133
+
134
+ describe "serializes yaml without reference to HashWithIndifferentAccess" do
135
+ before { `cp spec/support/Lubefile .`}
136
+ after { `rm Lubefile` }
137
+ it "to directory" do
138
+ instance = KY::Cli.new
139
+ instance.compile('spec/support/Procfile', 'spec/support/config.yml', 'spec/support/decoded.yml', tmpdir)
140
+ expect(File.exists?("#{tmpdir}/web.deployment.yml")).to be true
141
+ expect(File.read("#{tmpdir}/web.deployment.yml")).not_to match('HashWithIndifferentAccess')
142
+ end
143
+ end
144
+
145
+ describe "adds random inline value if force_configmap_apply is true" do
146
+ before { `cp spec/support/Lubefile .`}
147
+ after { `rm Lubefile` }
148
+ it "to directory" do
149
+ instance = KY::Cli.new
150
+ instance.options = {force_configmap_apply: true}
151
+ instance.compile('spec/support/Procfile', 'spec/support/config.yml', 'spec/support/decoded.yml', tmpdir)
152
+ expect(File.exists?("#{tmpdir}/web.deployment.yml")).to be true
153
+ expect(File.read("#{tmpdir}/web.deployment.yml")).to match('FORCE_CONFIGMAP_APPLY')
154
+ end
155
+ end
156
+
112
157
  end
113
158
  end
114
159
 
@@ -0,0 +1,23 @@
1
+ environments: [dev, stg, prd]
2
+ deployment: examples/deployment_base.yml
3
+ replica_count: 2
4
+ image_pull_policy: "IfNotPresent"
5
+ namespace: "default"
6
+ image: "organization/project"
7
+ image_tag: "latest"
8
+ api_version: "extensions/v1beta1"
9
+ inline_config: true
10
+ inline_secret: false
11
+ project_name: "connect"
12
+ config_path: "spec/support/config.yml"
13
+ secret_path: "spec/support/decoded.yml"
14
+ output_dir: "namespaces"
15
+ procfile_path: "spec/support/Procfile"
16
+ merge:
17
+ web:
18
+ spec:
19
+ template:
20
+ spec:
21
+ containers:
22
+ - ports:
23
+ - containerPort: 3000
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ky
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Glusman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-27 00:00:00.000000000 Z
11
+ date: 2016-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -90,11 +90,13 @@ files:
90
90
  - lib/ky/cli.rb
91
91
  - lib/ky/deploy_generation.rb
92
92
  - lib/ky/env_generation.rb
93
+ - lib/ky/hash.rb
93
94
  - lib/ky/manipulation.rb
94
95
  - lib/ky/template.rb
95
96
  - lib/ky/version.rb
96
97
  - spec/ky_bin_spec.rb
97
98
  - spec/spec_helper.rb
99
+ - spec/support/Lubefile
98
100
  - spec/support/Procfile
99
101
  - spec/support/config.yml
100
102
  - spec/support/decoded.yml