kdep 0.4.5 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0656510094c485735715a807beee8987cee2bb99ad72ae568d1cd09cfa885de1'
4
- data.tar.gz: 40b16c44f90fa3a3a903bf09a2727a5f2351d572833e59ad5c419da9d383a026
3
+ metadata.gz: 661f22197841e0bcfd4ff7089399ab62efbd428b5aec5b8bae914f5fd80d63a4
4
+ data.tar.gz: 2671d06e5e5e71b3ba1842d5d58e172674c819a20ecce554f47d80a0d58798c1
5
5
  SHA512:
6
- metadata.gz: 2ec4299c6910de650560a825582177f91bbf94d27b0bd3ab65c2ed8db6d22c2e8904ee38250c63a2c20c09470353c1e8abf05c4d52fa51a114028ce9df99543d
7
- data.tar.gz: 8c9cfb160b021a28e4d77cd479665fff2c5d3d78abac7196b5bd13f011b6e867345046e6a14a150c736ecc35468192cddf70bf70e45dafab5dbb99c4dfc5f066
6
+ metadata.gz: 02bb2c68ba2fc5a08746214c2b163d7fa8d51fb220853e2facd17b3af86c62cd36c1b9e5f95e77e5ee18cc345b4ad97a8874af8764f87f49e4f22aa3c8b78651
7
+ data.tar.gz: f9e32dff02c9061d0f00aea72980d1e8fe1687a29f68e48faa72e11eee4f13758e32e0ee6fcede71357bf44778d2ad045537d06eec6ab4c7512a84d5074da8c2
@@ -42,6 +42,11 @@ module Kdep
42
42
  # Load config
43
43
  config = Kdep::Config.new(deploy_dir, env).load
44
44
 
45
+ if config["build"] == false
46
+ @ui.error("#{deploy_dir} declares `build: false` — its image is built elsewhere, so there is nothing to build here.")
47
+ exit 1
48
+ end
49
+
45
50
  # Resolve tag from state.yml — app.yml has no tag field by design;
46
51
  # state.yml is the source of truth for the last bumped version.
47
52
  tag = Kdep::State.tag(deploy_dir)
@@ -59,8 +59,13 @@ module Kdep
59
59
  if preset == "helm"
60
60
  run_helm_pipeline(deploy_dir, env)
61
61
  return
62
- elsif preset == "custom"
63
- run_custom_pipeline(deploy_dir, config, kdep_dir, env)
62
+ elsif preset == "custom" || config["build"] == false
63
+ # `build: false` marks a target whose image is built elsewhere
64
+ # (upstream images: elasticsearch, fluentd, curator...). It takes the
65
+ # same no-docker route as `custom`, but keeps its own preset's
66
+ # resource list -- the point is to reuse web/worker/statefulset/etc
67
+ # without a Dockerfile. Its tag comes from app.yml, not state.yml.
68
+ run_no_build_pipeline(deploy_dir, config, kdep_dir, env)
64
69
  return
65
70
  end
66
71
 
@@ -229,8 +234,11 @@ module Kdep
229
234
  helm_install.execute
230
235
  end
231
236
 
232
- # Feature E: custom-preset bump = render + apply, no docker, no state.
233
- def run_custom_pipeline(deploy_dir, config, kdep_dir, env = nil)
237
+ # Feature E: bump with no image to build = render + apply, no docker, no
238
+ # state.yml. Used by the `custom` preset and by any preset with
239
+ # `build: false`; the only difference is which resource list is rendered,
240
+ # which is why this reads config["preset"] instead of hardcoding one.
241
+ def run_no_build_pipeline(deploy_dir, config, kdep_dir, env = nil)
234
242
  # Context guard for safety.
235
243
  begin
236
244
  Kdep::ContextGuard.new(config["context"]).validate!
@@ -246,7 +254,7 @@ module Kdep
246
254
  writer = Kdep::Writer.new(output_dir)
247
255
  writer.clean
248
256
  renderer = Kdep::Renderer.new(config, deploy_dir, env: env)
