kdep 0.3.6 → 0.4.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/lib/kdep/check_runner.rb +52 -0
- data/lib/kdep/cli.rb +1 -0
- data/lib/kdep/commands/bump.rb +82 -0
- data/lib/kdep/commands/check.rb +84 -0
- data/lib/kdep/commands/helm_install.rb +90 -7
- data/lib/kdep/config.rb +24 -0
- data/lib/kdep/configmap_overlay.rb +110 -0
- data/lib/kdep/helm.rb +10 -3
- data/lib/kdep/kubectl.rb +30 -10
- data/lib/kdep/path_resolver.rb +61 -0
- data/lib/kdep/renderer.rb +22 -0
- data/lib/kdep/rollout_tracker.rb +62 -0
- data/lib/kdep/version.rb +1 -1
- data/lib/kdep/writer.rb +12 -0
- data/lib/kdep.rb +5 -0
- data/templates/resources/helm_ingress.yml.erb +43 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '09613eb122fcc0b4963a04b0d2d982dbd3b4a6d7c3325aebebdfad3875eb6d69'
|
|
4
|
+
data.tar.gz: 1d844ed77f42b6b50fdbdeaf3aece71e78658223571f827d5c31a9713361c4f5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 15743ca9ed3ced89956e3b83535629d127f0c7b51378b54653d3dc4c62b4c799f63e5c28fdf760ac1c327173656741a69a8efe10b8821c4a4536e9ba06c8b11e
|
|
7
|
+
data.tar.gz: edfc8dae8a811f72fc12563f5832c253b4637e6da02bfe134b1aed19ed0ce2e830697d8631b74e18cb63d6e811b9941e5598ff915e33ac58928cb2304cf19863
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "open3"
|
|
2
|
+
|
|
3
|
+
module Kdep
|
|
4
|
+
# Runs declarative preflight validators from app.yml's `check:` list.
|
|
5
|
+
# Each entry is `cmd: <shell-string>` plus optional `inputs: [paths]`. Paths
|
|
6
|
+
# are resolved via PathResolver. The full invocation is `sh -c "<cmd>
|
|
7
|
+
# <resolved_inputs...>"`. Any non-zero exit halts the pipeline.
|
|
8
|
+
class CheckRunner
|
|
9
|
+
class Error < StandardError; end
|
|
10
|
+
|
|
11
|
+
# Injectable runner so tests can assert argv without shelling out.
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :runner
|
|
14
|
+
end
|
|
15
|
+
self.runner = ->(cmd) { Open3.capture3("sh", "-c", cmd) }
|
|
16
|
+
|
|
17
|
+
def initialize(checks:, path_resolver:, ui:)
|
|
18
|
+
@checks = checks || []
|
|
19
|
+
@resolver = path_resolver
|
|
20
|
+
@ui = ui
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run!
|
|
24
|
+
return true if @checks.empty?
|
|
25
|
+
|
|
26
|
+
@checks.each do |entry|
|
|
27
|
+
cmd = entry["cmd"].to_s
|
|
28
|
+
raise Error, "check entry missing 'cmd'" if cmd.empty?
|
|
29
|
+
inputs = Array(entry["inputs"]).map { |p| @resolver.resolve(p, warn_on_fallback: true) }
|
|
30
|
+
# Shell-quote each input so paths with spaces survive.
|
|
31
|
+
quoted_inputs = inputs.compact.map { |p| shell_quote(p) }
|
|
32
|
+
full_cmd = ([cmd] + quoted_inputs).join(" ")
|
|
33
|
+
|
|
34
|
+
@ui.info("check: #{full_cmd}")
|
|
35
|
+
stdout, stderr, status = self.class.runner.call(full_cmd)
|
|
36
|
+
unless status.success?
|
|
37
|
+
@ui.error("check failed: #{full_cmd}")
|
|
38
|
+
@ui.error(stderr.strip) unless stderr.strip.empty?
|
|
39
|
+
@ui.error(stdout.strip) unless stdout.strip.empty?
|
|
40
|
+
raise Error, "preflight check failed: #{full_cmd}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def shell_quote(str)
|
|
49
|
+
"'#{str.to_s.gsub("'", %q('\\\\''))}'"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/kdep/cli.rb
CHANGED
data/lib/kdep/commands/bump.rb
CHANGED
|
@@ -14,6 +14,7 @@ module Kdep
|
|
|
14
14
|
opts.on("--no-dashboard", "Skip TUI dashboard after deploy")
|
|
15
15
|
opts.on("--platform=PLATFORM", "Target platform (e.g., linux/amd64)")
|
|
16
16
|
opts.on("--init-state", "Seed state.yml at 0.0 when missing (non-interactive)")
|
|
17
|
+
opts.on("--no-check", "Skip preflight check: validators")
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -44,6 +45,25 @@ module Kdep
|
|
|
44
45
|
|
|
45
46
|
config = Kdep::Config.new(deploy_dir, env).load
|
|
46
47
|
|
|
48
|
+
# Feature G: preflight validators. Runs for every preset, halts on
|
|
49
|
+
# first failure. --no-check skips.
|
|
50
|
+
unless @command_options[:"no-check"]
|
|
51
|
+
run_preflight_checks(config, deploy_dir, kdep_dir)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Feature E: dispatch on preset before the docker-heavy app pipeline.
|
|
55
|
+
# Helm and custom presets don't build/push images — they route to
|
|
56
|
+
# preset-specific pipelines that keep the rest of bump's guarantees
|
|
57
|
+
# (context guard, rollout tracking, etc.).
|
|
58
|
+
preset = config["preset"]
|
|
59
|
+
if preset == "helm"
|
|
60
|
+
run_helm_pipeline(deploy_dir, env)
|
|
61
|
+
return
|
|
62
|
+
elsif preset == "custom"
|
|
63
|
+
run_custom_pipeline(deploy_dir, config, kdep_dir)
|
|
64
|
+
return
|
|
65
|
+
end
|
|
66
|
+
|
|
47
67
|
# Step 2: Local config sanity — read state.yml BEFORE touching the
|
|
48
68
|
# cluster. A missing/corrupt state.yml is a local config error and
|
|
49
69
|
# should fail fast without waking kubectl.
|
|
@@ -196,6 +216,68 @@ module Kdep
|
|
|
196
216
|
|
|
197
217
|
private
|
|
198
218
|
|
|
219
|
+
# Feature E: helm-preset bump = helm-install pipeline. Preflight
|
|
220
|
+
# already ran; context guard runs inside HelmInstall; rollout flush
|
|
221
|
+
# and configmap overlays are internal to it.
|
|
222
|
+
def run_helm_pipeline(deploy_dir, env)
|
|
223
|
+
deploy_arg = File.basename(deploy_dir)
|
|
224
|
+
helm_install = Kdep::Commands::HelmInstall.new(
|
|
225
|
+
global_options: @global_options,
|
|
226
|
+
command_options: { :"dry-run" => @command_options[:"dry-run"] },
|
|
227
|
+
args: env ? [deploy_arg, env] : [deploy_arg],
|
|
228
|
+
)
|
|
229
|
+
helm_install.execute
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Feature E: custom-preset bump = render + apply, no docker, no state.
|
|
233
|
+
def run_custom_pipeline(deploy_dir, config, kdep_dir)
|
|
234
|
+
# Context guard for safety.
|
|
235
|
+
begin
|
|
236
|
+
Kdep::ContextGuard.new(config["context"]).validate!
|
|
237
|
+
rescue Kdep::Kubectl::Error => e
|
|
238
|
+
@ui.error(e.message)
|
|
239
|
+
exit 1
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
namespace = config["namespace"]
|
|
243
|
+
repo_root = File.dirname(kdep_dir)
|
|
244
|
+
output_dir = File.join(deploy_dir, ".rendered")
|
|
245
|
+
|
|
246
|
+
writer = Kdep::Writer.new(output_dir)
|
|
247
|
+
writer.clean
|
|
248
|
+
renderer = Kdep::Renderer.new(config, deploy_dir)
|
|
249
|
+
preset_resources = Kdep::Preset.new("custom", deploy_dir).resources
|
|
250
|
+
|
|
251
|
+
preset_resources.each_with_index do |resource, idx|
|
|
252
|
+
content = renderer.render_resource(resource)
|
|
253
|
+
path = writer.write(resource, content, idx + 1)
|
|
254
|
+
@ui.file_written(path.sub(repo_root + "/", "")) if path
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
unless @command_options[:"dry-run"]
|
|
258
|
+
Dir.glob(File.join(output_dir, "*.yml")).sort.each do |file_path|
|
|
259
|
+
Kdep::Kubectl.apply(file_path, namespace: namespace)
|
|
260
|
+
@ui.info("applied: #{File.basename(file_path)}")
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def run_preflight_checks(config, deploy_dir, kdep_dir)
|
|
266
|
+
checks = config["check"]
|
|
267
|
+
return if checks.nil? || checks.empty?
|
|
268
|
+
|
|
269
|
+
resolver = Kdep::PathResolver.new(
|
|
270
|
+
deploy_dir: deploy_dir,
|
|
271
|
+
kdep_dir: kdep_dir,
|
|
272
|
+
paths_from: config["paths_from"],
|
|
273
|
+
)
|
|
274
|
+
begin
|
|
275
|
+
Kdep::CheckRunner.new(checks: checks, path_resolver: resolver, ui: @ui).run!
|
|
276
|
+
rescue Kdep::CheckRunner::Error
|
|
277
|
+
exit 1
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
199
281
|
def handle_missing_state(deploy_dir)
|
|
200
282
|
if @command_options[:"init-state"]
|
|
201
283
|
seed_initial_state(deploy_dir, reason: :flag)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "optparse"
|
|
2
|
+
|
|
3
|
+
module Kdep
|
|
4
|
+
module Commands
|
|
5
|
+
# `kdep check <deploy>` — runs preflight validators declared in app.yml's
|
|
6
|
+
# check: list. Exits 0 on success, non-zero on first failing validator.
|
|
7
|
+
class Check
|
|
8
|
+
def self.option_parser
|
|
9
|
+
OptionParser.new do |opts|
|
|
10
|
+
opts.banner = "Usage: kdep check [deploy] [env]"
|
|
11
|
+
opts.separator ""
|
|
12
|
+
opts.separator "Runs preflight validators (promtool, amtool, etc.) declared in app.yml's"
|
|
13
|
+
opts.separator "check: list. Invoked automatically by kdep bump unless --no-check is set."
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(global_options:, command_options:, args:)
|
|
18
|
+
@global_options = global_options
|
|
19
|
+
@command_options = command_options
|
|
20
|
+
@args = args
|
|
21
|
+
@ui = Kdep::UI.new(color: false)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def execute
|
|
25
|
+
deploy_name = @args[0]
|
|
26
|
+
env = @args[1]
|
|
27
|
+
|
|
28
|
+
discovery = Kdep::Discovery.new
|
|
29
|
+
kdep_dir = discovery.find_kdep_dir
|
|
30
|
+
unless kdep_dir
|
|
31
|
+
@ui.error("No kdep/ directory found")
|
|
32
|
+
exit 1
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
deploy_dir = resolve_deploy_dir(kdep_dir, deploy_name, discovery)
|
|
36
|
+
exit 1 unless deploy_dir
|
|
37
|
+
|
|
38
|
+
config = Kdep::Config.new(deploy_dir, env).load
|
|
39
|
+
checks = config["check"]
|
|
40
|
+
if checks.nil? || checks.empty?
|
|
41
|
+
@ui.info("no check: entries declared for this deploy")
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
resolver = Kdep::PathResolver.new(
|
|
46
|
+
deploy_dir: deploy_dir,
|
|
47
|
+
kdep_dir: kdep_dir,
|
|
48
|
+
paths_from: config["paths_from"],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
begin
|
|
52
|
+
Kdep::CheckRunner.new(checks: checks, path_resolver: resolver, ui: @ui).run!
|
|
53
|
+
@ui.success("all #{checks.size} checks passed")
|
|
54
|
+
rescue Kdep::CheckRunner::Error
|
|
55
|
+
exit 1
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def resolve_deploy_dir(kdep_dir, deploy_name, discovery)
|
|
62
|
+
if deploy_name
|
|
63
|
+
path = File.join(kdep_dir, deploy_name)
|
|
64
|
+
unless File.directory?(path)
|
|
65
|
+
@ui.error("Deploy target not found: #{deploy_name}")
|
|
66
|
+
return nil
|
|
67
|
+
end
|
|
68
|
+
path
|
|
69
|
+
else
|
|
70
|
+
deploys = discovery.find_deploys
|
|
71
|
+
if deploys.length == 1
|
|
72
|
+
File.join(kdep_dir, deploys[0])
|
|
73
|
+
elsif deploys.length > 1
|
|
74
|
+
@ui.error("Multiple deploys found, specify one: #{deploys.join(', ')}")
|
|
75
|
+
nil
|
|
76
|
+
else
|
|
77
|
+
@ui.error("No deploy targets found in kdep/")
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -46,6 +46,18 @@ module Kdep
|
|
|
46
46
|
# Load config
|
|
47
47
|
config = Kdep::Config.new(@deploy_dir, env).load
|
|
48
48
|
@namespace = config["namespace"]
|
|
49
|
+
@kdep_dir = kdep_dir
|
|
50
|
+
@resolver = Kdep::PathResolver.new(
|
|
51
|
+
deploy_dir: @deploy_dir,
|
|
52
|
+
kdep_dir: @kdep_dir,
|
|
53
|
+
paths_from: config["paths_from"],
|
|
54
|
+
)
|
|
55
|
+
@tracker = Kdep::RolloutTracker.new(
|
|
56
|
+
rollout_on: config["rollout_on"],
|
|
57
|
+
namespace: @namespace,
|
|
58
|
+
ui: @ui,
|
|
59
|
+
dry_run: @dry_run,
|
|
60
|
+
)
|
|
49
61
|
|
|
50
62
|
unless config["preset"] == "helm"
|
|
51
63
|
@ui.error("#{deploy_name} is not a helm preset (preset: #{config["preset"]})")
|
|
@@ -88,10 +100,43 @@ module Kdep
|
|
|
88
100
|
install_chart(chart_conf)
|
|
89
101
|
end
|
|
90
102
|
|
|
103
|
+
# Feature A: overlay rich config into helm-owned configmaps.
|
|
104
|
+
apply_configmap_overlays(config)
|
|
105
|
+
|
|
91
106
|
# Render and apply additional k8s resources (ingress, secret, etc.)
|
|
92
107
|
apply_extra_resources(config, kdep_dir)
|
|
108
|
+
|
|
109
|
+
# Feature F: flush rollout-restarts after all apply phases complete.
|
|
110
|
+
ok = @tracker.flush!
|
|
111
|
+
exit 1 if @tracker.errors? && !ok
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def apply_configmap_overlays(config)
|
|
117
|
+
overlays = config["configmap_overlays"] || []
|
|
118
|
+
return if overlays.empty?
|
|
119
|
+
|
|
120
|
+
overlays.each do |overlay_hash|
|
|
121
|
+
overlay = Kdep::ConfigmapOverlay.new(
|
|
122
|
+
overlay_hash,
|
|
123
|
+
namespace: @namespace,
|
|
124
|
+
path_resolver: @resolver,
|
|
125
|
+
ui: @ui,
|
|
126
|
+
dry_run: @dry_run,
|
|
127
|
+
)
|
|
128
|
+
begin
|
|
129
|
+
result = overlay.apply!
|
|
130
|
+
@tracker.record_applied("configmap", overlay_hash["name"]) if result&.applied
|
|
131
|
+
rescue Kdep::ConfigmapOverlay::Error => e
|
|
132
|
+
@ui.error(e.message)
|
|
133
|
+
exit 1
|
|
134
|
+
end
|
|
135
|
+
end
|
|
93
136
|
end
|
|
94
137
|
|
|
138
|
+
public
|
|
139
|
+
|
|
95
140
|
private
|
|
96
141
|
|
|
97
142
|
# Build a normalized list of chart configs.
|
|
@@ -108,16 +153,22 @@ module Kdep
|
|
|
108
153
|
{
|
|
109
154
|
release: c["release"] || config["name"],
|
|
110
155
|
chart: c["chart"],
|
|
156
|
+
version: c["version"],
|
|
111
157
|
values: c["values"],
|
|
112
158
|
sets: c["sets"] || {},
|
|
113
159
|
}
|
|
114
160
|
end
|
|
115
161
|
elsif config["chart"]
|
|
162
|
+
sets = config["sets"] || config["helm_sets"] || {}
|
|
163
|
+
if config.key?("helm_sets") && !config.key?("sets")
|
|
164
|
+
@ui.warn("helm_sets: is deprecated; use sets: (will be removed in kdep 0.5.x)")
|
|
165
|
+
end
|
|
116
166
|
[{
|
|
117
167
|
release: config["release"] || config["name"],
|
|
118
168
|
chart: config["chart"],
|
|
169
|
+
version: config["version"],
|
|
119
170
|
values: nil, # auto-detect values.yml in deploy dir
|
|
120
|
-
sets:
|
|
171
|
+
sets: sets,
|
|
121
172
|
}]
|
|
122
173
|
else
|
|
123
174
|
[]
|
|
@@ -137,7 +188,9 @@ module Kdep
|
|
|
137
188
|
sets[key] = interpolate(value.to_s, @secrets)
|
|
138
189
|
end
|
|
139
190
|
|
|
140
|
-
|
|
191
|
+
version = chart_conf[:version]
|
|
192
|
+
version_suffix = version ? " --version #{version}" : ""
|
|
193
|
+
@ui.info("helm upgrade --install #{release} #{chart} -n #{@namespace}#{version_suffix}")
|
|
141
194
|
@ui.info(" values: #{values_file}") if values_file
|
|
142
195
|
sets.each { |k, v| @ui.info(" --set #{k}=#{mask(v)}") }
|
|
143
196
|
|
|
@@ -146,6 +199,7 @@ module Kdep
|
|
|
146
199
|
release: release,
|
|
147
200
|
chart: chart,
|
|
148
201
|
namespace: @namespace,
|
|
202
|
+
version: version,
|
|
149
203
|
values_file: values_file,
|
|
150
204
|
sets: sets,
|
|
151
205
|
dry_run: @dry_run
|
|
@@ -160,13 +214,14 @@ module Kdep
|
|
|
160
214
|
|
|
161
215
|
def resolve_values_file(explicit_name)
|
|
162
216
|
if explicit_name
|
|
163
|
-
path =
|
|
164
|
-
return path if File.exist?(path)
|
|
217
|
+
path = @resolver.resolve(explicit_name, warn_on_fallback: true)
|
|
218
|
+
return path if path && File.exist?(path)
|
|
165
219
|
@ui.warn("Values file not found: #{explicit_name}")
|
|
166
220
|
return nil
|
|
167
221
|
end
|
|
168
222
|
|
|
169
|
-
# Auto-detect values.yml or values.yaml
|
|
223
|
+
# Auto-detect values.yml or values.yaml in the deploy dir (not affected
|
|
224
|
+
# by paths_from: repo_root, since auto-detect is a deploy-dir contract).
|
|
170
225
|
%w[values.yml values.yaml].each do |name|
|
|
171
226
|
path = File.join(@deploy_dir, name)
|
|
172
227
|
return path if File.exist?(path)
|
|
@@ -190,8 +245,35 @@ module Kdep
|
|
|
190
245
|
files_written = 0
|
|
191
246
|
render_errors = []
|
|
192
247
|
|
|
193
|
-
|
|
194
|
-
|
|
248
|
+
next_index = 1
|
|
249
|
+
resources.each do |resource_name|
|
|
250
|
+
# Feature B: multi-ingress replaces the single built-in ingress when
|
|
251
|
+
# config["ingresses"] is set.
|
|
252
|
+
if resource_name == "ingress" && config["ingresses"]
|
|
253
|
+
begin
|
|
254
|
+
entries = renderer.render_helm_ingresses
|
|
255
|
+
rescue => e
|
|
256
|
+
render_errors << "ingress: #{e.message}"
|
|
257
|
+
next
|
|
258
|
+
end
|
|
259
|
+
entries.each do |suffix, content|
|
|
260
|
+
unless content.nil? || content.strip.empty?
|
|
261
|
+
result = validator.validate(content, "ingress")
|
|
262
|
+
unless result["valid"]
|
|
263
|
+
result["errors"].each { |err| render_errors << "ingress[#{suffix}]: #{err}" }
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
path = writer.write_suffixed("ingress", suffix, content, next_index)
|
|
267
|
+
if path
|
|
268
|
+
@ui.file_written(path.sub(repo_root + "/", ""))
|
|
269
|
+
files_written += 1
|
|
270
|
+
next_index += 1
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
next
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
index = next_index
|
|
195
277
|
begin
|
|
196
278
|
content = renderer.render_resource(resource_name)
|
|
197
279
|
rescue => e
|
|
@@ -212,6 +294,7 @@ module Kdep
|
|
|
212
294
|
if path
|
|
213
295
|
@ui.file_written(path.sub(repo_root + "/", ""))
|
|
214
296
|
files_written += 1
|
|
297
|
+
next_index += 1
|
|
215
298
|
end
|
|
216
299
|
end
|
|
217
300
|
|
data/lib/kdep/config.rb
CHANGED
|
@@ -64,7 +64,31 @@ module Kdep
|
|
|
64
64
|
result["image"] = result["name"]
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
desugar_configmap_overlay_rollout!(result)
|
|
68
|
+
|
|
67
69
|
result
|
|
68
70
|
end
|
|
71
|
+
|
|
72
|
+
# Feature F sugar: `configmap_overlays[].rollout` becomes an entry in
|
|
73
|
+
# `rollout_on` with source: configmap/<overlay-name>. If an explicit
|
|
74
|
+
# rollout_on entry already exists for that source, merge targets (union,
|
|
75
|
+
# deduped). Canonical internal form is always `rollout_on`.
|
|
76
|
+
def desugar_configmap_overlay_rollout!(config)
|
|
77
|
+
overlays = config["configmap_overlays"]
|
|
78
|
+
return unless overlays.is_a?(Array)
|
|
79
|
+
|
|
80
|
+
overlays.each do |overlay|
|
|
81
|
+
next unless overlay.is_a?(Hash) && overlay["rollout"]
|
|
82
|
+
src = "configmap/#{overlay["name"]}"
|
|
83
|
+
target = overlay["rollout"]
|
|
84
|
+
config["rollout_on"] ||= []
|
|
85
|
+
existing = config["rollout_on"].find { |r| r.is_a?(Hash) && r["source"] == src }
|
|
86
|
+
if existing
|
|
87
|
+
existing["targets"] = (Array(existing["targets"]) + [target]).uniq
|
|
88
|
+
else
|
|
89
|
+
config["rollout_on"] << { "source" => src, "targets" => [target] }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
69
93
|
end
|
|
70
94
|
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module Kdep
|
|
5
|
+
# Overlays rich configmap data from external files onto a helm-owned
|
|
6
|
+
# configmap. Runs after `helm upgrade --install` so the chart creates the
|
|
7
|
+
# configmap first; this module rewrites its data keys without touching
|
|
8
|
+
# helm-set labels or annotations.
|
|
9
|
+
#
|
|
10
|
+
# Two merge strategies:
|
|
11
|
+
# replace — fetch live configmap, replace `data:` with only the listed
|
|
12
|
+
# keys, `kubectl apply` the full manifest back.
|
|
13
|
+
# patch — emit a JSON patch that adds/updates only the listed keys,
|
|
14
|
+
# leaving any other data helm wrote intact.
|
|
15
|
+
#
|
|
16
|
+
# First-deploy safety: if the configmap does not yet exist (chart hasn't
|
|
17
|
+
# run yet), the overlay is skipped with a warning; a re-run picks it up.
|
|
18
|
+
class ConfigmapOverlay
|
|
19
|
+
class Error < StandardError; end
|
|
20
|
+
|
|
21
|
+
Result = Struct.new(:configmap_name, :applied, :skipped_reason,
|
|
22
|
+
keyword_init: true)
|
|
23
|
+
|
|
24
|
+
def initialize(overlay, namespace:, path_resolver:, ui:, dry_run: false)
|
|
25
|
+
@overlay = overlay
|
|
26
|
+
@namespace = namespace
|
|
27
|
+
@resolver = path_resolver
|
|
28
|
+
@ui = ui
|
|
29
|
+
@dry_run = dry_run
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def name
|
|
33
|
+
@overlay["name"]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def merge_strategy
|
|
37
|
+
(@overlay["merge"] || "replace").to_s
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def apply!
|
|
41
|
+
case merge_strategy
|
|
42
|
+
when "replace" then apply_replace!
|
|
43
|
+
when "patch" then apply_patch!
|
|
44
|
+
else
|
|
45
|
+
raise Error, "configmap_overlay #{name}: unknown merge strategy '#{merge_strategy}' (expected replace|patch)"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def data_payload
|
|
52
|
+
payload = {}
|
|
53
|
+
@overlay["data_files"].each do |key, path|
|
|
54
|
+
resolved = @resolver.resolve(path, warn_on_fallback: true)
|
|
55
|
+
raise Error, "configmap_overlay #{name}: source file not found: #{path}" unless resolved && File.exist?(resolved)
|
|
56
|
+
content = File.read(resolved)
|
|
57
|
+
raise Error, "configmap_overlay #{name}: source file is empty: #{path}" if content.empty?
|
|
58
|
+
payload[key] = content
|
|
59
|
+
end
|
|
60
|
+
payload
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def apply_replace!
|
|
64
|
+
live_yaml = Kdep::Kubectl.get("configmap", name, namespace: @namespace)
|
|
65
|
+
if live_yaml.nil? || live_yaml.strip.empty?
|
|
66
|
+
@ui.warn("configmap_overlay #{name}: not yet created (first deploy); skipping")
|
|
67
|
+
return Result.new(configmap_name: name, applied: false, skipped_reason: "not_yet_created")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
live = Kdep::YAMLCompat.safe_load(live_yaml)
|
|
71
|
+
raise Error, "configmap_overlay #{name}: unexpected live configmap shape" unless live.is_a?(Hash)
|
|
72
|
+
live["data"] = data_payload
|
|
73
|
+
# Strip server-side-managed fields that confuse kubectl apply.
|
|
74
|
+
if live["metadata"].is_a?(Hash)
|
|
75
|
+
%w[resourceVersion uid creationTimestamp managedFields selfLink generation].each { |k| live["metadata"].delete(k) }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
require "tempfile"
|
|
79
|
+
tmp = Tempfile.new(["#{name}-overlay", ".yml"])
|
|
80
|
+
tmp.write(YAML.dump(live))
|
|
81
|
+
tmp.close
|
|
82
|
+
|
|
83
|
+
if @dry_run
|
|
84
|
+
@ui.info("[dry-run] would apply configmap_overlay #{name}")
|
|
85
|
+
else
|
|
86
|
+
Kdep::Kubectl.apply(tmp.path, namespace: @namespace)
|
|
87
|
+
@ui.info("configmap_overlay #{name}: applied (replace)")
|
|
88
|
+
end
|
|
89
|
+
Result.new(configmap_name: name, applied: true, skipped_reason: nil)
|
|
90
|
+
ensure
|
|
91
|
+
tmp&.unlink
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def apply_patch!
|
|
95
|
+
ops = data_payload.map { |k, v| { "op" => "add", "path" => "/data/#{escape_patch_key(k)}", "value" => v } }
|
|
96
|
+
if @dry_run
|
|
97
|
+
@ui.info("[dry-run] would patch configmap_overlay #{name} (#{ops.size} keys)")
|
|
98
|
+
else
|
|
99
|
+
Kdep::Kubectl.patch("configmap", name, namespace: @namespace, type: "json", patch: ops.to_json)
|
|
100
|
+
@ui.info("configmap_overlay #{name}: patched (#{ops.size} keys)")
|
|
101
|
+
end
|
|
102
|
+
Result.new(configmap_name: name, applied: true, skipped_reason: nil)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# RFC 6901 JSON-pointer escaping for `/` and `~` in key names.
|
|
106
|
+
def escape_patch_key(key)
|
|
107
|
+
key.to_s.gsub("~", "~0").gsub("/", "~1")
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/kdep/helm.rb
CHANGED
|
@@ -4,17 +4,24 @@ module Kdep
|
|
|
4
4
|
module Helm
|
|
5
5
|
class Error < StandardError; end
|
|
6
6
|
|
|
7
|
+
class << self
|
|
8
|
+
attr_accessor :runner
|
|
9
|
+
end
|
|
10
|
+
# Default: shell out to the real helm binary.
|
|
11
|
+
self.runner = ->(*args) { Open3.capture3("helm", *args) }
|
|
12
|
+
|
|
7
13
|
def self.run(*args)
|
|
8
|
-
stdout, stderr, status =
|
|
14
|
+
stdout, stderr, status = runner.call(*args)
|
|
9
15
|
unless status.success?
|
|
10
16
|
raise Error, "helm #{args.first} failed: #{stderr.strip}"
|
|
11
17
|
end
|
|
12
18
|
stdout
|
|
13
19
|
end
|
|
14
20
|
|
|
15
|
-
# helm upgrade --install <release> <chart> -n <namespace> [-f values.yml] [--set k=v ...]
|
|
16
|
-
def self.upgrade_install(release:, chart:, namespace:, values_file: nil, sets: {}, dry_run: false)
|
|
21
|
+
# helm upgrade --install <release> <chart> -n <namespace> [--version <v>] [-f values.yml] [--set k=v ...]
|
|
22
|
+
def self.upgrade_install(release:, chart:, namespace:, version: nil, values_file: nil, sets: {}, dry_run: false)
|
|
17
23
|
args = ["upgrade", "--install", release, chart, "-n", namespace]
|
|
24
|
+
args += ["--version", version] if version
|
|
18
25
|
args += ["-f", values_file] if values_file
|
|
19
26
|
sets.each do |key, value|
|
|
20
27
|
args += ["--set", "#{key}=#{value}"]
|
data/lib/kdep/kubectl.rb
CHANGED
|
@@ -5,8 +5,14 @@ module Kdep
|
|
|
5
5
|
module Kubectl
|
|
6
6
|
class Error < StandardError; end
|
|
7
7
|
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor :runner
|
|
10
|
+
end
|
|
11
|
+
# Default: shell out to the real kubectl binary.
|
|
12
|
+
self.runner = ->(*args) { Open3.capture3("kubectl", *args) }
|
|
13
|
+
|
|
8
14
|
def self.run(*args)
|
|
9
|
-
stdout, stderr, status =
|
|
15
|
+
stdout, stderr, status = runner.call(*args)
|
|
10
16
|
unless status.success?
|
|
11
17
|
raise Error, "kubectl #{args.first} failed: #{stderr.strip}"
|
|
12
18
|
end
|
|
@@ -14,8 +20,7 @@ module Kdep
|
|
|
14
20
|
end
|
|
15
21
|
|
|
16
22
|
def self.run_json(*args)
|
|
17
|
-
|
|
18
|
-
JSON.parse(output)
|
|
23
|
+
JSON.parse(run(*args, "-o", "json"))
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
def self.current_context
|
|
@@ -27,15 +32,30 @@ module Kdep
|
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
def self.diff(file_path)
|
|
30
|
-
stdout, stderr, status =
|
|
35
|
+
stdout, stderr, status = runner.call("diff", "-f", file_path)
|
|
31
36
|
case status.exitstatus
|
|
32
|
-
when 0
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
stdout
|
|
36
|
-
else
|
|
37
|
-
raise Error, "kubectl diff failed (exit #{status.exitstatus}): #{stderr.strip}"
|
|
37
|
+
when 0 then nil # no diff
|
|
38
|
+
when 1 then stdout # diffs present on stdout
|
|
39
|
+
else raise Error, "kubectl diff failed (exit #{status.exitstatus}): #{stderr.strip}"
|
|
38
40
|
end
|
|
39
41
|
end
|
|
42
|
+
|
|
43
|
+
# Declarative rollout-restart helper used by Kdep::RolloutTracker (Feature F).
|
|
44
|
+
def self.rollout_restart(kind, name, namespace:)
|
|
45
|
+
run("rollout", "restart", "#{kind}/#{name}", "-n", namespace)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# JSON-patch helper used by Kdep::ConfigmapOverlay (Feature A) with merge: patch.
|
|
49
|
+
def self.patch(kind, name, namespace:, type:, patch:)
|
|
50
|
+
run("patch", kind, name, "-n", namespace, "--type", type, "-p", patch)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Live-fetch helper. Returns nil when the resource is absent (NotFound).
|
|
54
|
+
# Caller distinguishes "not yet created" from real errors.
|
|
55
|
+
def self.get(kind, name, namespace:, output: "yaml")
|
|
56
|
+
run("get", kind, name, "-n", namespace, "-o", output)
|
|
57
|
+
rescue Error
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
40
60
|
end
|
|
41
61
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require "pathname"
|
|
2
|
+
|
|
3
|
+
module Kdep
|
|
4
|
+
# Resolves user-supplied relative file paths from app.yml against either
|
|
5
|
+
# the deploy dir (default) or the repo root that contains kdep/.
|
|
6
|
+
# Mode is selected by the top-level `paths_from:` field.
|
|
7
|
+
class PathResolver
|
|
8
|
+
def initialize(deploy_dir:, kdep_dir:, paths_from:)
|
|
9
|
+
@deploy_dir = deploy_dir
|
|
10
|
+
@repo_root = File.dirname(kdep_dir)
|
|
11
|
+
@mode = paths_from.to_s == "repo_root" ? :repo_root : :target
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Returns the absolute filesystem path for a relative input. If the
|
|
15
|
+
# resolved path does not exist at the configured origin but DOES exist at
|
|
16
|
+
# the other origin, resolves at the other and optionally warns — this
|
|
17
|
+
# catches the common "forgot paths_from: repo_root" mistake.
|
|
18
|
+
def resolve(path, warn_on_fallback: false)
|
|
19
|
+
return nil if path.nil?
|
|
20
|
+
return path if Pathname.new(path).absolute?
|
|
21
|
+
|
|
22
|
+
primary = File.expand_path(path, primary_base)
|
|
23
|
+
return primary if File.exist?(primary)
|
|
24
|
+
|
|
25
|
+
fallback = File.expand_path(path, fallback_base)
|
|
26
|
+
if File.exist?(fallback)
|
|
27
|
+
if warn_on_fallback
|
|
28
|
+
warn "path '#{path}' not found under #{@mode}; falling back to #{other_mode}. " \
|
|
29
|
+
"Set paths_from: #{other_mode} or fix the path."
|
|
30
|
+
end
|
|
31
|
+
return fallback
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
primary
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Checks either origin. Used by validators that don't care WHICH origin
|
|
38
|
+
# holds the file, only that it exists somewhere sensible.
|
|
39
|
+
def exists?(path)
|
|
40
|
+
return false if path.nil?
|
|
41
|
+
return File.exist?(path) if Pathname.new(path).absolute?
|
|
42
|
+
|
|
43
|
+
File.exist?(File.expand_path(path, primary_base)) ||
|
|
44
|
+
File.exist?(File.expand_path(path, fallback_base))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def primary_base
|
|
50
|
+
@mode == :repo_root ? @repo_root : @deploy_dir
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def fallback_base
|
|
54
|
+
@mode == :repo_root ? @deploy_dir : @repo_root
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def other_mode
|
|
58
|
+
@mode == :repo_root ? :target : :repo_root
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/kdep/renderer.rb
CHANGED
|
@@ -28,6 +28,28 @@ module Kdep
|
|
|
28
28
|
erb.result(context.get_binding)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# Feature B: render N Ingress resources from config["ingresses"].
|
|
32
|
+
# Returns [[suffix, yaml], ...] so the caller can emit multiple files.
|
|
33
|
+
def render_helm_ingresses
|
|
34
|
+
entries = @config["ingresses"] || []
|
|
35
|
+
return [] if entries.empty?
|
|
36
|
+
|
|
37
|
+
template_path = File.join(Kdep.templates_dir, "resources", "helm_ingress.yml.erb")
|
|
38
|
+
raise "helm_ingress template not found at #{template_path}" unless File.exist?(template_path)
|
|
39
|
+
template_content = File.read(template_path)
|
|
40
|
+
erb = if RUBY_VERSION >= "2.6"
|
|
41
|
+
ERB.new(template_content, trim_mode: "-")
|
|
42
|
+
else
|
|
43
|
+
ERB.new(template_content, nil, "-")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
entries.map do |ingress|
|
|
47
|
+
context = TemplateContext.new(@config)
|
|
48
|
+
context.instance_variable_set(:@ingress, ingress)
|
|
49
|
+
[ingress["name"], erb.result(context.get_binding)]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
31
53
|
private
|
|
32
54
|
|
|
33
55
|
def resolve_template(resource_name)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
|
|
3
|
+
module Kdep
|
|
4
|
+
# Declarative rollout-restart triggers. Phases that apply resources register
|
|
5
|
+
# sources; at end of run the tracker looks up matching rollout_on rules and
|
|
6
|
+
# restarts the unique set of targets once.
|
|
7
|
+
#
|
|
8
|
+
# sources are "kind/name" (configmap, secret).
|
|
9
|
+
# targets are "kind/name" (deployment, statefulset, daemonset).
|
|
10
|
+
class RolloutTracker
|
|
11
|
+
class Error < StandardError; end
|
|
12
|
+
|
|
13
|
+
def initialize(rollout_on:, namespace:, ui:, dry_run: false)
|
|
14
|
+
@rules = rollout_on || []
|
|
15
|
+
@namespace = namespace
|
|
16
|
+
@ui = ui
|
|
17
|
+
@dry_run = dry_run
|
|
18
|
+
@applied = Set.new
|
|
19
|
+
@errors = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def record_applied(kind, name)
|
|
23
|
+
@applied << "#{kind.to_s.downcase}/#{name}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def flush!
|
|
27
|
+
targets = Set.new
|
|
28
|
+
@rules.each do |rule|
|
|
29
|
+
src = normalize(rule["source"])
|
|
30
|
+
next unless @applied.include?(src)
|
|
31
|
+
Array(rule["targets"]).each { |t| targets << normalize(t) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
targets.sort.each do |target|
|
|
35
|
+
kind, name = target.split("/", 2)
|
|
36
|
+
if @dry_run
|
|
37
|
+
@ui.info("[dry-run] would rollout-restart #{kind}/#{name}")
|
|
38
|
+
next
|
|
39
|
+
end
|
|
40
|
+
begin
|
|
41
|
+
Kdep::Kubectl.rollout_restart(kind, name, namespace: @namespace)
|
|
42
|
+
@ui.info("rollout-restarted #{kind}/#{name}")
|
|
43
|
+
rescue Kdep::Kubectl::Error => e
|
|
44
|
+
@errors << "#{kind}/#{name}: #{e.message}"
|
|
45
|
+
@ui.error("rollout-restart failed for #{kind}/#{name}: #{e.message}")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@errors.empty?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def errors?
|
|
53
|
+
!@errors.empty?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def normalize(s)
|
|
59
|
+
s.to_s.downcase
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/kdep/version.rb
CHANGED
data/lib/kdep/writer.rb
CHANGED
|
@@ -22,5 +22,17 @@ module Kdep
|
|
|
22
22
|
File.write(path, content)
|
|
23
23
|
path
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
# Used by Feature B (multi-ingress): one logical resource "ingress"
|
|
27
|
+
# emits N files named NN-ingress-<suffix>.yml so kubectl apply diffs
|
|
28
|
+
# them independently.
|
|
29
|
+
def write_suffixed(base_name, suffix, content, index)
|
|
30
|
+
return nil if content.nil? || content.strip.empty?
|
|
31
|
+
FileUtils.mkdir_p(@output_dir)
|
|
32
|
+
filename = format("%02d-%s-%s.yml", index, base_name, suffix)
|
|
33
|
+
path = File.join(@output_dir, filename)
|
|
34
|
+
File.write(path, content)
|
|
35
|
+
path
|
|
36
|
+
end
|
|
25
37
|
end
|
|
26
38
|
end
|
data/lib/kdep.rb
CHANGED
|
@@ -18,6 +18,10 @@ require "kdep/helm"
|
|
|
18
18
|
require "kdep/registry"
|
|
19
19
|
require "kdep/version_tagger"
|
|
20
20
|
require "kdep/state"
|
|
21
|
+
require "kdep/path_resolver"
|
|
22
|
+
require "kdep/configmap_overlay"
|
|
23
|
+
require "kdep/rollout_tracker"
|
|
24
|
+
require "kdep/check_runner"
|
|
21
25
|
require "kdep/doctor"
|
|
22
26
|
require "kdep/commands/render"
|
|
23
27
|
require "kdep/commands/init"
|
|
@@ -37,6 +41,7 @@ require "kdep/commands/migrate"
|
|
|
37
41
|
require "kdep/commands/helm_install"
|
|
38
42
|
require "kdep/commands/dashboard"
|
|
39
43
|
require "kdep/commands/doctor"
|
|
44
|
+
require "kdep/commands/check"
|
|
40
45
|
require "kdep/dashboard/screen"
|
|
41
46
|
require "kdep/dashboard/layout"
|
|
42
47
|
require "kdep/dashboard/panel"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<%
|
|
2
|
+
raise "helm_ingress template requires @ingress (single entry)" unless @ingress
|
|
3
|
+
ingress = @ingress
|
|
4
|
+
ingress_name = "ingress-#{ingress["name"]}"
|
|
5
|
+
default_annotations = {
|
|
6
|
+
"kubernetes.io/ingress.class" => "nginx",
|
|
7
|
+
"cert-manager.io/cluster-issuer" => "letsencrypt",
|
|
8
|
+
}
|
|
9
|
+
if ingress["auth_secret"] && !ingress["auth_secret"].to_s.empty?
|
|
10
|
+
default_annotations["nginx.ingress.kubernetes.io/auth-type"] = "basic"
|
|
11
|
+
default_annotations["nginx.ingress.kubernetes.io/auth-secret"] = ingress["auth_secret"]
|
|
12
|
+
default_annotations["nginx.ingress.kubernetes.io/auth-realm"] = "Auth required"
|
|
13
|
+
end
|
|
14
|
+
user_annotations = ingress["annotations"].is_a?(Hash) ? ingress["annotations"] : {}
|
|
15
|
+
annotations = default_annotations.merge(user_annotations)
|
|
16
|
+
path = ingress["path"] || "/"
|
|
17
|
+
path_type = ingress["path_type"] || "Prefix"
|
|
18
|
+
-%>
|
|
19
|
+
apiVersion: networking.k8s.io/v1
|
|
20
|
+
kind: Ingress
|
|
21
|
+
metadata:
|
|
22
|
+
name: <%= ingress_name %>
|
|
23
|
+
namespace: <%= namespace %>
|
|
24
|
+
annotations:
|
|
25
|
+
<% annotations.each do |k, v| -%>
|
|
26
|
+
<%= k %>: "<%= v %>"
|
|
27
|
+
<% end -%>
|
|
28
|
+
spec:
|
|
29
|
+
tls:
|
|
30
|
+
- hosts:
|
|
31
|
+
- <%= ingress["host"] %>
|
|
32
|
+
secretName: <%= ingress["tls_secret"] %>
|
|
33
|
+
rules:
|
|
34
|
+
- host: <%= ingress["host"] %>
|
|
35
|
+
http:
|
|
36
|
+
paths:
|
|
37
|
+
- path: <%= path %>
|
|
38
|
+
pathType: <%= path_type %>
|
|
39
|
+
backend:
|
|
40
|
+
service:
|
|
41
|
+
name: <%= ingress["service"] %>
|
|
42
|
+
port:
|
|
43
|
+
number: <%= ingress["port"] %>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kdep
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Leadfy
|
|
@@ -63,11 +63,13 @@ files:
|
|
|
63
63
|
- LICENSE.txt
|
|
64
64
|
- exe/kdep
|
|
65
65
|
- lib/kdep.rb
|
|
66
|
+
- lib/kdep/check_runner.rb
|
|
66
67
|
- lib/kdep/cli.rb
|
|
67
68
|
- lib/kdep/cluster_health.rb
|
|
68
69
|
- lib/kdep/commands/apply.rb
|
|
69
70
|
- lib/kdep/commands/build.rb
|
|
70
71
|
- lib/kdep/commands/bump.rb
|
|
72
|
+
- lib/kdep/commands/check.rb
|
|
71
73
|
- lib/kdep/commands/dashboard.rb
|
|
72
74
|
- lib/kdep/commands/diff.rb
|
|
73
75
|
- lib/kdep/commands/doctor.rb
|
|
@@ -84,6 +86,7 @@ files:
|
|
|
84
86
|
- lib/kdep/commands/sh.rb
|
|
85
87
|
- lib/kdep/commands/status.rb
|
|
86
88
|
- lib/kdep/config.rb
|
|
89
|
+
- lib/kdep/configmap_overlay.rb
|
|
87
90
|
- lib/kdep/context_guard.rb
|
|
88
91
|
- lib/kdep/dashboard.rb
|
|
89
92
|
- lib/kdep/dashboard/health_panel.rb
|
|
@@ -107,9 +110,11 @@ files:
|
|
|
107
110
|
- lib/kdep/helm.rb
|
|
108
111
|
- lib/kdep/kubectl.rb
|
|
109
112
|
- lib/kdep/old_format.rb
|
|
113
|
+
- lib/kdep/path_resolver.rb
|
|
110
114
|
- lib/kdep/preset.rb
|
|
111
115
|
- lib/kdep/registry.rb
|
|
112
116
|
- lib/kdep/renderer.rb
|
|
117
|
+
- lib/kdep/rollout_tracker.rb
|
|
113
118
|
- lib/kdep/state.rb
|
|
114
119
|
- lib/kdep/template_context.rb
|
|
115
120
|
- lib/kdep/ui.rb
|
|
@@ -133,6 +138,7 @@ files:
|
|
|
133
138
|
- templates/resources/configmap.yml.erb
|
|
134
139
|
- templates/resources/cronjob.yml.erb
|
|
135
140
|
- templates/resources/deployment.yml.erb
|
|
141
|
+
- templates/resources/helm_ingress.yml.erb
|
|
136
142
|
- templates/resources/ingress.yml.erb
|
|
137
143
|
- templates/resources/job.yml.erb
|
|
138
144
|
- templates/resources/secret.yml.erb
|