kdep 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/lib/kdep/cli.rb +1 -0
- data/lib/kdep/commands/apply.rb +1 -1
- data/lib/kdep/commands/bump.rb +17 -10
- data/lib/kdep/commands/diff.rb +1 -1
- data/lib/kdep/commands/helm_install.rb +286 -0
- data/lib/kdep/commands/migrate.rb +176 -71
- data/lib/kdep/commands/render.rb +2 -2
- data/lib/kdep/defaults.rb +6 -2
- data/lib/kdep/docker.rb +57 -1
- data/lib/kdep/helm.rb +34 -0
- data/lib/kdep/old_format.rb +690 -9
- data/lib/kdep/preset.rb +1 -1
- data/lib/kdep/registry.rb +119 -3
- data/lib/kdep/version.rb +1 -1
- data/lib/kdep.rb +2 -0
- data/templates/presets/helm +4 -0
- data/templates/presets/statefulset +4 -0
- data/templates/presets/statefulset_svc +5 -0
- data/templates/resources/configmap.yml.erb +7 -3
- data/templates/resources/cronjob.yml.erb +49 -12
- data/templates/resources/deployment.yml.erb +64 -11
- data/templates/resources/ingress.yml.erb +59 -13
- data/templates/resources/secret.yml.erb +7 -7
- data/templates/resources/service.yml.erb +26 -2
- data/templates/resources/statefulset.yml.erb +125 -0
- metadata +16 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f971ace54be5f10abde07364654600377be07f9f77c1b7afb5bb9f73e5b3ddfe
|
|
4
|
+
data.tar.gz: f2040905cec594653386a4a5f9049e6217628a29925d5f7fcc8aa0724eb873dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0cb2245173f84ca39286e957c7fce8a83491f403adc8c591c40da30cbe0f3ee26bf48ce432fa7c5deac669a43fdaa328212d3e1e00ec8266248164d76779957e
|
|
7
|
+
data.tar.gz: b4cbb931a8a08250b1489ad22e9020a4642650330b1efbf79329294f777f2f1cfeb85b9b02c97bd1a0901318efa0b6f2deaa3ee5791b05eaf7a4cf02339a4a69
|
data/lib/kdep/cli.rb
CHANGED
data/lib/kdep/commands/apply.rb
CHANGED
data/lib/kdep/commands/bump.rb
CHANGED
|
@@ -63,14 +63,17 @@ module Kdep
|
|
|
63
63
|
repo_root = File.dirname(kdep_dir)
|
|
64
64
|
|
|
65
65
|
begin
|
|
66
|
-
# Step 3:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
# Step 3: Read current tag from state.yml, increment minor
|
|
67
|
+
state_path = File.join(deploy_dir, "state.yml")
|
|
68
|
+
if File.exist?(state_path)
|
|
69
|
+
state = YAML.safe_load(File.read(state_path)) || {}
|
|
70
|
+
current_tag = state["tag"] || "0.0"
|
|
71
|
+
else
|
|
72
|
+
current_tag = "0.0"
|
|
73
|
+
end
|
|
74
|
+
parsed = Kdep::VersionTagger.parse(current_tag)
|
|
75
|
+
next_version = parsed ? "#{parsed[0]}.#{parsed[1] + 1}" : "0.1"
|
|
76
|
+
@ui.info("#{current_tag} -> #{next_version}")
|
|
74
77
|
|
|
75
78
|
# Step 5: Build
|
|
76
79
|
if registry_url && !registry_url.to_s.empty?
|
|
@@ -84,7 +87,8 @@ module Kdep
|
|
|
84
87
|
context_dir: repo_root,
|
|
85
88
|
dockerfile: config["dockerfile"],
|
|
86
89
|
target: config["target"],
|
|
87
|
-
platform: @command_options[:platform] || config["platform"]
|
|
90
|
+
platform: @command_options[:platform] || config["platform"],
|
|
91
|
+
build_args: config["build_args"]
|
|
88
92
|
)
|
|
89
93
|
@ui.info("Built: #{full_tag}")
|
|
90
94
|
|
|
@@ -99,7 +103,7 @@ module Kdep
|
|
|
99
103
|
# Render manifests
|
|
100
104
|
preset = Kdep::Preset.new(config["preset"], deploy_dir)
|
|
101
105
|
resources = preset.resources
|
|
102
|
-
output_dir = File.join(
|
|
106
|
+
output_dir = File.join(deploy_dir, ".rendered")
|
|
103
107
|
|
|
104
108
|
writer = Kdep::Writer.new(output_dir)
|
|
105
109
|
writer.clean
|
|
@@ -161,6 +165,9 @@ module Kdep
|
|
|
161
165
|
if errors.empty?
|
|
162
166
|
@ui.info("#{applied} files applied, 0 errors")
|
|
163
167
|
|
|
168
|
+
# Save new tag to state.yml
|
|
169
|
+
File.write(File.join(deploy_dir, "state.yml"), YAML.dump({"tag" => next_version}))
|
|
170
|
+
|
|
164
171
|
# Launch TUI dashboard for live monitoring
|
|
165
172
|
unless @command_options[:"no-dashboard"]
|
|
166
173
|
dashboard = Kdep::Dashboard.new(
|
data/lib/kdep/commands/diff.rb
CHANGED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
require "optparse"
|
|
2
|
+
|
|
3
|
+
module Kdep
|
|
4
|
+
module Commands
|
|
5
|
+
class HelmInstall
|
|
6
|
+
def self.option_parser
|
|
7
|
+
OptionParser.new do |opts|
|
|
8
|
+
opts.banner = "Usage: kdep helm-install [deploy] [env]"
|
|
9
|
+
opts.separator ""
|
|
10
|
+
opts.separator "Runs helm upgrade --install for a helm preset deploy target,"
|
|
11
|
+
opts.separator "then renders and applies any additional k8s resources (ingress, secrets)."
|
|
12
|
+
opts.separator ""
|
|
13
|
+
opts.separator "Supports single chart (chart:) or multiple charts (charts: array)."
|
|
14
|
+
opts.separator ""
|
|
15
|
+
opts.on("--dry-run", "Show what would be run without executing")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(global_options:, command_options:, args:)
|
|
20
|
+
@global_options = global_options
|
|
21
|
+
@command_options = command_options
|
|
22
|
+
@args = args
|
|
23
|
+
@ui = Kdep::UI.new(color: false)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def execute
|
|
27
|
+
deploy_name = @args[0]
|
|
28
|
+
env = @args[1]
|
|
29
|
+
@dry_run = @command_options[:"dry-run"]
|
|
30
|
+
|
|
31
|
+
# Discover kdep/ directory
|
|
32
|
+
discovery = Kdep::Discovery.new
|
|
33
|
+
kdep_dir = discovery.find_kdep_dir
|
|
34
|
+
|
|
35
|
+
unless kdep_dir
|
|
36
|
+
@ui.error("No kdep/ directory found")
|
|
37
|
+
exit 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Resolve deploy directory
|
|
41
|
+
@deploy_dir = resolve_deploy_dir(kdep_dir, deploy_name, discovery)
|
|
42
|
+
unless @deploy_dir
|
|
43
|
+
exit 1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Load config
|
|
47
|
+
config = Kdep::Config.new(@deploy_dir, env).load
|
|
48
|
+
@namespace = config["namespace"]
|
|
49
|
+
|
|
50
|
+
unless config["preset"] == "helm"
|
|
51
|
+
@ui.error("#{deploy_name} is not a helm preset (preset: #{config["preset"]})")
|
|
52
|
+
exit 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Validate context
|
|
56
|
+
begin
|
|
57
|
+
Kdep::ContextGuard.new(config["context"]).validate!
|
|
58
|
+
rescue Kdep::Kubectl::Error => e
|
|
59
|
+
@ui.error(e.message)
|
|
60
|
+
exit 1
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Load secrets for --set interpolation
|
|
64
|
+
@secrets = load_secrets(@deploy_dir)
|
|
65
|
+
|
|
66
|
+
# Add helm repos
|
|
67
|
+
repos = config["helm_repos"] || {}
|
|
68
|
+
# Also support singular helm_repo for backward compat
|
|
69
|
+
if config["helm_repo"]
|
|
70
|
+
repo_name, repo_url = config["helm_repo"].split("=", 2)
|
|
71
|
+
repos[repo_name] = repo_url if repo_name && repo_url
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
repos.each do |repo_name, repo_url|
|
|
75
|
+
@ui.info("Adding helm repo: #{repo_name} -> #{repo_url}")
|
|
76
|
+
Kdep::Helm.repo_add(repo_name, repo_url) unless @dry_run
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Install charts
|
|
80
|
+
charts = build_chart_list(config)
|
|
81
|
+
|
|
82
|
+
if charts.empty?
|
|
83
|
+
@ui.error("app.yml has no chart or charts defined")
|
|
84
|
+
exit 1
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
charts.each do |chart_conf|
|
|
88
|
+
install_chart(chart_conf)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Render and apply additional k8s resources (ingress, secret, etc.)
|
|
92
|
+
apply_extra_resources(config, kdep_dir)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
# Build a normalized list of chart configs.
|
|
98
|
+
# Supports:
|
|
99
|
+
# chart: livekit/livekit-server (single)
|
|
100
|
+
# charts: (multiple)
|
|
101
|
+
# - release: livekit-server
|
|
102
|
+
# chart: livekit/livekit-server
|
|
103
|
+
# values: server-values.yaml
|
|
104
|
+
# sets: { ... }
|
|
105
|
+
def build_chart_list(config)
|
|
106
|
+
if config["charts"]
|
|
107
|
+
config["charts"].map do |c|
|
|
108
|
+
{
|
|
109
|
+
release: c["release"] || config["name"],
|
|
110
|
+
chart: c["chart"],
|
|
111
|
+
values: c["values"],
|
|
112
|
+
sets: c["sets"] || {},
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
elsif config["chart"]
|
|
116
|
+
[{
|
|
117
|
+
release: config["release"] || config["name"],
|
|
118
|
+
chart: config["chart"],
|
|
119
|
+
values: nil, # auto-detect values.yml in deploy dir
|
|
120
|
+
sets: config["helm_sets"] || {},
|
|
121
|
+
}]
|
|
122
|
+
else
|
|
123
|
+
[]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def install_chart(chart_conf)
|
|
128
|
+
release = chart_conf[:release]
|
|
129
|
+
chart = chart_conf[:chart]
|
|
130
|
+
|
|
131
|
+
# Resolve values file
|
|
132
|
+
values_file = resolve_values_file(chart_conf[:values])
|
|
133
|
+
|
|
134
|
+
# Build --set arguments, interpolating secrets
|
|
135
|
+
sets = {}
|
|
136
|
+
chart_conf[:sets].each do |key, value|
|
|
137
|
+
sets[key] = interpolate(value.to_s, @secrets)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
@ui.info("helm upgrade --install #{release} #{chart} -n #{@namespace}")
|
|
141
|
+
@ui.info(" values: #{values_file}") if values_file
|
|
142
|
+
sets.each { |k, v| @ui.info(" --set #{k}=#{mask(v)}") }
|
|
143
|
+
|
|
144
|
+
begin
|
|
145
|
+
output = Kdep::Helm.upgrade_install(
|
|
146
|
+
release: release,
|
|
147
|
+
chart: chart,
|
|
148
|
+
namespace: @namespace,
|
|
149
|
+
values_file: values_file,
|
|
150
|
+
sets: sets,
|
|
151
|
+
dry_run: @dry_run
|
|
152
|
+
)
|
|
153
|
+
@ui.info(output) unless output.strip.empty?
|
|
154
|
+
@ui.success("helm install complete: #{release}")
|
|
155
|
+
rescue Kdep::Helm::Error => e
|
|
156
|
+
@ui.error(e.message)
|
|
157
|
+
exit 1
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def resolve_values_file(explicit_name)
|
|
162
|
+
if explicit_name
|
|
163
|
+
path = File.join(@deploy_dir, explicit_name)
|
|
164
|
+
return path if File.exist?(path)
|
|
165
|
+
@ui.warn("Values file not found: #{explicit_name}")
|
|
166
|
+
return nil
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Auto-detect values.yml or values.yaml
|
|
170
|
+
%w[values.yml values.yaml].each do |name|
|
|
171
|
+
path = File.join(@deploy_dir, name)
|
|
172
|
+
return path if File.exist?(path)
|
|
173
|
+
end
|
|
174
|
+
nil
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def apply_extra_resources(config, kdep_dir)
|
|
178
|
+
preset = Kdep::Preset.new("helm", @deploy_dir)
|
|
179
|
+
resources = preset.resources
|
|
180
|
+
return if resources.empty?
|
|
181
|
+
|
|
182
|
+
repo_root = File.dirname(kdep_dir)
|
|
183
|
+
output_dir = File.join(@deploy_dir, ".rendered")
|
|
184
|
+
|
|
185
|
+
writer = Kdep::Writer.new(output_dir)
|
|
186
|
+
writer.clean
|
|
187
|
+
renderer = Kdep::Renderer.new(config, @deploy_dir)
|
|
188
|
+
validator = Kdep::Validator.new
|
|
189
|
+
|
|
190
|
+
files_written = 0
|
|
191
|
+
render_errors = []
|
|
192
|
+
|
|
193
|
+
resources.each_with_index do |resource_name, idx|
|
|
194
|
+
index = idx + 1
|
|
195
|
+
begin
|
|
196
|
+
content = renderer.render_resource(resource_name)
|
|
197
|
+
rescue => e
|
|
198
|
+
render_errors << "#{resource_name}: #{e.message}"
|
|
199
|
+
next
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
unless content.nil? || content.strip.empty?
|
|
203
|
+
result = validator.validate(content, resource_name)
|
|
204
|
+
unless result["valid"]
|
|
205
|
+
result["errors"].each do |err|
|
|
206
|
+
render_errors << "#{resource_name}: #{err}"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
path = writer.write(resource_name, content, index)
|
|
212
|
+
if path
|
|
213
|
+
@ui.file_written(path.sub(repo_root + "/", ""))
|
|
214
|
+
files_written += 1
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
render_errors.each { |err| @ui.error(err) } unless render_errors.empty?
|
|
219
|
+
|
|
220
|
+
unless @dry_run
|
|
221
|
+
rendered_files = Dir.glob(File.join(output_dir, "*.yml")).sort
|
|
222
|
+
applied = 0
|
|
223
|
+
apply_errors = []
|
|
224
|
+
|
|
225
|
+
rendered_files.each do |file_path|
|
|
226
|
+
begin
|
|
227
|
+
Kdep::Kubectl.apply(file_path, namespace: @namespace)
|
|
228
|
+
@ui.info("applied: #{File.basename(file_path)}")
|
|
229
|
+
applied += 1
|
|
230
|
+
rescue Kdep::Kubectl::Error => e
|
|
231
|
+
apply_errors << e.message
|
|
232
|
+
@ui.error("#{File.basename(file_path)}: #{e.message}")
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
@ui.info("#{applied} additional resources applied") if applied > 0
|
|
237
|
+
exit 1 if apply_errors.any?
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def interpolate(value, secrets)
|
|
242
|
+
value.gsub(/\$\{(\w+)\}/) do
|
|
243
|
+
key = $1
|
|
244
|
+
secrets[key] || ENV[key] || "${#{key}}"
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def mask(value)
|
|
249
|
+
return value if value.length <= 8
|
|
250
|
+
value[0..3] + "****"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def load_secrets(deploy_dir)
|
|
254
|
+
secrets_path = File.join(deploy_dir, "secrets.yml")
|
|
255
|
+
return {} unless File.exist?(secrets_path)
|
|
256
|
+
|
|
257
|
+
content = File.read(secrets_path)
|
|
258
|
+
YAML.safe_load(content) || {}
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def resolve_deploy_dir(kdep_dir, deploy_name, discovery)
|
|
262
|
+
if deploy_name
|
|
263
|
+
deploy_dir = File.join(kdep_dir, deploy_name)
|
|
264
|
+
unless File.directory?(deploy_dir)
|
|
265
|
+
@ui.error("Deploy target not found: #{deploy_name}")
|
|
266
|
+
deploys = discovery.find_deploys
|
|
267
|
+
@ui.info("Available deploys: #{deploys.join(', ')}") if deploys.any?
|
|
268
|
+
return nil
|
|
269
|
+
end
|
|
270
|
+
deploy_dir
|
|
271
|
+
else
|
|
272
|
+
deploys = discovery.find_deploys
|
|
273
|
+
if deploys.length == 1
|
|
274
|
+
File.join(kdep_dir, deploys[0])
|
|
275
|
+
elsif deploys.length > 1
|
|
276
|
+
@ui.error("Multiple deploys found, specify one: #{deploys.join(', ')}")
|
|
277
|
+
nil
|
|
278
|
+
else
|
|
279
|
+
@ui.error("No deploy targets found in kdep/")
|
|
280
|
+
nil
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|