249
- preset_resources = Kdep::Preset.new("custom", deploy_dir).resources
257
+ preset_resources = Kdep::Preset.new(config["preset"], deploy_dir).resources
250
258
 
251
259
  preset_resources.each_with_index do |resource, idx|
252
260
  content = renderer.render_resource(resource)
@@ -41,6 +41,11 @@ module Kdep
41
41
  # Load config
42
42
  config = Kdep::Config.new(deploy_dir, env).load
43
43
 
44
+ if config["build"] == false
45
+ @ui.error("#{deploy_dir} declares `build: false` — its image lives in an upstream registry, so there is nothing to push.")
46
+ exit 1
47
+ end
48
+
44
49
  # Resolve tag from state.yml — app.yml has no tag field by design;
45
50
  # state.yml is the source of truth for the last bumped version.
46
51
  tag = Kdep::State.tag(deploy_dir)
data/lib/kdep/config.rb CHANGED
@@ -49,6 +49,8 @@ module Kdep
49
49
  preset_name = config.fetch("preset", "custom")
50
50
  defaults = Kdep::Defaults.for_preset(preset_name)
51
51
  result = deep_merge(defaults, config)
52
+ # Persist the resolved name: every consumer reads config["preset"] directly
53
+ result["preset"] = preset_name
52
54
 
53
55
  # Name default: derive from project folder + deploy folder name
54
56
  # deploy_dir is [project]/kdep/[deploy], so go up 2 levels for project name
data/lib/kdep/defaults.rb CHANGED
@@ -13,6 +13,9 @@ module Kdep
13
13
  "cronjob" => {},
14
14
  "statefulset" => {},
15
15
  "statefulset_svc" => {},
16
+ # No replicas override: a DaemonSet has no replicas field at all, so the
17
+ # BASE_DEFAULTS value is simply never read by daemonset.yml.erb.
18
+ "daemonset" => {},
16
19
  "helm" => {},
17
20
  "custom" => {},
18
21
  }.freeze
data/lib/kdep/preset.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  module Kdep
2
2
  class Preset
3
- BUILT_IN = %w[web worker job cronjob statefulset statefulset_svc helm custom].freeze
3
+ BUILT_IN = %w[web worker job cronjob statefulset statefulset_svc daemonset helm custom].freeze
4
4
 
5
5
  def initialize(preset_name, deploy_dir)
6
+ raise "preset: is required in app.yml" if preset_name.to_s.strip.empty?
7
+
6
8
  @preset_name = preset_name
7
9
  @deploy_dir = deploy_dir
8
10
  end
@@ -1,3 +1,5 @@
1
+ require "json"
2
+
1
3
  module Kdep
2
4
  class TemplateContext
3
5
  def initialize(config, secrets = {})
@@ -18,5 +20,45 @@ module Kdep
18
20
  def yaml_value(val)
19
21
  val.is_a?(String) ? val : val.to_s
20
22
  end
23
+
24
+ # Helper: fully-qualified container image reference, "[registry/]image:tag".
25
+ #
26
+ # An empty or whitespace-only `registry:` counts as absent. Ruby treats ""
27
+ # as truthy, so the old inline `@config["registry"] ? ... : ...` ternary in
28
+ # every template emitted a leading slash ("/image:tag") for `registry: ""` --
29
+ # a real gotcha for upstream-image deploys (repleadfy/kdep#1). Same for a
30
+ # blank `tag:`, which would otherwise render an invalid trailing colon.
31
+ def image_ref
32
+ registry = @config["registry"].to_s.strip
33
+ image = @config["image"].to_s
34
+ tag = @config["tag"].to_s.strip
35
+ tag = "latest" if tag.empty?
36
+
37
+ repository = registry.empty? ? image : "#{registry}/#{image}"
38
+ "#{repository}:#{tag}"
39
+ end
40
+
41
+ # Helper: render one ConfigMap `data:` value.
42
+ #
43
+ # Multiline values become YAML block scalars, so config files mounted into
44
+ # containers (prometheus.yml, fluent.conf, curator action files...) survive
45
+ # intact instead of collapsing into one quoted line (repleadfy/kdep#1).
46
+ # Single-line values stay quoted strings: ConfigMap data values must be
47
+ # strings, so an unquoted `8080` or `true` would be rejected by the API.
48
+ #
49
+ # `indent` is the column the block body sits at -- two levels under `data:`.
50
+ def configmap_value(value, indent = 4)
51
+ str = value.to_s
52
+ return str.to_json unless str.include?("\n")
53
+
54
+ pad = " " * indent
55
+ # `|` keeps a single trailing newline, `|-` strips it -- preserve whichever
56
+ # the value actually had, since some consumers care.
57
+ chomp = str.end_with?("\n")
58
+ body = chomp ? str.sub(/\n\z/, "") : str
59
+ lines = body.split("\n", -1).map { |line| line.empty? ? "" : "#{pad}#{line}" }
60
+
61
+ "#{chomp ? "|" : "|-"}\n#{lines.join("\n")}"
62
+ end
21
63
  end
