kube_cluster 0.15.0 → 0.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29e455d15de0900af5bf1f941e9636c0e8a1d220a4e6a64dc433aac86d43e596
4
- data.tar.gz: 741e89bb988a9ed630b5d1b58e1ec95dcf85acb278e4045f936ad8803e0e7d82
3
+ metadata.gz: f7b1fa07c1c7e080961d17a281c4b2c8a6e84abc6cc9d4ae6c568415b2e989ae
4
+ data.tar.gz: 80994ba308efe68700f925dff182abf89908a55b1d35224c75eb577d7a42485c
5
5
  SHA512:
6
- metadata.gz: 84041914a07f9bb7c21c1653d53781fefa350f54d0fe66b45e519bb6dbfbbd5788df10e49a347c33deebd779e9443348f577a36e787a7451841bd1812a7fc918
7
- data.tar.gz: 1ca89cc294ed092ba68bd28086bbd81c655896cea7d84a745d9974f93d093d71fe7d76e83ae1da0080179827d8c86274d40f05ecc66b102aa9fe27ad1c514631
6
+ metadata.gz: ac7da1390a3fedf1973a709b0ce5547ee99590d68dfdf46686d4fcf64e237b3a37e86f27b3dfc1350ef22bd264828d4c13705b79e05e6abc1f5ac20ae2cd8364
7
+ data.tar.gz: 96154be98c24b04e56a49c2c6a4df93b94fe67bfa8b79c754c62e543187a7083418815049e05a47db516b16d58db91e2e9dc649a46f9d04243219c28af87df27
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kube_cluster (0.15.0)
4
+ kube_cluster (0.17.0)
5
5
  activesupport (~> 8.0)
6
6
  kube_kubectl (~> 2.0)
7
7
  kube_schema (~> 1.7)
@@ -25,9 +25,25 @@ module Kube
25
25
 
26
26
  h = resource.to_h
27
27
  h[:metadata] ||= {}
28
- next resource if h[:metadata][:namespace] && h[:metadata][:namespace] != 'default'
29
28
 
30
- h[:metadata][:namespace] = @namespace
29
+ unless h[:metadata][:namespace] && h[:metadata][:namespace] != 'default'
30
+ h[:metadata][:namespace] = @namespace
31
+ end
32
+
33
+ # A RoleBinding's ServiceAccount subjects need an explicit
34
+ # namespace; fill in any left blank with the target namespace so
35
+ # same-namespace bindings (e.g. ServiceAccountWithRole) resolve.
36
+ if h[:kind] == 'RoleBinding' && h[:subjects].is_a?(Array)
37
+ h[:subjects] = h[:subjects].map { |subject|
38
+ if subject[:kind] == 'ServiceAccount' &&
39
+ (subject[:namespace].nil? || subject[:namespace].to_s.empty?)
40
+ subject.merge(namespace: @namespace)
41
+ else
42
+ subject
43
+ end
44
+ }
45
+ end
46
+
31
47
  resource.rebuild(h)
32
48
  }
