blob: 7272e888bcfda8dacb63e44ced6fb578deac1288 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package admission
18
19import (
20 "fmt"
21 "strings"
22 "sync"
23
24 "k8s.io/apimachinery/pkg/runtime"
25 "k8s.io/apimachinery/pkg/runtime/schema"
26 "k8s.io/apimachinery/pkg/util/validation"
27 "k8s.io/apiserver/pkg/authentication/user"
28)
29
30type attributesRecord struct {
31 kind schema.GroupVersionKind
32 namespace string
33 name string
34 resource schema.GroupVersionResource
35 subresource string
36 operation Operation
37 object runtime.Object
38 oldObject runtime.Object
39 userInfo user.Info
40
41 // other elements are always accessed in single goroutine.
42 // But ValidatingAdmissionWebhook add annotations concurrently.
43 annotations map[string]string
44 annotationsLock sync.RWMutex
45}
46
47func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, userInfo user.Info) Attributes {
48 return &attributesRecord{
49 kind: kind,
50 namespace: namespace,
51 name: name,
52 resource: resource,
53 subresource: subresource,
54 operation: operation,
55 object: object,
56 oldObject: oldObject,
57 userInfo: userInfo,
58 }
59}
60
61func (record *attributesRecord) GetKind() schema.GroupVersionKind {
62 return record.kind
63}
64
65func (record *attributesRecord) GetNamespace() string {
66 return record.namespace
67}
68
69func (record *attributesRecord) GetName() string {
70 return record.name
71}
72
73func (record *attributesRecord) GetResource() schema.GroupVersionResource {
74 return record.resource
75}
76
77func (record *attributesRecord) GetSubresource() string {
78 return record.subresource
79}
80
81func (record *attributesRecord) GetOperation() Operation {
82 return record.operation
83}
84
85func (record *attributesRecord) GetObject() runtime.Object {
86 return record.object
87}
88
89func (record *attributesRecord) GetOldObject() runtime.Object {
90 return record.oldObject
91}
92
93func (record *attributesRecord) GetUserInfo() user.Info {
94 return record.userInfo
95}
96
97// getAnnotations implements privateAnnotationsGetter.It's a private method used
98// by WithAudit decorator.
99func (record *attributesRecord) getAnnotations() map[string]string {
100 record.annotationsLock.RLock()
101 defer record.annotationsLock.RUnlock()
102
103 if record.annotations == nil {
104 return nil
105 }
106 cp := make(map[string]string, len(record.annotations))
107 for key, value := range record.annotations {
108 cp[key] = value
109 }
110 return cp
111}
112
113func (record *attributesRecord) AddAnnotation(key, value string) error {
114 if err := checkKeyFormat(key); err != nil {
115 return err
116 }
117
118 record.annotationsLock.Lock()
119 defer record.annotationsLock.Unlock()
120
121 if record.annotations == nil {
122 record.annotations = make(map[string]string)
123 }
124 if v, ok := record.annotations[key]; ok && v != value {
125 return fmt.Errorf("admission annotations are not allowd to be overwritten, key:%q, old value: %q, new value:%q", key, record.annotations[key], value)
126 }
127 record.annotations[key] = value
128 return nil
129}
130
131func checkKeyFormat(key string) error {
132 parts := strings.Split(key, "/")
133 if len(parts) != 2 {
134 return fmt.Errorf("annotation key has invalid format, the right format is a DNS subdomain prefix and '/' and key name. (e.g. 'podsecuritypolicy.admission.k8s.io/admit-policy')")
135 }
136 if msgs := validation.IsQualifiedName(key); len(msgs) != 0 {
137 return fmt.Errorf("annotation key has invalid format %s. A qualified name like 'podsecuritypolicy.admission.k8s.io/admit-policy' is required.", strings.Join(msgs, ","))
138 }
139 return nil
140}