feat: implement strategic merge patching for API server admission config

The testcase explains it better, but tl;dr is that this allows to do
strategic merge patching e.g. for the Pod Security configuration.

Fixes #5895

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Andrey Smirnov 2022-07-15 23:54:15 +04:00
parent be98cb82b5
commit f1c2b5c558
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
7 changed files with 344 additions and 29 deletions

View File

@ -42,6 +42,10 @@ var (
//nolint:gocyclo,cyclop
func merge(vl, vr reflect.Value, replace bool) error {
if vl == zeroValue && vr == zeroValue {
return nil
}
tl, tr := vl.Type(), vr.Type()
if tl != tr {
@ -53,7 +57,7 @@ func merge(vl, vr reflect.Value, replace bool) error {
}
switch tl.Kind() { //nolint:exhaustive
case reflect.Pointer:
case reflect.Pointer, reflect.Interface:
if vr.IsZero() {
return nil
}
@ -102,11 +106,32 @@ func merge(vl, vr reflect.Value, replace bool) error {
for _, k := range vr.MapKeys() {
if vl.MapIndex(k) != zeroValue {
v := reflect.New(tl.Elem()).Elem()
v.Set(vl.MapIndex(k))
valueType := tl.Elem()
if err := merge(v, vr.MapIndex(k), false); err != nil {
return err
var v, rightV reflect.Value
if valueType.Kind() == reflect.Interface { // special case for map[string]interface{}
// here we have to instantiate the actual type behind in the `interface{}`, otherwise it's not settable
valueType = vl.MapIndex(k).Elem().Type()
if valueType != vr.MapIndex(k).Elem().Type() {
return fmt.Errorf("merge type mismatch left %v right %v", valueType, vr.MapIndex(k).Elem().Type())
}
v = reflect.New(valueType).Elem()
v.Set(vl.MapIndex(k).Elem())
rightV = reflect.New(valueType).Elem()
rightV.Set(vr.MapIndex(k).Elem())
} else { // "normal" maps
v = reflect.New(valueType).Elem()
v.Set(vl.MapIndex(k))
rightV = vr.MapIndex(k)
}
if err := merge(v, rightV, false); err != nil {
return fmt.Errorf("merge map key %v[%v]: %w", tl, k, err)
}
vl.SetMapIndex(k, v)
@ -135,7 +160,7 @@ func merge(vl, vr reflect.Value, replace bool) error {
fr := vr.FieldByIndex(tr.Field(i).Index)
if err := merge(fl, fr, replace); err != nil {
return fmt.Errorf("merge field %v.%v: %v", tl, tl.Field(i).Name, err)
return fmt.Errorf("merge field %v.%v: %w", tl, tl.Field(i).Name, err)
}
}
case

View File

@ -45,18 +45,20 @@ func (s *CustomSlice) Merge(other interface{}) error {
return nil
}
type Unstructured map[string]interface{}
func TestMerge(t *testing.T) {
for _, tt := range []struct {
name string
left, right Config
expected Config
left, right interface{}
expected interface{}
}{
{
name: "zero",
},
{
name: "partial merge",
left: Config{
left: &Config{
A: "a",
B: 3,
C: pointer.To(true),
@ -75,7 +77,7 @@ func TestMerge(t *testing.T) {
},
},
},
right: Config{
right: &Config{
A: "aa",
B: 4,
Slice: []Struct{
@ -97,7 +99,7 @@ func TestMerge(t *testing.T) {
},
},
},
expected: Config{
expected: &Config{
A: "aa",
B: 4,
C: pointer.To(true),
@ -127,7 +129,7 @@ func TestMerge(t *testing.T) {
},
{
name: "merge with zero",
left: Config{
left: &Config{
A: "a",
B: 3,
C: pointer.To(true),
@ -146,8 +148,8 @@ func TestMerge(t *testing.T) {
},
},
},
right: Config{},
expected: Config{
right: &Config{},
expected: &Config{
A: "a",
B: 3,
C: pointer.To(true),
@ -169,8 +171,8 @@ func TestMerge(t *testing.T) {
},
{
name: "merge from zero",
left: Config{},
right: Config{
left: &Config{},
right: &Config{
A: "a",
B: 3,
C: pointer.To(true),
@ -189,7 +191,7 @@ func TestMerge(t *testing.T) {
},
},
},
expected: Config{
expected: &Config{
A: "a",
B: 3,
C: pointer.To(true),
@ -211,41 +213,74 @@ func TestMerge(t *testing.T) {
},
{
name: "replace slice",
left: Config{
left: &Config{
ReplacedSlice: []string{"a", "b"},
},
right: Config{
right: &Config{
ReplacedSlice: []string{"c", "d"},
},
expected: Config{
expected: &Config{
ReplacedSlice: []string{"c", "d"},
},
},
{
name: "zero slice",
left: Config{},
right: Config{
left: &Config{},
right: &Config{
Slice: []Struct{},
},
expected: Config{
expected: &Config{
Slice: []Struct{},
},
},
{
name: "custom slice",
left: Config{
left: &Config{
CustomSlice: []string{"a", "c"},
},
right: Config{
right: &Config{
CustomSlice: []string{"b", "d"},
},
expected: Config{
expected: &Config{
CustomSlice: []string{"a", "b", "c", "d"},
},
},
{
name: "unstructured",
left: &Unstructured{
"a": "aa",
"map": map[string]interface{}{
"slice": []interface{}{
"s1",
},
"some": "value",
},
},
right: &Unstructured{
"b": "bb",
"map": map[string]interface{}{
"slice": []interface{}{
"s2",
},
"other": "thing",
},
},
expected: &Unstructured{
"a": "aa",
"b": "bb",
"map": map[string]interface{}{
"slice": []interface{}{
"s1",
"s2",
},
"some": "value",
"other": "thing",
},
},
},
} {
t.Run(tt.name, func(t *testing.T) {
err := merge.Merge(&tt.left, &tt.right)
err := merge.Merge(tt.left, tt.right)
require.NoError(t, err)
assert.Equal(t, tt.expected, tt.left)

View File

@ -0,0 +1,89 @@
version: v1alpha1
debug: false
persist: true
machine:
type: controlplane
token: u8ei4i.iymakyzguuqaw30r
ca:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K
certSANs: []
kubelet:
image: ghcr.io/siderolabs/kubelet:v1.24.2
network:
hostname:
interfaces:
- interface: eth0
addresses:
- 172.20.0.2/24
dhcp: true
- deviceSelector:
driver: macvtap
dhcp: false
install:
disk: /dev/sda
image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e
bootloader: true
wipe: false
features:
rbac: true
cluster:
id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ=
secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI=
controlPlane:
endpoint: https://127.0.0.1:6643/
clusterName: foo
network:
dnsDomain: cluster.local
podSubnets:
- 10.244.0.0/16
serviceSubnets:
- 10.96.0.0/12
token: 4pcl58.l0i5cv8h9k3k1az8
aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg=
ca:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
aggregatorCA:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
serviceAccount:
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
apiServer:
image: k8s.gcr.io/kube-apiserver:v1.24.2
certSANs:
- 127.0.0.1
disablePodSecurityPolicy: true
admissionControl:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1alpha1
defaults:
audit: restricted
audit-version: latest
enforce: restricted
enforce-version: latest
warn: restricted
warn-version: latest
exemptions:
namespaces:
- kube-system
- rook-system
runtimeClasses: []
usernames: []
kind: PodSecurityConfiguration
controllerManager:
image: k8s.gcr.io/kube-controller-manager:v1.24.2
proxy:
image: k8s.gcr.io/kube-proxy:v1.24.2
scheduler:
image: k8s.gcr.io/kube-scheduler:v1.24.2
discovery:
enabled: true
registries:
kubernetes: {}
service: {}
etcd:
ca:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=

View File

@ -0,0 +1,88 @@
version: v1alpha1
debug: false
persist: true
machine:
type: controlplane
token: u8ei4i.iymakyzguuqaw30r
ca:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K
certSANs: []
kubelet:
image: ghcr.io/siderolabs/kubelet:v1.24.2
network:
hostname:
interfaces:
- interface: eth0
addresses:
- 172.20.0.2/24
dhcp: true
- deviceSelector:
driver: macvtap
dhcp: false
install:
disk: /dev/sda
image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e
bootloader: true
wipe: false
features:
rbac: true
cluster:
id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ=
secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI=
controlPlane:
endpoint: https://127.0.0.1:6643/
clusterName: foo
network:
dnsDomain: cluster.local
podSubnets:
- 10.244.0.0/16
serviceSubnets:
- 10.96.0.0/12
token: 4pcl58.l0i5cv8h9k3k1az8
aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg=
ca:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
aggregatorCA:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
serviceAccount:
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
apiServer:
image: k8s.gcr.io/kube-apiserver:v1.24.2
certSANs:
- 127.0.0.1
disablePodSecurityPolicy: true
admissionControl:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1alpha1
defaults:
audit: restricted
audit-version: latest
enforce: baseline
enforce-version: latest
warn: restricted
warn-version: latest
exemptions:
namespaces:
- kube-system
runtimeClasses: []
usernames: []
kind: PodSecurityConfiguration
controllerManager:
image: k8s.gcr.io/kube-controller-manager:v1.24.2
proxy:
image: k8s.gcr.io/kube-proxy:v1.24.2
scheduler:
image: k8s.gcr.io/kube-scheduler:v1.24.2
discovery:
enabled: true
registries:
kubernetes: {}
service: {}
etcd:
ca:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=

View File

@ -0,0 +1,11 @@
cluster:
apiServer:
image: k8s.gcr.io/kube-apiserver:v1.24.2
admissionControl:
- name: PodSecurity
configuration:
defaults:
enforce: restricted
exemptions:
namespaces:
- rook-system

View File

@ -1492,7 +1492,48 @@ type APIServerConfig struct {
// Configure the API server admission plugins.
// examples:
// - value: admissionControlConfigExample
AdmissionControlConfig []*AdmissionPluginConfig `yaml:"admissionControl,omitempty"`
AdmissionControlConfig AdmissionPluginConfigList `yaml:"admissionControl,omitempty"`
}
// AdmissionPluginConfigList represents the admission plugin configuration list.
//
//docgen:alias
type AdmissionPluginConfigList []*AdmissionPluginConfig
// Merge the admission plugin configuration intelligently.
func (configs *AdmissionPluginConfigList) Merge(other interface{}) error {
otherConfigs, ok := other.(AdmissionPluginConfigList)
if !ok {
return fmt.Errorf("unexpected type for device merge %T", other)
}
for _, config := range otherConfigs {
if err := configs.mergeConfig(config); err != nil {
return err
}
}
return nil
}
func (configs *AdmissionPluginConfigList) mergeConfig(config *AdmissionPluginConfig) error {
var existing *AdmissionPluginConfig
for _, c := range *configs {
if c.PluginName == config.PluginName {
existing = c
break
}
}
if existing != nil {
return merge.Merge(existing, config)
}
*configs = append(*configs, config)
return nil
}
// AdmissionPluginConfig represents the API server admission plugin configuration.

View File

@ -43,7 +43,7 @@ func (in *APIServerConfig) DeepCopyInto(out *APIServerConfig) {
}
if in.AdmissionControlConfig != nil {
in, out := &in.AdmissionControlConfig, &out.AdmissionControlConfig
*out = make([]*AdmissionPluginConfig, len(*in))
*out = make(AdmissionPluginConfigList, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
@ -98,6 +98,32 @@ func (in *AdmissionPluginConfig) DeepCopy() *AdmissionPluginConfig {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in AdmissionPluginConfigList) DeepCopyInto(out *AdmissionPluginConfigList) {
{
in := &in
*out = make(AdmissionPluginConfigList, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(AdmissionPluginConfig)
(*in).DeepCopyInto(*out)
}
}
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionPluginConfigList.
func (in AdmissionPluginConfigList) DeepCopy() AdmissionPluginConfigList {
if in == nil {
return nil
}
out := new(AdmissionPluginConfigList)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Base64Bytes) DeepCopyInto(out *Base64Bytes) {
{