22
64
  end
data/lib/kdep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kdep
2
- VERSION = "0.4.5"
2
+ VERSION = "0.4.7"
3
3
  end
@@ -0,0 +1,5 @@
1
+ # daemonset preset: one pod per node (log shippers, node exporters, agents)
2
+ # Order determines kubectl apply sequence
3
+ configmap
4
+ secret
5
+ daemonset
@@ -6,6 +6,6 @@ metadata:
6
6
  <% if @config["configmap"] && !@config["configmap"].empty? -%>
7
7
  data:
8
8
  <% @config["configmap"].sort.each do |key, value| -%>
9
- <%= key %>: "<%= value %>"
9
+ <%= key %>: <%= configmap_value(value) %>
10
10
  <% end -%>
11
11
  <% end -%>
@@ -5,6 +5,9 @@ metadata:
5
5
  namespace: <%= namespace %>
6
6
  spec:
7
7
  schedule: "<%= @config["schedule"] %>"
8
+ <% if @config["time_zone"] -%>
9
+ timeZone: "<%= @config["time_zone"] %>"
10
+ <% end -%>
8
11
  <% if @config.key?("suspend") -%>
9
12
  suspend: <%= @config["suspend"] %>
10
13
  <% end -%>
@@ -34,7 +37,7 @@ spec:
34
37
  <% end -%>
35
38
  containers:
36
39
  - name: <%= @config["container_name"] || name %>
37
- image: <%= @config["registry"] ? "#{@config["registry"]}/#{image}" : image %>:<%= @config["tag"] || "latest" %>
40
+ image: <%= image_ref %>
38
41
  <% if @config["image_pull_policy"] -%>
39
42
  imagePullPolicy: <%= @config["image_pull_policy"] %>
40
43
  <% end -%>
