kube_cluster 1.7.0 → 1.8.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/CHANGELOG.md +27 -0
- data/Gemfile.lock +3 -3
- data/kube_cluster.gemspec +1 -1
- data/lib/kube/cluster/middleware/set_namespace.rb +83 -14
- data/lib/kube/cluster/standard/cluster_role.rb +69 -0
- data/lib/kube/cluster/standard/cluster_role_binding.rb +56 -0
- data/lib/kube/cluster/standard/rbac_rules.rb +111 -0
- data/lib/kube/cluster/standard/role.rb +6 -11
- data/lib/kube/cluster/version.rb +1 -1
- data/lib/kube/cluster.rb +2 -0
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 63bb454e7b06393a663b744754a36bbd4bf3e411a7b062e42f1a1db3cca08ec3
|
|
4
|
+
data.tar.gz: 38f4c283662fc9aab31e22efb373188ec8c9b0cf7ae1fa46c1f49e8013599edc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85b42c1eb2f29f6789af473465fbbfd45d4e21ea167a2bef10697b1d13f1801e4b88ac397523b20e1bc6dc672c439113609bdcc87fcb8d7af41df69c2bb2ca41
|
|
7
|
+
data.tar.gz: 371b122e930400db8ec2400f138902ee6fe454427955bb547ad4db318b4c908ed3953c7b4be0e6ecf6f581a9b3c8c46641564a83a8080b49136cfb44de9f3b02
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.8.0] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `Standard::RbacRules` — the rules-shorthand builder behind
|
|
12
|
+
`Standard::Role`, now a shared module. The spec grammar handles
|
|
13
|
+
`"resource"`, `"resource/subresource"`, `"group/resource"` and
|
|
14
|
+
`"group/resource/subresource"`: the first segment is an API group when
|
|
15
|
+
`Kube::Schema.api_groups` knows it (`batch`, `apps`, `argoproj.io`, ...) or
|
|
16
|
+
when it contains a dot (API groups are DNS subdomains, which covers CRD
|
|
17
|
+
groups no schema has registered). Verbs are stringified, so symbols work.
|
|
18
|
+
- `Standard::ClusterRole` — the rules shorthand, cluster-wide.
|
|
19
|
+
- `Standard::ClusterRoleBinding` — wires a ClusterRole to a ServiceAccount,
|
|
20
|
+
mirroring `Standard::RoleBinding`.
|
|
21
|
+
- `Middleware::SetNamespace` now fills blank ServiceAccount subject
|
|
22
|
+
namespaces on ClusterRoleBinding as well as RoleBinding. ClusterRoleBinding
|
|
23
|
+
is cluster-scoped, so there is no metadata.namespace to copy from — the
|
|
24
|
+
middleware fills the subjects directly.
|
|
25
|
+
- `ClusterRole` and `ClusterRoleBinding` pinned in the resolve table.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- Requires kube_schema `~> 1.11.0` for `Kube::Schema.api_groups`.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- The rules shorthand parsed `"pods/exec"` as `apiGroups: ["pods"],
|
|
32
|
+
resources: ["exec"]` — a rule matching nothing. Core subresources now
|
|
33
|
+
render as `apiGroups: [""], resources: ["pods/exec"]`.
|
|
34
|
+
|
|
8
35
|
## [1.7.0] - 2026-09-17
|
|
9
36
|
|
|
10
37
|
### Added
|
data/Gemfile.lock
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
kube_cluster (1.
|
|
4
|
+
kube_cluster (1.8.0)
|
|
5
5
|
activesupport (~> 8.0)
|
|
6
6
|
kube_kubectl (~> 2.0)
|
|
7
|
-
kube_schema (~> 1.
|
|
7
|
+
kube_schema (~> 1.11.0)
|
|
8
8
|
|
|
9
9
|
GEM
|
|
10
10
|
remote: https://rubygems.org/
|
|
@@ -132,7 +132,7 @@ GEM
|
|
|
132
132
|
rubyshell (~> 1.5)
|
|
133
133
|
shellwords (~> 0.2.2)
|
|
134
134
|
string_builder (~> 1.2.2)
|
|
135
|
-
kube_schema (1.
|
|
135
|
+
kube_schema (1.11.0)
|
|
136
136
|
json_schemer (~> 2.5.0)
|
|
137
137
|
rubyshell (~> 1.5.0)
|
|
138
138
|
lefthook (2.1.10)
|
data/kube_cluster.gemspec
CHANGED
|
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
spec.add_development_dependency "scampi", "~> 1.0"
|
|
33
33
|
spec.add_development_dependency "lefthook", "~> 2.1"
|
|
34
34
|
|
|
35
|
-
spec.add_dependency "kube_schema", "~> 1.
|
|
35
|
+
spec.add_dependency "kube_schema", "~> 1.11.0"
|
|
36
36
|
spec.add_dependency "kube_kubectl", "~> 2.0"
|
|
37
37
|
spec.add_dependency "activesupport", "~> 8.0"
|
|
38
38
|
end
|
|
@@ -21,6 +21,29 @@ module Kube
|
|
|
21
21
|
def call(manifest)
|
|
22
22
|
manifest.resources.map! { |resource|
|
|
23
23
|
filter(resource) {
|
|
24
|
+
# A binding's ServiceAccount subjects need an explicit
|
|
25
|
+
# namespace; fill in any left blank with the target namespace so
|
|
26
|
+
# same-namespace bindings (e.g. ServiceAccountWithRole) resolve.
|
|
27
|
+
# ClusterRoleBinding is cluster-scoped, so this runs before the
|
|
28
|
+
# scope check below.
|
|
29
|
+
if ['RoleBinding', 'ClusterRoleBinding'].include?(resource.kind)
|
|
30
|
+
h = resource.to_h
|
|
31
|
+
if h[:subjects].is_a?(Array)
|
|
32
|
+
filled = h[:subjects].map { |subject|
|
|
33
|
+
if subject[:kind] == 'ServiceAccount' &&
|
|
34
|
+
(subject[:namespace].nil? || subject[:namespace].to_s.empty?)
|
|
35
|
+
subject.merge(namespace: @namespace)
|
|
36
|
+
else
|
|
37
|
+
subject
|
|
38
|
+
end
|
|
39
|
+
}
|
|
40
|
+
if filled != h[:subjects]
|
|
41
|
+
h[:subjects] = filled
|
|
42
|
+
resource = resource.rebuild(h)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
24
47
|
next resource if resource.cluster_scoped?
|
|
25
48
|
|
|
26
49
|
h = resource.to_h
|
|
@@ -30,20 +53,6 @@ module Kube
|
|
|
30
53
|
h[:metadata][:namespace] = @namespace
|
|
31
54
|
end
|
|
32
55
|
|
|
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
|
-
|
|
47
56
|
resource.rebuild(h)
|
|
48
57
|
}
|
|
49
58
|
}
|
|
@@ -52,3 +61,63 @@ module Kube
|
|
|
52
61
|
end
|
|
53
62
|
end
|
|
54
63
|
end
|
|
64
|
+
|
|
65
|
+
__END__
|
|
66
|
+
|
|
67
|
+
Middleware = Kube::Cluster::Middleware
|
|
68
|
+
|
|
69
|
+
it "sets the namespace on namespaced resources" do
|
|
70
|
+
m = manifest(Kube::Cluster["ConfigMap"].new { metadata.name = "test" })
|
|
71
|
+
|
|
72
|
+
Middleware::SetNamespace.new("production").call(m)
|
|
73
|
+
|
|
74
|
+
m.resources.first.to_h.dig(:metadata, :namespace).should == "production"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "fills blank ServiceAccount subject namespaces on RoleBinding" do
|
|
78
|
+
m = manifest(Kube::Cluster["RoleBinding"].new {
|
|
79
|
+
metadata.name = "rb"
|
|
80
|
+
self.roleRef = { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "r" }
|
|
81
|
+
self.subjects = [{ kind: "ServiceAccount", name: "sa" }]
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
Middleware::SetNamespace.new("production").call(m)
|
|
85
|
+
rb = m.resources.first.to_h
|
|
86
|
+
|
|
87
|
+
rb.dig(:subjects, 0, :namespace).should == "production"
|
|
88
|
+
rb.dig(:metadata, :namespace).should == "production"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "fills blank ServiceAccount subject namespaces on cluster-scoped ClusterRoleBinding" do
|
|
92
|
+
m = manifest(Kube::Cluster["ClusterRoleBinding"].new {
|
|
93
|
+
metadata.name = "crb"
|
|
94
|
+
self.roleRef = { apiGroup: "rbac.authorization.k8s.io", kind: "ClusterRole", name: "cr" }
|
|
95
|
+
self.subjects = [{ kind: "ServiceAccount", name: "sa" }]
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
Middleware::SetNamespace.new("production").call(m)
|
|
99
|
+
crb = m.resources.first.to_h
|
|
100
|
+
|
|
101
|
+
crb.dig(:subjects, 0, :namespace).should == "production"
|
|
102
|
+
crb.dig(:metadata, :namespace).should.be.nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "leaves pre-set subject namespaces alone" do
|
|
106
|
+
m = manifest(Kube::Cluster["ClusterRoleBinding"].new {
|
|
107
|
+
metadata.name = "crb"
|
|
108
|
+
self.roleRef = { apiGroup: "rbac.authorization.k8s.io", kind: "ClusterRole", name: "cr" }
|
|
109
|
+
self.subjects = [{ kind: "ServiceAccount", name: "sa", namespace: "other" }]
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
Middleware::SetNamespace.new("production").call(m)
|
|
113
|
+
|
|
114
|
+
m.resources.first.to_h.dig(:subjects, 0, :namespace).should == "other"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def manifest(*resources)
|
|
120
|
+
m = Kube::Cluster::Manifest.new
|
|
121
|
+
resources.each { |r| m << r }
|
|
122
|
+
m
|
|
123
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "kube/cluster"
|
|
5
|
+
require "kube/cluster/standard/rbac_rules"
|
|
6
|
+
|
|
7
|
+
module Kube
|
|
8
|
+
module Cluster
|
|
9
|
+
module Standard
|
|
10
|
+
# A ClusterRole with the same rules shorthand as Role (see RbacRules):
|
|
11
|
+
#
|
|
12
|
+
# ClusterRole.new(name: "node-reader", rules: {
|
|
13
|
+
# "nodes" => %w[get list],
|
|
14
|
+
# "nodes/proxy" => %w[get],
|
|
15
|
+
# "argoproj.io/workflows" => %w[get list],
|
|
16
|
+
# })
|
|
17
|
+
#
|
|
18
|
+
class ClusterRole < Kube::Cluster["ClusterRole"]
|
|
19
|
+
# The original rules shorthand, so a named copy can be rebuilt from it
|
|
20
|
+
# (rebuild downgrades the class; reconstructing keeps it a
|
|
21
|
+
# Standard::ClusterRole).
|
|
22
|
+
attr_reader :rules_input
|
|
23
|
+
|
|
24
|
+
def initialize(rules:, name: nil, &block)
|
|
25
|
+
@rules_input = rules
|
|
26
|
+
|
|
27
|
+
super() do
|
|
28
|
+
metadata.name = name if name
|
|
29
|
+
self.rules = RbacRules.build(rules)
|
|
30
|
+
instance_exec(&block) if block
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def name
|
|
35
|
+
to_h.dig(:metadata, :name)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
__END__
|
|
43
|
+
|
|
44
|
+
describe "ClusterRole" do
|
|
45
|
+
it "expands the rules shorthand" do
|
|
46
|
+
yaml = Kube::Cluster::Standard::ClusterRole
|
|
47
|
+
.new(name: "r", rules: {
|
|
48
|
+
"namespaces" => %w[get list],
|
|
49
|
+
"pods/log" => %w[get],
|
|
50
|
+
"argoproj.io/workflows" => %w[get list],
|
|
51
|
+
"batch/cronjobs" => %w[get],
|
|
52
|
+
})
|
|
53
|
+
.to_yaml
|
|
54
|
+
|
|
55
|
+
yaml.include?("kind: ClusterRole").should == true
|
|
56
|
+
yaml.include?("- namespaces").should == true
|
|
57
|
+
yaml.include?("- pods/log").should == true
|
|
58
|
+
yaml.include?("- argoproj.io").should == true
|
|
59
|
+
yaml.include?("- batch").should == true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "renders core subresources in the core group (not as apiGroups: [pods])" do
|
|
63
|
+
rules = Kube::Cluster::Standard::ClusterRole
|
|
64
|
+
.new(name: "r", rules: { "pods/exec" => %w[create] })
|
|
65
|
+
.to_h[:rules]
|
|
66
|
+
|
|
67
|
+
rules.should == [{ apiGroups: [""], resources: ["pods/exec"], verbs: %w[create] }]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
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 ClusterRoleBinding that wires a ClusterRole to a ServiceAccount.
|
|
10
|
+
# Mirrors RoleBinding:
|
|
11
|
+
#
|
|
12
|
+
# ClusterRoleBinding.new(role: MyClusterRole, service_account: MyServiceAccount)
|
|
13
|
+
#
|
|
14
|
+
# The subject namespace comes from the ServiceAccount when it has one,
|
|
15
|
+
# and is otherwise left blank for the SetNamespace middleware to fill --
|
|
16
|
+
# ClusterRoleBinding is cluster-scoped, so there is no metadata.namespace
|
|
17
|
+
# to copy from, but the middleware fills binding subjects directly.
|
|
18
|
+
class ClusterRoleBinding < Kube::Cluster["ClusterRoleBinding"]
|
|
19
|
+
def initialize(role:, service_account:, name: nil, &block)
|
|
20
|
+
name ||= role.name || service_account.name
|
|
21
|
+
role_name = role.name || name
|
|
22
|
+
|
|
23
|
+
subject = { kind: "ServiceAccount", name: service_account.name }
|
|
24
|
+
subject[:namespace] = service_account.namespace if service_account.namespace
|
|
25
|
+
|
|
26
|
+
super() do
|
|
27
|
+
metadata.name = name
|
|
28
|
+
self.roleRef = {
|
|
29
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
30
|
+
kind: "ClusterRole",
|
|
31
|
+
name: role_name,
|
|
32
|
+
}
|
|
33
|
+
self.subjects = [subject]
|
|
34
|
+
instance_exec(&block) if block
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
__END__
|
|
43
|
+
|
|
44
|
+
describe "ClusterRoleBinding" do
|
|
45
|
+
it "references the cluster role and service account" do
|
|
46
|
+
yaml = Kube::Cluster::Standard::ClusterRoleBinding.new(
|
|
47
|
+
role: Kube::Cluster::Standard::ClusterRole.new(name: "cr", rules: { "nodes" => %w[get] }),
|
|
48
|
+
service_account: Kube::Cluster::Standard::ServiceAccount.new(name: "sa"),
|
|
49
|
+
).to_yaml
|
|
50
|
+
|
|
51
|
+
yaml.include?("kind: ClusterRoleBinding").should == true
|
|
52
|
+
yaml.include?("kind: ClusterRole").should == true
|
|
53
|
+
yaml.include?("name: cr").should == true
|
|
54
|
+
yaml.include?("name: sa").should == true
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
# Builds Kubernetes RBAC PolicyRule arrays from a shorthand hash of
|
|
10
|
+
# resource-spec => verbs. The spec is "resource" (core API group),
|
|
11
|
+
# "resource/subresource" (core), "group/resource", or
|
|
12
|
+
# "group/resource/subresource":
|
|
13
|
+
#
|
|
14
|
+
# RbacRules.build(
|
|
15
|
+
# "secrets" => %w[get list],
|
|
16
|
+
# "pods/log" => %w[get],
|
|
17
|
+
# "batch/cronjobs" => %w[get],
|
|
18
|
+
# "argoproj.io/workflows" => %w[get list],
|
|
19
|
+
# "subresources.kubevirt.io/virtualmachines/restart" => %w[create],
|
|
20
|
+
# )
|
|
21
|
+
#
|
|
22
|
+
# The first path segment is an API group when the schema knows it as
|
|
23
|
+
# one (Kube::Schema.api_groups -- "batch", "apps", "argoproj.io", ...)
|
|
24
|
+
# or when it contains a dot: API groups are DNS subdomains, so a dotted
|
|
25
|
+
# segment is a group even when no schema has registered it (e.g. a CRD
|
|
26
|
+
# group local to one cluster). Anything else is a core resource,
|
|
27
|
+
# subresource included.
|
|
28
|
+
module RbacRules
|
|
29
|
+
def self.build(rules)
|
|
30
|
+
entries = rules.is_a?(Hash) ? [rules] : Array(rules)
|
|
31
|
+
|
|
32
|
+
entries.flat_map do |entry|
|
|
33
|
+
entry.map do |spec, verbs|
|
|
34
|
+
group, resource = split_spec(spec.to_s)
|
|
35
|
+
{ apiGroups: [group], resources: [resource], verbs: Array(verbs).map(&:to_s) }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.split_spec(spec)
|
|
41
|
+
return ["", spec] unless spec.include?("/")
|
|
42
|
+
|
|
43
|
+
first, rest = spec.split("/", 2)
|
|
44
|
+
if Kube::Schema.api_groups.include?(first) || first.include?(".")
|
|
45
|
+
[first, rest]
|
|
46
|
+
else
|
|
47
|
+
["", spec]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
__END__
|
|
56
|
+
|
|
57
|
+
describe "RbacRules" do
|
|
58
|
+
it "expands core resources into the empty group" do
|
|
59
|
+
Kube::Cluster::Standard::RbacRules
|
|
60
|
+
.build("secrets" => %w[get list])
|
|
61
|
+
.should == [{ apiGroups: [""], resources: ["secrets"], verbs: %w[get list] }]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "keeps core subresources with their resource" do
|
|
65
|
+
Kube::Cluster::Standard::RbacRules
|
|
66
|
+
.build("pods/log" => %w[get])
|
|
67
|
+
.should == [{ apiGroups: [""], resources: ["pods/log"], verbs: %w[get] }]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "splits groups the schema knows even without a dot (apps, batch)" do
|
|
71
|
+
Kube::Cluster::Standard::RbacRules
|
|
72
|
+
.build("batch/cronjobs" => %w[get], "apps/deployments" => %w[get])
|
|
73
|
+
.should == [
|
|
74
|
+
{ apiGroups: ["batch"], resources: ["cronjobs"], verbs: %w[get] },
|
|
75
|
+
{ apiGroups: ["apps"], resources: ["deployments"], verbs: %w[get] },
|
|
76
|
+
]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "splits dotted groups" do
|
|
80
|
+
Kube::Cluster::Standard::RbacRules
|
|
81
|
+
.build("argoproj.io/workflows" => %w[get list])
|
|
82
|
+
.should == [{ apiGroups: ["argoproj.io"], resources: ["workflows"], verbs: %w[get list] }]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "keeps the subresource on grouped resources" do
|
|
86
|
+
Kube::Cluster::Standard::RbacRules
|
|
87
|
+
.build("subresources.kubevirt.io/virtualmachines/restart" => %w[create])
|
|
88
|
+
.should == [{ apiGroups: ["subresources.kubevirt.io"], resources: ["virtualmachines/restart"], verbs: %w[create] }]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "treats an unregistered dotted group as a group" do
|
|
92
|
+
Kube::Cluster::Standard::RbacRules
|
|
93
|
+
.build("tradeportal.ai/tenantvmclaims" => %w[get])
|
|
94
|
+
.should == [{ apiGroups: ["tradeportal.ai"], resources: ["tenantvmclaims"], verbs: %w[get] }]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "stringifies symbol verbs" do
|
|
98
|
+
Kube::Cluster::Standard::RbacRules
|
|
99
|
+
.build("secrets" => [:get, :list])
|
|
100
|
+
.should == [{ apiGroups: [""], resources: ["secrets"], verbs: %w[get list] }]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "accepts an array of hashes as well as a single hash" do
|
|
104
|
+
Kube::Cluster::Standard::RbacRules
|
|
105
|
+
.build([{ "secrets" => %w[get] }, { "pods" => %w[list] }])
|
|
106
|
+
.should == [
|
|
107
|
+
{ apiGroups: [""], resources: ["secrets"], verbs: %w[get] },
|
|
108
|
+
{ apiGroups: [""], resources: ["pods"], verbs: %w[list] },
|
|
109
|
+
]
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
require "bundler/setup"
|
|
4
4
|
require "kube/cluster"
|
|
5
|
+
require "kube/cluster/standard/rbac_rules"
|
|
5
6
|
|
|
6
7
|
module Kube
|
|
7
8
|
module Cluster
|
|
8
9
|
module Standard
|
|
9
10
|
# A Role with an ergonomic rules shorthand. Each rule maps a resource
|
|
10
|
-
# spec to its verbs; the spec
|
|
11
|
-
# "group/resource":
|
|
11
|
+
# spec to its verbs; the spec grammar ("resource", "resource/subresource",
|
|
12
|
+
# "group/resource", "group/resource/subresource") is RbacRules':
|
|
12
13
|
#
|
|
13
14
|
# Role.new(rules: [
|
|
14
15
|
# "secrets" => %w[get list],
|
|
16
|
+
# "pods/log" => %w[get],
|
|
15
17
|
# "batch/cronjobs" => %w[get],
|
|
16
18
|
# ])
|
|
17
19
|
#
|
|
@@ -25,7 +27,7 @@ module Kube
|
|
|
25
27
|
|
|
26
28
|
super() do
|
|
27
29
|
metadata.name = name if name
|
|
28
|
-
self.rules =
|
|
30
|
+
self.rules = RbacRules.build(rules)
|
|
29
31
|
instance_exec(&block) if block
|
|
30
32
|
end
|
|
31
33
|
end
|
|
@@ -35,14 +37,7 @@ module Kube
|
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
def self.build_rules(rules)
|
|
38
|
-
|
|
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
|
|
40
|
+
RbacRules.build(rules)
|
|
46
41
|
end
|
|
47
42
|
end
|
|
48
43
|
end
|
data/lib/kube/cluster/version.rb
CHANGED
data/lib/kube/cluster.rb
CHANGED
|
@@ -103,6 +103,8 @@ Kube::Cluster.config do
|
|
|
103
103
|
resolve "Job", to: "batch/v1/Job"
|
|
104
104
|
resolve "Role", to: "rbac.authorization.k8s.io/v1/Role"
|
|
105
105
|
resolve "RoleBinding", to: "rbac.authorization.k8s.io/v1/RoleBinding"
|
|
106
|
+
resolve "ClusterRole", to: "rbac.authorization.k8s.io/v1/ClusterRole"
|
|
107
|
+
resolve "ClusterRoleBinding", to: "rbac.authorization.k8s.io/v1/ClusterRoleBinding"
|
|
106
108
|
resolve "CustomResourceDefinition", to: "apiextensions.k8s.io/v1/CustomResourceDefinition"
|
|
107
109
|
|
|
108
110
|
# Metacontroller.
|
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: 1.
|
|
4
|
+
version: 1.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nathan K
|
|
@@ -57,14 +57,14 @@ dependencies:
|
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 1.
|
|
60
|
+
version: 1.11.0
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 1.
|
|
67
|
+
version: 1.11.0
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: kube_kubectl
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -219,6 +219,8 @@ files:
|
|
|
219
219
|
- lib/kube/cluster/standard/cloud_native_pg/database_with_external_secret.rb
|
|
220
220
|
- lib/kube/cluster/standard/cloud_native_pg/external_secret.rb
|
|
221
221
|
- lib/kube/cluster/standard/cloud_native_pg/helm.rb
|
|
222
|
+
- lib/kube/cluster/standard/cluster_role.rb
|
|
223
|
+
- lib/kube/cluster/standard/cluster_role_binding.rb
|
|
222
224
|
- lib/kube/cluster/standard/config_map.rb
|
|
223
225
|
- lib/kube/cluster/standard/cron_job.rb
|
|
224
226
|
- lib/kube/cluster/standard/cron_job_with_service_account.rb
|
|
@@ -239,6 +241,7 @@ files:
|
|
|
239
241
|
- lib/kube/cluster/standard/perses/perses.rb
|
|
240
242
|
- lib/kube/cluster/standard/perses/perses_datasource.rb
|
|
241
243
|
- lib/kube/cluster/standard/persistent_volume_claim.rb
|
|
244
|
+
- lib/kube/cluster/standard/rbac_rules.rb
|
|
242
245
|
- lib/kube/cluster/standard/role.rb
|
|
243
246
|
- lib/kube/cluster/standard/role_binding.rb
|
|
244
247
|
- lib/kube/cluster/standard/secret.rb
|