kerbi 0.0.1
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 +7 -0
- data/bin/kerbi +3 -0
- data/boilerplate/Gemfile.erb +6 -0
- data/boilerplate/kerbifile.rb.erb +9 -0
- data/boilerplate/values.yaml.erb +1 -0
- data/lib/cli/base.rb +83 -0
- data/lib/cli/project_handler.rb +17 -0
- data/lib/cli/root_handler.rb +47 -0
- data/lib/cli/values_handler.rb +11 -0
- data/lib/config/cli_opts.rb +50 -0
- data/lib/config/cli_schema.rb +104 -0
- data/lib/config/globals.rb +7 -0
- data/lib/config/manager.rb +36 -0
- data/lib/kerbi.rb +43 -0
- data/lib/main/code_gen.rb +131 -0
- data/lib/main/mixer.rb +235 -0
- data/lib/main/state_manager.rb +47 -0
- data/lib/mixins/mixer.rb +79 -0
- data/lib/utils/cli.rb +59 -0
- data/lib/utils/helm.rb +64 -0
- data/lib/utils/kubectl.rb +58 -0
- data/lib/utils/misc.rb +41 -0
- data/lib/utils/mixing.rb +181 -0
- data/lib/utils/values.rb +133 -0
- data/spec/fixtures/embedding.yaml.erb +3 -0
- data/spec/main/examples_spec.rb +12 -0
- data/spec/main/mixer_spec.rb +126 -0
- data/spec/main/project_code_gen_spec.rb +25 -0
- data/spec/main/state_manager_spec.rb +84 -0
- data/spec/mixins/mixer_mixin_spec.rb +55 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/utils/helm_spec.rb +114 -0
- data/spec/utils/misc_utils_spec.rb +22 -0
- data/spec/utils/mixing_utils_spec.rb +209 -0
- data/spec/utils/state_utils.rb +15 -0
- data/spec/utils/values_utils_spec.rb +146 -0
- metadata +170 -0
data/lib/utils/mixing.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
module Kerbi
|
2
|
+
module Utils
|
3
|
+
module Mixing
|
4
|
+
|
5
|
+
##
|
6
|
+
# Parses and interpolates YAML or JSON dicts and outputs
|
7
|
+
# their symbol-keyed Hash forms.
|
8
|
+
# @param [String] yaml_str plain yaml, json, or erb string
|
9
|
+
# @param [Hash] extras additional hash passed to ERB
|
10
|
+
# @return [Array<Hash>] list of inflated dicts
|
11
|
+
def self.yaml_str_to_dicts(yaml_str, opts={})
|
12
|
+
interpolated_yaml = self.interpolate_erb_string(yaml_str, **opts)
|
13
|
+
hashes = YAML.load_stream(interpolated_yaml)
|
14
|
+
self.clean_and_filter_dicts(hashes, **opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Loads a YAML/JSON/ERB file, parses it, interpolates it,
|
19
|
+
# and returns the resulting dicts.
|
20
|
+
# @param [String] fname simplified or absolute path of file
|
21
|
+
# @param [Hash] extras additional hash passed to ERB
|
22
|
+
# @return [Array<Hash>] list of res-hashes
|
23
|
+
def self.yaml_file_to_dicts(fname, opts={})
|
24
|
+
contents = File.read(fname)
|
25
|
+
begin
|
26
|
+
self.yaml_str_to_dicts(contents, **opts)
|
27
|
+
rescue Exception => e
|
28
|
+
STDERR.puts "Exception below from file #{fname}"
|
29
|
+
raise e
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Performs ERB interpolation on an ERB string, and returns
|
35
|
+
# the interpolated string.
|
36
|
+
# @param [String] yaml_str contents of a yaml or yaml.erb
|
37
|
+
# @param [Hash] opts an additional hash available to ERB
|
38
|
+
# @return [String] the interpolated string
|
39
|
+
def self.interpolate_erb_string(yaml_str, opts={})
|
40
|
+
final_binding = opts[:src_binding] || binding
|
41
|
+
final_binding.local_variable_set(:extras, opts[:extras] || {})
|
42
|
+
ERB.new(yaml_str).result(final_binding)
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Loads, interpolates, and parses all dicts found in all YAML/JSON/ERB
|
47
|
+
# files in a given directory.
|
48
|
+
# @param [String] dir relative or absolute path of the directory
|
49
|
+
# @param [Array<String>] file_blacklist list of filenames to avoid
|
50
|
+
# @return [Array<Hash>] array of processed dicts
|
51
|
+
def self.yamls_in_dir_to_dicts(pwd, dir, opts={})
|
52
|
+
file_blacklist = opts.delete(:file_blacklist)
|
53
|
+
blacklist = file_blacklist || []
|
54
|
+
|
55
|
+
dir ||= pwd
|
56
|
+
dir = "#{pwd}/#{dir}" if dir && pwd && dir.start_with?(".")
|
57
|
+
yaml_files = Dir["#{dir}/*.yaml"]
|
58
|
+
erb_files = Dir["#{dir}/*.yaml.erb"]
|
59
|
+
|
60
|
+
(yaml_files + erb_files).map do |fname|
|
61
|
+
is_blacklisted = blacklist.include?(File.basename(fname))
|
62
|
+
unless is_blacklisted
|
63
|
+
self.yaml_file_to_dicts(fname, **opts)
|
64
|
+
end
|
65
|
+
end.compact.flatten
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Turns hashes into symbol-keyed hashes,
|
70
|
+
# and applies white/blacklisting based on filters supplied
|
71
|
+
# @param [Array<Hash>] dicts list of inflated hashes
|
72
|
+
# @param [Array<String>] white_rules list/single k8s res ID to whitelist
|
73
|
+
# @param [Array<String>] black_rules list/single k8s res ID to blacklist
|
74
|
+
# @return [Array<Hash>] list of clean and filtered hashes
|
75
|
+
def self.clean_and_filter_dicts(dicts, opts={})
|
76
|
+
white_rules = opts[:white_rules] || opts[:only]
|
77
|
+
black_rules = opts[:black_rules] || opts[:except]
|
78
|
+
_dicts = self.hash_to_cloned_hashes(dicts)
|
79
|
+
_dicts = _dicts.compact.map(&:deep_symbolize_keys).to_a
|
80
|
+
_dicts = self.select_res_dicts_whitelist(_dicts, white_rules)
|
81
|
+
_dicts = self.select_res_dicts_blacklist(_dicts, black_rules)
|
82
|
+
self.sanitize_res_dict_list(_dicts)
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.sanitize_res_dict_list(res_dicts)
|
86
|
+
pushable_list = nil
|
87
|
+
if res_dicts.is_a?(Array)
|
88
|
+
pushable_list = res_dicts
|
89
|
+
elsif res_dicts.is_a?(Hash)
|
90
|
+
pushable_list = [res_dicts]
|
91
|
+
end
|
92
|
+
|
93
|
+
if pushable_list.present?
|
94
|
+
#noinspection RubyNilAnalysis
|
95
|
+
pushable_list.select do |item|
|
96
|
+
item.present? && item.is_a?(Hash)
|
97
|
+
end.map(&:deep_symbolize_keys).compact
|
98
|
+
else
|
99
|
+
[]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# @return [Array<Hash>] list of clean and filtered hashes
|
105
|
+
def self.hash_to_cloned_hashes(hashes)
|
106
|
+
if !hashes.is_a?(Array)
|
107
|
+
[hashes]
|
108
|
+
else
|
109
|
+
hashes
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Returns res dicts that match one or more rules
|
115
|
+
# @param [Array<Hash>] res_dicts k8s res-hashes
|
116
|
+
# @param [Array<String>] rule_dicts list of simple k8s res-ids by which to filter
|
117
|
+
# @return [Array<Hash>] list of clean and filtered hashes
|
118
|
+
def self.select_res_dicts_whitelist(res_dicts, rule_dicts)
|
119
|
+
_res_dicts = res_dicts.compact.map(&:deep_symbolize_keys).to_a
|
120
|
+
return _res_dicts if (rule_dicts || []).compact.empty?
|
121
|
+
_res_dicts.select do |res_dict|
|
122
|
+
rule_dicts.any? do |rule_dict|
|
123
|
+
self.res_dict_matches_rule?(res_dict, rule_dict)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Returns res dicts that match zero rules
|
130
|
+
# @param [Array<Hash>] res_dicts k8s res-hashes
|
131
|
+
# @param [Array<String>] rule_dicts list of simple k8s res-ids by which to filter
|
132
|
+
# @return [Array<Hash>] list of clean and filtered hashes
|
133
|
+
def self.select_res_dicts_blacklist(res_dicts, rule_dicts)
|
134
|
+
_res_dicts = res_dicts.compact.map(&:deep_symbolize_keys).to_a
|
135
|
+
return _res_dicts if (rule_dicts || []).compact.empty?
|
136
|
+
_res_dicts.reject do |res_dict|
|
137
|
+
rule_dicts.any? do |rule_dict|
|
138
|
+
self.res_dict_matches_rule?(res_dict, rule_dict)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Checks whether a dict, assumed to be a Kubernetes resource,
|
145
|
+
# matches a kerbi resource selection rule.
|
146
|
+
# @param [Hash] res_dict the Kubernetes-style resource dict
|
147
|
+
# @param [Hash] rule_dict the kerbi resource selector dict
|
148
|
+
# @return [TrueClass, FalseClass] true if the selector selects the resource
|
149
|
+
def self.res_dict_matches_rule?(res_dict, rule_dict)
|
150
|
+
return false unless res_dict.is_a?(Hash)
|
151
|
+
return false unless res_dict.present?
|
152
|
+
|
153
|
+
return false unless rule_dict.is_a?(Hash)
|
154
|
+
return false unless rule_dict.present?
|
155
|
+
|
156
|
+
target_kind = rule_dict[:kind].presence
|
157
|
+
target_name = rule_dict[:name].presence
|
158
|
+
if !target_kind || self.str_cmp(target_kind, res_dict[:kind])
|
159
|
+
wild = !target_name || target_name == "*"
|
160
|
+
res_name = res_dict[:metadata]&.[](:name)
|
161
|
+
wild || self.str_cmp(target_name, res_name)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
##
|
166
|
+
# Checks whether the value matching string given in a kerbi
|
167
|
+
# resource selector dict matches the value of the attribute
|
168
|
+
# in the Kubernetes-style resource dict. Works by performing
|
169
|
+
# a regex check on the two values with a full stop e.g ^$.
|
170
|
+
# @param [String] rule_str the regex to test the candidate string
|
171
|
+
# @param [String] actual_str the candidate string being tested
|
172
|
+
# @return [TrueClass, FalseClass] true if the rule matches the input
|
173
|
+
def self.str_cmp(rule_str, actual_str)
|
174
|
+
final_regex = Regexp.new("^#{rule_str}$")
|
175
|
+
match_result = actual_str =~ final_regex
|
176
|
+
!match_result.nil?
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
data/lib/utils/values.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
module Kerbi
|
2
|
+
module Utils
|
3
|
+
##
|
4
|
+
# Utilities module for all value loading functionality.
|
5
|
+
module Values
|
6
|
+
|
7
|
+
DEFAULT_VALUE_PATH = "values"
|
8
|
+
|
9
|
+
def self.from_files(fname_exprs, **opts)
|
10
|
+
final_paths = resolve_fname_exprs(fname_exprs, **opts)
|
11
|
+
load_yaml_files(final_paths)
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Resolves each filename expression given, returning an array
|
16
|
+
# of absolute paths. Automatically prepends the default values filename
|
17
|
+
# - values.yaml - to the list and does not complain if it does not exist.
|
18
|
+
# If two or more filename expressions resolve to the same absolute path,
|
19
|
+
# only one copy will be in the list.
|
20
|
+
# @param [Array<String>] fname_exprs cli-level values file path names
|
21
|
+
# @param [Hash] opts downstream options for file-loading methods
|
22
|
+
# @return [Array<String>] list of unique absolute filenames
|
23
|
+
def self.resolve_fname_exprs(fname_exprs, **opts)
|
24
|
+
final_exprs = [self::DEFAULT_VALUE_PATH, *fname_exprs].uniq
|
25
|
+
final_exprs.map do |fname_expr|
|
26
|
+
path = resolve_fname_expr(fname_expr, **opts)
|
27
|
+
if fname_expr != 'values' && !path
|
28
|
+
raise "Could not resolve file '#{fname_expr}'"
|
29
|
+
end
|
30
|
+
path.presence
|
31
|
+
end.compact.uniq
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Loads the dicts from files pointed to by final_file_paths into
|
36
|
+
# memory, and returns the deep-merged hash in the order of the files.
|
37
|
+
# @param [Array<String>] final_file_paths absolute filenames
|
38
|
+
# of value assignment files
|
39
|
+
def self.load_yaml_files(final_file_paths)
|
40
|
+
final_file_paths.inject({}) do |whole, fname|
|
41
|
+
file_values = self.load_yaml_file(fname)
|
42
|
+
whole.deep_merge(file_values)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Parses and merges cli-level key-value assignments of the form
|
48
|
+
# foo.bar=baz.
|
49
|
+
# @param [Array<String>] inline_exprs e.g %w[foo=bar, foo.bar=baz]
|
50
|
+
def self.from_inlines(inline_exprs)
|
51
|
+
inline_exprs.inject({}) do |whole, str_assignment|
|
52
|
+
assignment = self.parse_inline_assignment(str_assignment)
|
53
|
+
whole.deep_merge(assignment)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Turns a user supplied values filename into a final, usable
|
59
|
+
# absolute filesystem path. This method calls the values_paths method,
|
60
|
+
# which assumes a kerbi directory structure.
|
61
|
+
# @param [String] expr
|
62
|
+
# @param [Hash] opts
|
63
|
+
# @return [?String] the absolute filename or nil if it does not exist
|
64
|
+
def self.resolve_fname_expr(expr, **opts)
|
65
|
+
candidate_paths = self.values_paths(expr, **opts)
|
66
|
+
candidate_paths.find do |candidate_path|
|
67
|
+
File.exists?(candidate_path) && \
|
68
|
+
!File.directory?(candidate_path)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Parses a single cli-level key-value assignment with the form
|
74
|
+
# foo.bar=baz. Raises an exception if the expression is malformed.
|
75
|
+
# @param [String] str_assign e.g foo=bar
|
76
|
+
# @return [Hash] corresponding symbol hash e.g {foo: bar}
|
77
|
+
def self.parse_inline_assignment(str_assign)
|
78
|
+
deep_key, value = str_assign.split("=")
|
79
|
+
raise "malformed assignment #{str_assign}" unless deep_key && value
|
80
|
+
assign_parts = deep_key.split(".") << value
|
81
|
+
assignment = assign_parts.reverse.inject{ |a, n| { n => a } }
|
82
|
+
assignment.deep_symbolize_keys
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Loads and performs all interpolation operations on file, returns
|
87
|
+
# corresponding symbol hash of values. File is expected to contain
|
88
|
+
# one root element.
|
89
|
+
# @param [String] good_fname path of values file
|
90
|
+
#noinspection RubyResolve
|
91
|
+
# @return [Hash] corresponding value hash
|
92
|
+
def self.load_yaml_file(good_fname)
|
93
|
+
file_contents = File.read(good_fname)
|
94
|
+
interpolated = ErbWrapper.new.interpolate(file_contents)
|
95
|
+
YAML.load(interpolated).deep_symbolize_keys
|
96
|
+
end
|
97
|
+
|
98
|
+
#noinspection RubyLiteralArrayInspection
|
99
|
+
##
|
100
|
+
# Returns all possible paths that a values filename might
|
101
|
+
# resolve to according to kerbi conventions. Does not check
|
102
|
+
# for file existence. The paths are ordered by similarity to
|
103
|
+
# the input expression, starting obviously with the input itself.
|
104
|
+
# @param [Object] fname cli-level filename expression for a values file
|
105
|
+
# @param [String] root optionally pass in project/mixer root dir
|
106
|
+
# @return [Array<String>] all possible paths
|
107
|
+
def self.values_paths(fname, root: nil)
|
108
|
+
if root.nil?
|
109
|
+
root = ''
|
110
|
+
else
|
111
|
+
root = "#{root}/" unless root.end_with?("/")
|
112
|
+
end
|
113
|
+
[
|
114
|
+
"#{root}#{fname}",
|
115
|
+
"#{root}#{fname}.yaml",
|
116
|
+
"#{root}#{fname}.json",
|
117
|
+
"#{root}#{fname}.yaml.erb",
|
118
|
+
"#{root}values/#{fname}",
|
119
|
+
"#{root}values/#{fname}.yaml.erb",
|
120
|
+
"#{root}values/#{fname}.yaml",
|
121
|
+
"#{root}values/#{fname}.json"
|
122
|
+
]
|
123
|
+
end
|
124
|
+
|
125
|
+
class ErbWrapper
|
126
|
+
include Kerbi::Mixins::Mixer
|
127
|
+
def interpolate(file_cont)
|
128
|
+
ERB.new(file_cont).result(binding)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative './../spec_helper'
|
2
|
+
require_relative './../../examples/hello-kerbi/kerbifile'
|
3
|
+
|
4
|
+
RSpec.describe "Examples Directory" do
|
5
|
+
describe "The mixer" do
|
6
|
+
describe "#run" do
|
7
|
+
it "runs" do
|
8
|
+
# puts Kerbi::Globals.mixers.first.new({ }).run
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require_relative './../spec_helper'
|
2
|
+
|
3
|
+
class PatchTestOne < Kerbi::Mixer
|
4
|
+
def mix
|
5
|
+
patched_with({x: {y: "z"}}) do
|
6
|
+
push dict(x: {z: "y"})
|
7
|
+
end
|
8
|
+
|
9
|
+
push dict(x: {y: "z"})
|
10
|
+
|
11
|
+
patched_with({x: {y: "z", v: "k"}}) do
|
12
|
+
patched_with({x: {k: "v"}}) do
|
13
|
+
push dict(x: {z: "y"})
|
14
|
+
end
|
15
|
+
push dict(x: {z: "y"})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class PatchTestTwo < Kerbi::Mixer
|
21
|
+
def mix
|
22
|
+
patched_with({x: {y: "z", v: "k"}}) do
|
23
|
+
patched_with({x: {k: "v"}}) do
|
24
|
+
push dict(x: {z: "y"})
|
25
|
+
end
|
26
|
+
push dict(x: {z: "y"})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec.describe Kerbi::Mixer do
|
32
|
+
|
33
|
+
subject { Kerbi::Mixer.new({}) }
|
34
|
+
|
35
|
+
before :each do
|
36
|
+
Kerbi::Testing.reset_test_yamls_dir
|
37
|
+
Kerbi::Mixer.locate_self Kerbi::Testing::TEST_YAMLS_DIR
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#patched_with" do
|
41
|
+
let(:mixer) { PatchTestOne.new({}) }
|
42
|
+
it "correctly patches output in the yielded block" do
|
43
|
+
expect(mixer.run.first).to eq({x: {z: "y", y: "z"}})
|
44
|
+
expect(mixer.patch_stack.count).to eq(0)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "does not patch out-of-block dicts" do
|
48
|
+
expect(mixer.run[1]).to eq({x: {y: "z"}})
|
49
|
+
expect(mixer.patch_stack.count).to eq(0)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "handles nested patches correctly" do
|
53
|
+
expect(mixer.run[2]).to eq({x: {z: "y", y: "z", v: "k", k: "v"}})
|
54
|
+
expect(mixer.patch_stack.count).to eq(0)
|
55
|
+
|
56
|
+
expect(mixer.run[3]).to eq({x: {z: "y", y: "z", v: "k"}})
|
57
|
+
expect(mixer.patch_stack.count).to eq(0)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "does not affect calls that are themselves patches" do
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#push" do
|
66
|
+
let(:expected) { [{x: "y"}, {y: 'z'}] }
|
67
|
+
it "pushes" do
|
68
|
+
subject.push({x: "y"})
|
69
|
+
subject.push(nil)
|
70
|
+
subject.push([{y: 'z'}, "not-a-dict"])
|
71
|
+
expect(subject.output).to match_array(expected)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#dict" do
|
76
|
+
it "returns the right result" do
|
77
|
+
result = subject.dict({foo: "bar"})
|
78
|
+
expect(result).to eq([{foo: "bar"}])
|
79
|
+
end
|
80
|
+
it "delegates to Utils::Mixing" do
|
81
|
+
expect(Kerbi::Utils::Mixing).to receive(:clean_and_filter_dicts)
|
82
|
+
subject.dict({foo: "bar"})
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#file" do
|
87
|
+
let(:values) { { foo: "zar" } }
|
88
|
+
subject { Kerbi::Mixer.new(values) }
|
89
|
+
|
90
|
+
it "works" do
|
91
|
+
Kerbi::Testing.reset_test_yamls_dir
|
92
|
+
Kerbi::Testing.make_yaml("foo.yaml", "foo: <%= values[:foo] %>")
|
93
|
+
result = subject.file("foo.yaml")
|
94
|
+
expect(result).to eq([{foo: "zar"}])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#resolve_file_name" do
|
99
|
+
subject { Kerbi::Mixer }
|
100
|
+
|
101
|
+
context 'when fname is not a real file' do
|
102
|
+
it 'returns the assumed fully qualified name' do
|
103
|
+
expect(subject.resolve_file_name('bar')).to eq(nil)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when fname is a real file' do
|
108
|
+
it 'returns the original fname'do
|
109
|
+
Kerbi::Testing.make_yaml('foo.yaml', {})
|
110
|
+
expected = "#{Kerbi::Testing::TEST_YAMLS_DIR}/foo.yaml"
|
111
|
+
expect(subject.resolve_file_name(expected)).to eq(expected)
|
112
|
+
expect(subject.resolve_file_name('foo')).to eq(expected)
|
113
|
+
expect(subject.resolve_file_name('foo.yaml')).to eq(expected)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe ".locate_self" do
|
119
|
+
it "stores and later outputs the value" do
|
120
|
+
class Subclass < Kerbi::Mixer
|
121
|
+
locate_self 'foo'
|
122
|
+
end
|
123
|
+
expect(Subclass.new({}).class.pwd).to eq('foo')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative './../spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Kerbi::CodeGen::ProjectGenerator do
|
4
|
+
|
5
|
+
let(:klass) { Kerbi::CodeGen::ProjectGenerator }
|
6
|
+
let(:root) { Kerbi::Testing::TEST_YAMLS_DIR }
|
7
|
+
let(:project_name) { "test-project" }
|
8
|
+
let(:dir_path) { "#{root}/#{project_name}" }
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
Kerbi::Testing.reset_test_yamls_dir
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#run" do
|
15
|
+
let(:expected_files) { %w[Gemfile kerbifile.rb values.yaml] }
|
16
|
+
it "creates a new dir and writes the files" do
|
17
|
+
expect(File.exists?(dir_path)).to be_falsey
|
18
|
+
generator = klass.new(project_name: project_name, root_dir: root)
|
19
|
+
generator.run
|
20
|
+
expect(File.exists?(dir_path)).to be_truthy
|
21
|
+
actual = Dir.entries(dir_path)
|
22
|
+
expect((expected_files - actual)).to be_empty
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# require_relative './../spec_helper'
|
2
|
+
|
3
|
+
# RSpec.describe Kerbi::StateManager do
|
4
|
+
#
|
5
|
+
# subject { Kerbi::StateManager.new }
|
6
|
+
#
|
7
|
+
# context = ENV['KERBI_RSPEC_K8S_CONTEXT'] || 'kind-kind'
|
8
|
+
#
|
9
|
+
# before :each do
|
10
|
+
# puts "KONTEXT #{context}"
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# describe "#patch" do
|
14
|
+
# context "when there is no entry for variables" do
|
15
|
+
#
|
16
|
+
# before :each do
|
17
|
+
# system(
|
18
|
+
# "kubectl delete cm state -n default --context #{context}",
|
19
|
+
# out: File::NULL,
|
20
|
+
# err: File::NULL
|
21
|
+
# )
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# it "patches the configmap" do
|
25
|
+
# argue("--context #{context} --set foo.bar=baz")
|
26
|
+
# old_values = subject.get_crt_vars
|
27
|
+
# expect(old_values).to eq({})
|
28
|
+
# subject.patch
|
29
|
+
# new_values = subject.get_crt_vars
|
30
|
+
# expect(new_values).to eq({foo: {bar: "baz"}})
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# context "when there is an entry for variables" do
|
35
|
+
#
|
36
|
+
# before :each do
|
37
|
+
# system "kubectl delete cm state -n default --context #{context}"
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# it "patches the configmap" do
|
41
|
+
# argue("--context #{context} --set foo.bar=baz")
|
42
|
+
# subject.patch
|
43
|
+
#
|
44
|
+
# argue("--context #{context} --set foo.bar=car")
|
45
|
+
# subject.patch
|
46
|
+
#
|
47
|
+
# new_values = subject.get_crt_vars
|
48
|
+
# expect(new_values).to eq({foo: {bar: "car"}})
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# describe "#get_configmap" do
|
55
|
+
# context "when it does not exist" do
|
56
|
+
#
|
57
|
+
# before :each do
|
58
|
+
# system "kubectl delete cm state -n default --context #{context}"
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# it "returns the configmap as a hash" do
|
62
|
+
# argue("--context #{context}")
|
63
|
+
# result = subject.get_configmap(raise_on_er: false)
|
64
|
+
# expect(result).to be_nil
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# context "when it does exist" do
|
69
|
+
# before :each do
|
70
|
+
# system "kubectl create cm state -n default --context #{context}"
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# it "returns the configmap as a hash" do
|
74
|
+
# argue("--context #{context}")
|
75
|
+
# result = subject.get_configmap
|
76
|
+
# expect(result).to_not be_nil
|
77
|
+
# must_have = { name: "state", namespace: "default" }
|
78
|
+
# expect(result[:metadata].slice(:name, :namespace)).to eq(must_have)
|
79
|
+
# end
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
|
83
|
+
|
84
|
+
# end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative './../spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Kerbi::Mixins::Mixer do
|
4
|
+
|
5
|
+
subject { Kerbi::Mixer.new({}) }
|
6
|
+
|
7
|
+
describe "#embed" do
|
8
|
+
subject { Kerbi::EmbeddingMixerTest.new({}) }
|
9
|
+
let(:expected) do
|
10
|
+
{
|
11
|
+
dict: { embedded: "scalar" },
|
12
|
+
list: ["item"],
|
13
|
+
dict_list: [{ embedded: "item" }]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "embeds the values correctly" do
|
18
|
+
result = subject.run
|
19
|
+
expect(result).to eq([expected])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#b64enc" do
|
24
|
+
it "base 64 encodes the value" do
|
25
|
+
mixer = Kerbi::Mixer.new({})
|
26
|
+
result = mixer.b64enc("hello world")
|
27
|
+
expect(result).to eq("aGVsbG8gd29ybGQ=")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#b64enc' do
|
32
|
+
context 'when the value is truthy' do
|
33
|
+
it 'returns the base64 encoding' do
|
34
|
+
expect(subject.b64enc('demo')).to eq("ZGVtbw==")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
context 'when the value is blank or nil' do
|
38
|
+
it 'returns an empty string' do
|
39
|
+
expect(subject.b64enc('')).to eq('')
|
40
|
+
expect(subject.b64enc(nil)).to eq('')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
module Kerbi
|
49
|
+
class EmbeddingMixerTest < Kerbi::Mixer
|
50
|
+
locate_self "#{Dir.pwd}/spec/fixtures"
|
51
|
+
def mix
|
52
|
+
push file("embedding")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'simplecov'
|
3
|
+
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
require_relative './../lib/kerbi'
|
7
|
+
|
8
|
+
module Kerbi
|
9
|
+
module Testing
|
10
|
+
|
11
|
+
TEST_YAMLS_DIR = '/tmp/kerbi-yamls'
|
12
|
+
|
13
|
+
def self.make_yaml(fname, contents)
|
14
|
+
File.open(full_name = self.f_fname(fname), "w") do |f|
|
15
|
+
contents = YAML.dump(contents) if contents.is_a?(Hash)
|
16
|
+
f.write(contents)
|
17
|
+
end
|
18
|
+
full_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.del_testfile(fname)
|
22
|
+
full_name = self.f_fname(fname)
|
23
|
+
if File.exists?(full_name)
|
24
|
+
File.delete(full_name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.f_fname(fname)
|
29
|
+
"#{TEST_YAMLS_DIR}/#{fname}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.reset_test_yamls_dir
|
33
|
+
system "rm -rf #{TEST_YAMLS_DIR}"
|
34
|
+
system "mkdir #{TEST_YAMLS_DIR}"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|