@@ -0,0 +1,120 @@
1
+ apiVersion: apps/v1
2
+ kind: DaemonSet
3
+ metadata:
4
+ name: <%= @config["daemonset_name"] || name %>
5
+ namespace: <%= namespace %>
6
+ labels:
7
+ app: <%= name %>
8
+ spec:
9
+ selector:
10
+ matchLabels:
11
+ app: <%= name %>
12
+ template:
13
+ metadata:
14
+ labels:
15
+ app: <%= name %>
16
+ <% if @config["pod_labels"] -%>
17
+ <% @config["pod_labels"].each do |k, v| -%>
18
+ <%= k %>: "<%= v %>"
19
+ <% end -%>
20
+ <% end -%>
21
+ spec:
22
+ <% if @config["service_account_name"] -%>
23
+ serviceAccountName: <%= @config["service_account_name"] %>
24
+ <% end -%>
25
+ <% if @config["host_network"] -%>
26
+ hostNetwork: <%= @config["host_network"] %>
27
+ <% end -%>
28
+ <% if @config["termination_grace_period"] -%>
29
+ terminationGracePeriodSeconds: <%= @config["termination_grace_period"] %>
30
+ <% end -%>
31
+ <% if @config["node_selector"] -%>
32
+ nodeSelector:
33
+ <%= @config["node_selector"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
34
+ <% end -%>
35
+ <% if @config["tolerations"] -%>
36
+ <%# DaemonSets routinely need to land on control-plane/tainted nodes. -%>
37
+ tolerations:
38
+ <%= @config["tolerations"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
39
+ <% end -%>
40
+ <% if @config["image_pull_secrets"] -%>
41
+ imagePullSecrets:
42
+ - name: <%= @config["image_pull_secrets"] %>
43
+ <% end -%>
44
+ <% if @config["init_containers"] -%>
45
+ initContainers:
46
+ <%= @config["init_containers"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
47
+ <% end -%>
48
+ containers:
49
+ - name: <%= @config["container_name"] || name %>
50
+ image: <%= image_ref %>
51
+ <% if @config["image_pull_policy"] -%>
52
+ imagePullPolicy: <%= @config["image_pull_policy"] %>
53
+ <% end -%>
54
+ <% if @config["command"] -%>
55
+ command: <%= @config["command"].is_a?(Array) ? @config["command"].inspect : "[\"#{@config["command"]}\"]" %>
56
+ <% end -%>
57
+ <% if @config["args"] -%>
58
+ args: <%= @config["args"].is_a?(Array) ? @config["args"].inspect : "[\"#{@config["args"]}\"]" %>
59
+ <% end -%>
60
+ <% if @config["port"] -%>
61
+ ports:
62
+ - containerPort: <%= port %>
63
+ <% if @config["container_port_protocol"] -%>
64
+ protocol: <%= @config["container_port_protocol"] %>
65
+ <% end -%>
66
+ <% end -%>
67
+ <% if @config["security_context"] -%>
68
+ securityContext:
69
+ <%= @config["security_context"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
70
+ <% end -%>
71
+ <% if @config["resources"] -%>
72
+ resources:
73
+ <% if @config["resources"]["requests"] -%>
74
+ requests:
75
+ <% @config["resources"]["requests"].each do |k, v| -%>
76
+ <%= k %>: <%= v.is_a?(Numeric) ? v : "\"#{v}\"" %>
77
+ <% end -%>
78
+ <% end -%>
79
+ <% if @config["resources"]["limits"] -%>
80
+ limits:
81
+ <% @config["resources"]["limits"].each do |k, v| -%>
82
+ <%= k %>: <%= v.is_a?(Numeric) ? v : "\"#{v}\"" %>
83
+ <% end -%>
84
+ <% end -%>
85
+ <% end -%>
86
+ <% if @config["volume_mounts"] -%>
87
+ volumeMounts:
88
+ <% @config["volume_mounts"].each do |vm| -%>
89
+ - name: <%= vm["name"] %>
90
+ mountPath: <%= vm["mountPath"] %>
91
+ <% if vm["readOnly"] -%>
92
+ readOnly: <%= vm["readOnly"] %>
93
+ <% end -%>
94
+ <% if vm["subPath"] -%>
95
+ subPath: <%= vm["subPath"] %>
96
+ <% end -%>
97
+ <% end -%>
98
+ <% end -%>
99
+ <% if @config["container_env"] -%>
100
+ <%# Named container_env, not env: `env:` is a backward-compat alias for -%>
101
+ <%# `configmap:` (see Config#apply_defaults), so it never reaches a template. -%>
102
+ env:
103
+ <%= @config["container_env"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
104
+ <% end -%>
105
+ envFrom:
106
+ <% env_from_list = @config["env_from"] || ["configmap/config-#{name}", "secret/secret-#{name}"] -%>
107
+ <% env_from_list.each do |ref| -%>
108
+ <% type, ref_name = ref.split("/", 2) -%>
109
+ <% if type == "configmap" -%>
110
+ - configMapRef:
111
+ name: <%= ref_name %>
112
+ <% elsif type == "secret" -%>
113
+ - secretRef:
114
+ name: <%= ref_name %>
115
+ <% end -%>
116
+ <% end -%>
117
+ <% if @config["volumes"] -%>
118
+ volumes:
119
+ <%= @config["volumes"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
120
+ <% end -%>
@@ -29,7 +29,7 @@ spec:
29
29
  <% end -%>
30
30
  containers:
31
31
  - name: <%= @config["container_name"] || name %>
32
- image: <%= @config["registry"] ? "#{@config["registry"]}/#{image}" : image %>:<%= @config["tag"] || "latest" %>
32
+ image: <%= image_ref %>
33
33
  <% if @config["image_pull_policy"] -%>
34
34
  imagePullPolicy: <%= @config["image_pull_policy"] %>
35
35
  <% end -%>
@@ -14,7 +14,7 @@ spec:
14
14
  restartPolicy: Never
15
15
  containers:
16
16
  - name: <%= name %>
17
- image: <%= @config["registry"] ? "#{@config["registry"]}/#{image}" : image %>:<%= @config["tag"] || "latest" %>
17
+ image: <%= image_ref %>
18
18
  <% if @config["command"] -%>
19
19
  command: <%= @config["command"].is_a?(Array) ? @config["command"].inspect : "[\"#{@config["command"]}\"]" %>
20
20
  <% end -%>
@@ -27,10 +27,14 @@ spec:
27
27
  <% if @config["image_pull_secrets"] -%>
28
28
  imagePullSecrets:
29
29
  - name: <%= @config["image_pull_secrets"] %>
30
+ <% end -%>
31
+ <% if @config["init_containers"] -%>
32
+ initContainers:
33
+ <%= @config["init_containers"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
30
34
  <% end -%>
31
35
  containers:
32
36
  - name: <%= @config["container_name"] || name %>
33
- image: <%= @config["registry"] ? "#{@config["registry"]}/#{image}" : image %>:<%= @config["tag"] || "latest" %>
37
+ image: <%= image_ref %>
34
38
  <% if @config["image_pull_policy"] -%>
35
39
  imagePullPolicy: <%= @config["image_pull_policy"] %>
36
40
  <% end -%>
@@ -106,6 +110,12 @@ spec:
106
110
  subPath: <%= vm["subPath"] %>
107
111
  <% end -%>
108
112
  <% end -%>
113
+ <% end -%>
114
+ <% if @config["container_env"] -%>
115
+ <%# Named container_env, not env: `env:` is a backward-compat alias for -%>
116
+ <%# `configmap:` (see Config#apply_defaults), so it never reaches a template. -%>
117
+ env:
118
+ <%= @config["container_env"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
109
119
  <% end -%>
110
120
  envFrom:
111
121
  <% env_from_list = @config["env_from"] || ["configmap/config-#{name}", "secret/secret-#{name}"] -%>
@@ -123,3 +133,9 @@ spec:
123
133
  volumes:
124
134
  <%= @config["volumes"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
125
135
  <% end -%>
136
+ <% if @config["volume_claim_templates"] -%>
137
+ <%# Sibling of `template:`, not part of the pod spec -- this is the one field -%>
138
+ <%# that makes a StatefulSet a StatefulSet, so it belongs in the built-in. -%>
139
+ volumeClaimTemplates:
140
+ <%= @config["volume_claim_templates"].to_yaml.sub("---\n", "").gsub(/^/, " ").rstrip %>
141
+ <% end -%>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kdep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leadfy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-25 00:00:00.000000000 Z
11
+ date: 2026-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: envspec
@@ -42,14 +42,14 @@ dependencies:
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '5.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.0'
55
55
  - !ruby/object:Gem::Dependency
@@ -150,6 +150,7 @@ files:
150
150
  - templates/init/secrets.yml
151
151
  - templates/presets/cronjob
152
152
  - templates/presets/custom
153
+ - templates/presets/daemonset
153
154
  - templates/presets/helm
154
155
  - templates/presets/job
155
156
  - templates/presets/statefulset
@@ -158,6 +159,7 @@ files:
158
159
  - templates/presets/worker
159
160
  - templates/resources/configmap.yml.erb
160
161
  - templates/resources/cronjob.yml.erb
162
+ - templates/resources/daemonset.yml.erb
161
163
  - templates/resources/deployment.yml.erb
162
164
  - templates/resources/helm_ingress.yml.erb
163
165
  - templates/resources/infisical_secret.yml.erb