mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 01:40:07 +00:00
Require nodeName for mirror pods, make mirror pod annotation immutable
This commit is contained in:
parent
5c23dc7897
commit
cd3a1187a1
@ -17,6 +17,9 @@ limitations under the License.
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// MirrorAnnotationKey represents the annotation key set by kubelets when creating mirror pods
|
||||||
|
MirrorPodAnnotationKey string = "kubernetes.io/config.mirror"
|
||||||
|
|
||||||
// TolerationsAnnotationKey represents the key of tolerations data (json serialized)
|
// TolerationsAnnotationKey represents the key of tolerations data (json serialized)
|
||||||
// in the Annotations of a Pod.
|
// in the Annotations of a Pod.
|
||||||
TolerationsAnnotationKey string = "scheduler.alpha.kubernetes.io/tolerations"
|
TolerationsAnnotationKey string = "scheduler.alpha.kubernetes.io/tolerations"
|
||||||
|
@ -111,6 +111,12 @@ func ValidatePodSpecificAnnotations(annotations map[string]string, spec *api.Pod
|
|||||||
allErrs = append(allErrs, ValidateAffinityInPodAnnotations(annotations, fldPath)...)
|
allErrs = append(allErrs, ValidateAffinityInPodAnnotations(annotations, fldPath)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if value, isMirror := annotations[api.MirrorPodAnnotationKey]; isMirror {
|
||||||
|
if len(spec.NodeName) == 0 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Key(api.MirrorPodAnnotationKey), value, "must set spec.nodeName if mirror pod annotation is set"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if annotations[api.TolerationsAnnotationKey] != "" {
|
if annotations[api.TolerationsAnnotationKey] != "" {
|
||||||
allErrs = append(allErrs, ValidateTolerationsInPodAnnotations(annotations, fldPath)...)
|
allErrs = append(allErrs, ValidateTolerationsInPodAnnotations(annotations, fldPath)...)
|
||||||
}
|
}
|
||||||
@ -177,20 +183,26 @@ func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *api.Pod, fldPath *fiel
|
|||||||
newAnnotations := newPod.Annotations
|
newAnnotations := newPod.Annotations
|
||||||
oldAnnotations := oldPod.Annotations
|
oldAnnotations := oldPod.Annotations
|
||||||
for k, oldVal := range oldAnnotations {
|
for k, oldVal := range oldAnnotations {
|
||||||
if newAnnotations[k] == oldVal {
|
if newVal, exists := newAnnotations[k]; exists && newVal == oldVal {
|
||||||
continue // No change.
|
continue // No change.
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
|
if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
|
||||||
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not update AppArmor annotations"))
|
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove or update AppArmor annotations"))
|
||||||
|
}
|
||||||
|
if k == api.MirrorPodAnnotationKey {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove or update mirror pod annotation"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check for removals.
|
// Check for additions
|
||||||
for k := range newAnnotations {
|
for k := range newAnnotations {
|
||||||
if _, ok := oldAnnotations[k]; ok {
|
if _, ok := oldAnnotations[k]; ok {
|
||||||
continue // No change.
|
continue // No change.
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
|
if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) {
|
||||||
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove AppArmor annotations"))
|
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not add AppArmor annotations"))
|
||||||
|
}
|
||||||
|
if k == api.MirrorPodAnnotationKey {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not add mirror pod annotation"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(newAnnotations, &newPod.Spec, fldPath)...)
|
allErrs = append(allErrs, ValidatePodSpecificAnnotations(newAnnotations, &newPod.Spec, fldPath)...)
|
||||||
@ -2548,9 +2560,10 @@ func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList {
|
|||||||
// ValidatePodStatusUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
|
// ValidatePodStatusUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
|
||||||
// that cannot be changed.
|
// that cannot be changed.
|
||||||
func ValidatePodStatusUpdate(newPod, oldPod *api.Pod) field.ErrorList {
|
func ValidatePodStatusUpdate(newPod, oldPod *api.Pod) field.ErrorList {
|
||||||
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, field.NewPath("metadata"))
|
fldPath := field.NewPath("metadata")
|
||||||
|
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
|
||||||
|
allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
|
||||||
|
|
||||||
// TODO: allow change when bindings are properly decoupled from pods
|
|
||||||
if newPod.Spec.NodeName != oldPod.Spec.NodeName {
|
if newPod.Spec.NodeName != oldPod.Spec.NodeName {
|
||||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("status", "nodeName"), "may not be changed directly"))
|
allErrs = append(allErrs, field.Forbidden(field.NewPath("status", "nodeName"), "may not be changed directly"))
|
||||||
}
|
}
|
||||||
|
@ -4335,6 +4335,22 @@ func TestValidatePod(t *testing.T) {
|
|||||||
DNSPolicy: api.DNSClusterFirst,
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"mirror-pod present without nodeName": {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns", Annotations: map[string]string{api.MirrorPodAnnotationKey: ""}},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
|
RestartPolicy: api.RestartPolicyAlways,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"mirror-pod populated without nodeName": {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns", Annotations: map[string]string{api.MirrorPodAnnotationKey: "foo"}},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
|
RestartPolicy: api.RestartPolicyAlways,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
if errs := ValidatePod(&v); len(errs) == 0 {
|
if errs := ValidatePod(&v); len(errs) == 0 {
|
||||||
@ -5056,6 +5072,30 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
"added invalid new toleration to existing tolerations in pod spec updates",
|
"added invalid new toleration to existing tolerations in pod spec updates",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.PodSpec{NodeName: "foo"}},
|
||||||
|
api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
|
||||||
|
false,
|
||||||
|
"removed nodeName from pod spec",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{api.MirrorPodAnnotationKey: ""}}, Spec: api.PodSpec{NodeName: "foo"}},
|
||||||
|
api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.PodSpec{NodeName: "foo"}},
|
||||||
|
false,
|
||||||
|
"removed mirror pod annotation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.PodSpec{NodeName: "foo"}},
|
||||||
|
api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{api.MirrorPodAnnotationKey: ""}}, Spec: api.PodSpec{NodeName: "foo"}},
|
||||||
|
false,
|
||||||
|
"added mirror pod annotation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{api.MirrorPodAnnotationKey: "foo"}}, Spec: api.PodSpec{NodeName: "foo"}},
|
||||||
|
api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{api.MirrorPodAnnotationKey: "bar"}}, Spec: api.PodSpec{NodeName: "foo"}},
|
||||||
|
false,
|
||||||
|
"changed mirror pod annotation",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ConfigSourceAnnotationKey = "kubernetes.io/config.source"
|
ConfigSourceAnnotationKey = "kubernetes.io/config.source"
|
||||||
ConfigMirrorAnnotationKey = "kubernetes.io/config.mirror"
|
ConfigMirrorAnnotationKey = kubeapi.MirrorPodAnnotationKey
|
||||||
ConfigFirstSeenAnnotationKey = "kubernetes.io/config.seen"
|
ConfigFirstSeenAnnotationKey = "kubernetes.io/config.seen"
|
||||||
ConfigHashAnnotationKey = "kubernetes.io/config.hash"
|
ConfigHashAnnotationKey = "kubernetes.io/config.hash"
|
||||||
CriticalPodAnnotationKey = "scheduler.alpha.kubernetes.io/critical-pod"
|
CriticalPodAnnotationKey = "scheduler.alpha.kubernetes.io/critical-pod"
|
||||||
|
@ -22,7 +22,6 @@ go_library(
|
|||||||
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||||
"//pkg/client/listers/core/internalversion:go_default_library",
|
"//pkg/client/listers/core/internalversion:go_default_library",
|
||||||
"//pkg/kubeapiserver/admission:go_default_library",
|
"//pkg/kubeapiserver/admission:go_default_library",
|
||||||
"//pkg/kubelet/types:go_default_library",
|
|
||||||
"//pkg/serviceaccount:go_default_library",
|
"//pkg/serviceaccount:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
@ -36,7 +36,6 @@ import (
|
|||||||
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||||
corelisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
|
corelisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
|
||||||
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||||
kubelet "k8s.io/kubernetes/pkg/kubelet/types"
|
|
||||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -146,7 +145,7 @@ func (s *serviceAccount) Admit(a admission.Attributes) (err error) {
|
|||||||
// Don't modify the spec of mirror pods.
|
// Don't modify the spec of mirror pods.
|
||||||
// That makes the kubelet very angry and confused, and it immediately deletes the pod (because the spec doesn't match)
|
// That makes the kubelet very angry and confused, and it immediately deletes the pod (because the spec doesn't match)
|
||||||
// That said, don't allow mirror pods to reference ServiceAccounts or SecretVolumeSources either
|
// That said, don't allow mirror pods to reference ServiceAccounts or SecretVolumeSources either
|
||||||
if _, isMirrorPod := pod.Annotations[kubelet.ConfigMirrorAnnotationKey]; isMirrorPod {
|
if _, isMirrorPod := pod.Annotations[api.MirrorPodAnnotationKey]; isMirrorPod {
|
||||||
if len(pod.Spec.ServiceAccountName) != 0 {
|
if len(pod.Spec.ServiceAccountName) != 0 {
|
||||||
return admission.NewForbidden(a, fmt.Errorf("a mirror pod may not reference service accounts"))
|
return admission.NewForbidden(a, fmt.Errorf("a mirror pod may not reference service accounts"))
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@ limitations under the License.
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// MirrorAnnotationKey represents the annotation key set by kubelets when creating mirror pods
|
||||||
|
MirrorPodAnnotationKey string = "kubernetes.io/config.mirror"
|
||||||
|
|
||||||
// TolerationsAnnotationKey represents the key of tolerations data (json serialized)
|
// TolerationsAnnotationKey represents the key of tolerations data (json serialized)
|
||||||
// in the Annotations of a Pod.
|
// in the Annotations of a Pod.
|
||||||
TolerationsAnnotationKey string = "scheduler.alpha.kubernetes.io/tolerations"
|
TolerationsAnnotationKey string = "scheduler.alpha.kubernetes.io/tolerations"
|
||||||
|
Loading…
Reference in New Issue
Block a user