33
49
  }
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
6
+ module Kube
7
+ module Cluster
8
+ module Standard
9
+ # A CronJob plus the ServiceAccount/Role/RoleBinding it runs as, bundled
10
+ # into one Manifest. The RBAC rules use the Role shorthand; the CronJob's
11
+ # serviceAccountName is wired to the generated ServiceAccount. Everything
12
+ # is named after +name+.
13
+ #
14
+ # CronJobWithServiceAccount.new(
15
+ # name: "glauth-config-builder",
16
+ # image: "nixery.dev/shell/ruby/kubectl/cacert",
17
+ # schedule: "*/5 * * * *",
18
+ # rules: [
19
+ # "secrets" => %w[get list],
20
+ # "batch/jobs" => %w[create],
21
+ # ],
22
+ # command: RubyScript(<<~'BUILD'),
23
+ # ...
24
+ # BUILD
25
+ # )
26
+ #
27
+ class CronJobWithServiceAccount < Kube::Cluster::Manifest
28
+ def initialize(name:, image:, schedule:, rules:, env: {}, command: nil,
29
+ backoff_limit: 3, ttl: 300, concurrency_policy: "Forbid", &block)
30
+ service_account_with_role = Kube::Cluster::Standard::ServiceAccountWithRole.new(
31
+ service_account: Kube::Cluster::Standard::ServiceAccount.new(name: name),
32
+ role: Kube::Cluster::Standard::Role.new(rules: rules),
33
+ )
34
+
35
+ cron_job = Kube::Cluster::Standard::CronJob.new(
36
+ name: name,
37
+ image: image,
38
+ schedule: schedule,
39
+ env: env,
40
+ command: command,
41
+ backoff_limit: backoff_limit,
42
+ ttl: ttl,
43
+ concurrency_policy: concurrency_policy,
44
+ ) do
45
+ spec.jobTemplate.spec.template.spec.serviceAccountName = name
46
+ end
47
+
48
+ super(*service_account_with_role.to_a, cron_job)
49
+
50
+ instance_exec(&block) if block
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ test do
58
+ describe "CronJobWithServiceAccount" do
59
+ it "emits the RBAC trio plus the cron job" do
60
+ m = Kube::Cluster::Standard::CronJobWithServiceAccount.new(
61
+ name: "builder",
62
+ image: "nixery.dev/shell/kubectl",
63
+ schedule: "*/5 * * * *",
64
+ rules: ["secrets" => %w[get list]],
65
+ command: ["true"],
66
+ )
67
+
68
+ m.map { |r| r.to_h[:kind] }.sort.should == %w[CronJob Role RoleBinding ServiceAccount]
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
6
+ module Kube
7
+ module Cluster
8
+ module Standard
9
+ # A one-shot Job plus the ServiceAccount/Role/RoleBinding it runs as,
10
+ # bundled into one Manifest. The RBAC rules use the Role shorthand; the
11
+ # Job's serviceAccountName is wired to the generated ServiceAccount.
12
+ # Everything is named after +name+.
13
+ #
14
+ # JobWithServiceAccount.new(
15
+ # name: "glauth-config-seed",
16
+ # image: "nixery.dev/shell/kubectl",
17
+ # rules: [
18
+ # "batch/cronjobs" => %w[get],
19
+ # "batch/jobs" => %w[create],
20
+ # ],
21
+ # command: BashScript(<<~'SEED'),
22
+ # ...
23
+ # SEED
24
+ # )
25
+ #
26
+ class JobWithServiceAccount < Kube::Cluster::Manifest
27
+ def initialize(name:, image:, rules:, env: {}, command: nil,
28
+ backoff_limit: 3, ttl: 300, &block)
29
+ service_account_with_role = Kube::Cluster::Standard::ServiceAccountWithRole.new(
30
+ service_account: Kube::Cluster::Standard::ServiceAccount.new(name: name),
31
+ role: Kube::Cluster::Standard::Role.new(rules: rules),
32
+ )
33
+
34
+ job = Kube::Cluster::Standard::Job.new(
35
+ name: name,
36
+ image: image,
37
+ env: env,
38
+ command: command,
39
+ backoff_limit: backoff_limit,
40
+ ttl: ttl,
41
+ ) do
42
+ spec.template.spec.serviceAccountName = name
43
+ end
44
+
45
+ super(*service_account_with_role.to_a, job)
46
+
47
+ instance_exec(&block) if block
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ test do
55
+ describe "JobWithServiceAccount" do
56
+ it "emits the RBAC trio plus the job" do
57
+ m = Kube::Cluster::Standard::JobWithServiceAccount.new(
58
+ name: "seed",
59
+ image: "nixery.dev/shell/kubectl",
60
+ rules: ["batch/jobs" => %w[create]],
61
+ command: ["true"],
62
+ )
63
+
64
+ m.map { |r| r.to_h[:kind] }.sort.should == %w[Job Role RoleBinding ServiceAccount]
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
6
+ module Kube
7
+ module Cluster
8
+ module Standard
9
+ # A Role with an ergonomic rules shorthand. Each rule maps a resource
10
+ # spec to its verbs; the spec is "resource" (core API group) or
11
+ # "group/resource":
12
+ #
13
+ # Role.new(rules: [
14
+ # "secrets" => %w[get list],
15
+ # "batch/cronjobs" => %w[get],
16
+ # ])
17
+ #
18
+ class Role < Kube::Cluster["Role"]
19
+ # The original rules shorthand, so a named copy can be rebuilt from it
20
+ # (rebuild downgrades the class; reconstructing keeps it a Standard::Role).
21
+ attr_reader :rules_input
22
+
23
+ def initialize(rules:, name: nil, &block)
24
+ @rules_input = rules
25
+
26
+ super() do
27
+ metadata.name = name if name
28
+ self.rules = Role.build_rules(rules)
29
+ instance_exec(&block) if block
30
+ end
31
+ end
32
+
33
+ def name
34
+ to_h.dig(:metadata, :name)
35
+ end
36
+
37
+ def self.build_rules(rules)
38
+ entries = rules.is_a?(Hash) ? [rules] : Array(rules)
39
+
40
+ entries.flat_map do |entry|
41
+ entry.map do |spec, verbs|
42
+ group, resource = spec.include?("/") ? spec.split("/", 2) : ["", spec]
43
+ { apiGroups: [group], resources: [resource], verbs: Array(verbs) }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ test do
53
+ describe "Role" do
54
+ it "expands the rules shorthand" do
55
+ yaml = Kube::Cluster::Standard::Role
56
+ .new(name: "r", rules: [
57
+ "secrets" => %w[get list],
58
+ "batch/cronjobs" => %w[get],
59
+ ])
60
+ .to_yaml
61
+
62
+ yaml.include?("resources").should == true
63
+ yaml.include?("batch").should == true
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
6
+ module Kube
7
+ module Cluster
8
+ module Standard
9
+ # A RoleBinding that wires a Role to a ServiceAccount. This is just the
10
+ # binding resource -- the Role and ServiceAccount are defined separately
11
+ # (and emitted together by ServiceAccountWithRole):
12
+ #
13
+ # RoleBinding.new(role: MyRole, service_account: MyServiceAccount)
14
+ #
15
+ # The subject namespace is left blank when the ServiceAccount has none, so
16
+ # the SetNamespace middleware fills it with the target namespace.
17
+ class RoleBinding < Kube::Cluster["RoleBinding"]
18
+ def initialize(role:, service_account:, name: nil, &block)
19
+ name ||= role.name || service_account.name
20
+ role_name = role.name || name
21
+
22
+ subject = { kind: "ServiceAccount", name: service_account.name }
23
+ subject[:namespace] = service_account.namespace if service_account.namespace
24
+
25
+ super() do
26
+ metadata.name = name
27
+ self.roleRef = {
28
+ apiGroup: "rbac.authorization.k8s.io",
29
+ kind: "Role",
30
+ name: role_name,
31
+ }
32
+ self.subjects = [subject]
33
+ instance_exec(&block) if block
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ test do
42
+ describe "RoleBinding" do
43
+ it "references the role and service account" do
44
+ yaml = Kube::Cluster::Standard::RoleBinding.new(
45
+ role: Kube::Cluster::Standard::Role.new(name: "r", rules: ["secrets" => %w[get]]),
46
+ service_account: Kube::Cluster::Standard::ServiceAccount.new(name: "sa"),
47
+ ).to_yaml
48
+
49
+ yaml.include?("name: r").should == true
50
+ yaml.include?("name: sa").should == true
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
6
+ module Kube
7
+ module Cluster
8
+ module Standard
9
+ class ServiceAccount < Kube::Cluster["ServiceAccount"]
10
+ def initialize(name:, namespace: nil, &block)
11
+ super() do
12
+ metadata.name = name
13
+ metadata.namespace = namespace if namespace
14
+ instance_exec(&block) if block
15
+ end
16
+ end
17
+
18
+ def name
19
+ to_h.dig(:metadata, :name)
20
+ end
21
+
22
+ def namespace
23
+ to_h.dig(:metadata, :namespace)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ test do
31
+ describe "ServiceAccount" do
32
+ it "initializes without error" do
33
+ Kube::Cluster::Standard::ServiceAccount
34
+ .new(name: "my-sa")
35
+ .to_yaml
36
+ .is_a?(String)
37
+ .should == true
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
6
+ module Kube
7
+ module Cluster
8
+ module Standard
9
+ # Bundles a ServiceAccount, a Role, and the RoleBinding that ties them
10
+ # together into one Manifest. The Role and RoleBinding take the
11
+ # ServiceAccount's name unless a name is given explicitly:
12
+ #
13
+ # ServiceAccountWithRole.new(
14
+ # service_account: ServiceAccount.new(name: "glauth-config-builder"),
15
+ # role: Role.new(rules: [
16
+ # "secrets" => %w[get list],
17
+ # "batch/cronjobs" => %w[get],
18
+ # ]),
19
+ # )
20
+ #
21
+ class ServiceAccountWithRole < Kube::Cluster::Manifest
22
+ def initialize(service_account:, role:, name: nil, &block)
23
+ name ||= service_account.name
24
+
25
+ # Rebuild the Role with the derived name (a Standard::Role, so the
26
+ # binding's role.name reader works).
27
+ named_role = Kube::Cluster::Standard::Role.new(name: name, rules: role.rules_input)
28
+
29
+ role_binding = Kube::Cluster::Standard::RoleBinding.new(
30
+ role: named_role,
31
+ service_account: service_account,
32
+ name: name,
33
+ )
34
+
35
+ super(service_account, named_role, role_binding)
36
+
37
+ instance_exec(&block) if block
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ test do
45
+ describe "ServiceAccountWithRole" do
46
+ it "emits the service account, role, and binding" do
47
+ m = Kube::Cluster::Standard::ServiceAccountWithRole.new(
48
+ service_account: Kube::Cluster::Standard::ServiceAccount.new(name: "sa"),
49
+ role: Kube::Cluster::Standard::Role.new(rules: ["secrets" => %w[get list]]),
50
+ )
51
+
52
+ m.map { |r| r.to_h[:kind] }.sort.should == %w[Role RoleBinding ServiceAccount]
53
+ end
54
+ end
55
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Kube
4
4
  module Cluster
5
- VERSION = "0.15.0"
5
+ VERSION = "0.17.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kube_cluster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan K
@@ -218,6 +218,7 @@ files:
218
218
  - lib/kube/cluster/standard/cloud_native_pg/helm.rb
219
219
  - lib/kube/cluster/standard/config_map.rb
220
220
  - lib/kube/cluster/standard/cron_job.rb
221
+ - lib/kube/cluster/standard/cron_job_with_service_account.rb
221
222
  - lib/kube/cluster/standard/custom_resource_definition.rb
222
223
  - lib/kube/cluster/standard/daemon_set.rb
223
224
  - lib/kube/cluster/standard/deployment_with_service.rb
@@ -226,14 +227,19 @@ files:
226
227
  - lib/kube/cluster/standard/forgejo/helm.rb
227
228
  - lib/kube/cluster/standard/gateway_api/http_route.rb
228
229
  - lib/kube/cluster/standard/job.rb
230
+ - lib/kube/cluster/standard/job_with_service_account.rb
229
231
  - lib/kube/cluster/standard/kube_virt/virtual_machine.rb
230
232
  - lib/kube/cluster/standard/meta_controller/composite_controller.rb
231
233
  - lib/kube/cluster/standard/meta_controller/decorator_controller.rb
232
234
  - lib/kube/cluster/standard/perses/perses.rb
233
235
  - lib/kube/cluster/standard/perses/perses_datasource.rb
234
236
  - lib/kube/cluster/standard/persistent_volume_claim.rb
237
+ - lib/kube/cluster/standard/role.rb
238
+ - lib/kube/cluster/standard/role_binding.rb
235
239
  - lib/kube/cluster/standard/secret.rb
236
240
  - lib/kube/cluster/standard/service.rb
241
+ - lib/kube/cluster/standard/service_account.rb
242
+ - lib/kube/cluster/standard/service_account_with_role.rb
237
243
  - lib/kube/cluster/standard/victoria_metrics/vl_agent.rb
238
244
  - lib/kube/cluster/standard/victoria_metrics/vl_single.rb
239
245
  - lib/kube/cluster/standard/victoria_metrics/vm_agent.rb