kubernetes/pkg/api/pod/warnings.go
Humble Chirammal 471fd1ae8a deprecate RBD plugin from available in-tree drivers
Based on https://groups.google.com/g/kubernetes-sig-storage/c/h5751_B5LQM, the
consensus was to start the deprecation in v1.28.

This commit start the deprecation process of RBD plugin from in-tree
drivers.

ACTION REQUIRED:
   RBD volume plugin ( `kubernetes.io/rbd`) has been deprecated in this release
   and will be removed in a subsequent release. Alternative is to use RBD CSI driver
   (https://github.com/ceph/ceph-csi/) in your Kubernetes Cluster.

Signed-off-by: Humble Chirammal <humble.devassy@gmail.com>
2023-06-16 19:45:36 +05:30

309 lines
14 KiB
Go

/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pod
import (
"context"
"fmt"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
nodeapi "k8s.io/kubernetes/pkg/api/node"
pvcutil "k8s.io/kubernetes/pkg/api/persistentvolumeclaim"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/pods"
)
func GetWarningsForPod(ctx context.Context, pod, oldPod *api.Pod) []string {
if pod == nil {
return nil
}
var (
oldSpec *api.PodSpec
oldMeta *metav1.ObjectMeta
)
if oldPod != nil {
oldSpec = &oldPod.Spec
oldMeta = &oldPod.ObjectMeta
}
return warningsForPodSpecAndMeta(nil, &pod.Spec, &pod.ObjectMeta, oldSpec, oldMeta)
}
func GetWarningsForPodTemplate(ctx context.Context, fieldPath *field.Path, podTemplate, oldPodTemplate *api.PodTemplateSpec) []string {
if podTemplate == nil {
return nil
}
var (
oldSpec *api.PodSpec
oldMeta *metav1.ObjectMeta
)
if oldPodTemplate != nil {
oldSpec = &oldPodTemplate.Spec
oldMeta = &oldPodTemplate.ObjectMeta
}
return warningsForPodSpecAndMeta(fieldPath, &podTemplate.Spec, &podTemplate.ObjectMeta, oldSpec, oldMeta)
}
var deprecatedAnnotations = []struct {
key string
prefix string
message string
}{
{
key: `scheduler.alpha.kubernetes.io/critical-pod`,
message: `non-functional in v1.16+; use the "priorityClassName" field instead`,
},
{
key: `security.alpha.kubernetes.io/sysctls`,
message: `non-functional in v1.11+; use the "sysctls" field instead`,
},
{
key: `security.alpha.kubernetes.io/unsafe-sysctls`,
message: `non-functional in v1.11+; use the "sysctls" field instead`,
},
}
func warningsForPodSpecAndMeta(fieldPath *field.Path, podSpec *api.PodSpec, meta *metav1.ObjectMeta, oldPodSpec *api.PodSpec, oldMeta *metav1.ObjectMeta) []string {
var warnings []string
// use of deprecated node labels in selectors/affinity/topology
for k := range podSpec.NodeSelector {
if msg, deprecated := nodeapi.GetNodeLabelDeprecatedMessage(k); deprecated {
warnings = append(warnings, fmt.Sprintf("%s: %s", fieldPath.Child("spec", "nodeSelector").Key(k), msg))
}
}
if podSpec.Affinity != nil && podSpec.Affinity.NodeAffinity != nil {
n := podSpec.Affinity.NodeAffinity
if n.RequiredDuringSchedulingIgnoredDuringExecution != nil {
termFldPath := fieldPath.Child("spec", "affinity", "nodeAffinity", "requiredDuringSchedulingIgnoredDuringExecution", "nodeSelectorTerms")
for i, term := range n.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
warnings = append(warnings, nodeapi.GetWarningsForNodeSelectorTerm(term, termFldPath.Index(i))...)
}
}
preferredFldPath := fieldPath.Child("spec", "affinity", "nodeAffinity", "preferredDuringSchedulingIgnoredDuringExecution")
for i, term := range n.PreferredDuringSchedulingIgnoredDuringExecution {
warnings = append(warnings, nodeapi.GetWarningsForNodeSelectorTerm(term.Preference, preferredFldPath.Index(i).Child("preference"))...)
}
}
for i, t := range podSpec.TopologySpreadConstraints {
if msg, deprecated := nodeapi.GetNodeLabelDeprecatedMessage(t.TopologyKey); deprecated {
warnings = append(warnings, fmt.Sprintf(
"%s: %s is %s",
fieldPath.Child("spec", "topologySpreadConstraints").Index(i).Child("topologyKey"),
t.TopologyKey,
msg,
))
}
// warn if labelSelector is empty which is no-match.
if t.LabelSelector == nil {
warnings = append(warnings, fmt.Sprintf("%s: a null labelSelector results in matching no pod", fieldPath.Child("spec", "topologySpreadConstraints").Index(i).Child("labelSelector")))
}
}
// use of deprecated annotations
for _, deprecated := range deprecatedAnnotations {
if _, exists := meta.Annotations[deprecated.key]; exists {
warnings = append(warnings, fmt.Sprintf("%s: %s", fieldPath.Child("metadata", "annotations").Key(deprecated.key), deprecated.message))
}
if len(deprecated.prefix) > 0 {
for k := range meta.Annotations {
if strings.HasPrefix(k, deprecated.prefix) {
warnings = append(warnings, fmt.Sprintf("%s: %s", fieldPath.Child("metadata", "annotations").Key(k), deprecated.message))
break
}
}
}
}
// deprecated and removed volume plugins
for i, v := range podSpec.Volumes {
if v.PhotonPersistentDisk != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.11, non-functional in v1.16+", fieldPath.Child("spec", "volumes").Index(i).Child("photonPersistentDisk")))
}
if v.GitRepo != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.11", fieldPath.Child("spec", "volumes").Index(i).Child("gitRepo")))
}
if v.ScaleIO != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.16, non-functional in v1.22+", fieldPath.Child("spec", "volumes").Index(i).Child("scaleIO")))
}
if v.Flocker != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.22, non-functional in v1.25+", fieldPath.Child("spec", "volumes").Index(i).Child("flocker")))
}
if v.StorageOS != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.22, non-functional in v1.25+", fieldPath.Child("spec", "volumes").Index(i).Child("storageOS")))
}
if v.Quobyte != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.22, non-functional in v1.25+", fieldPath.Child("spec", "volumes").Index(i).Child("quobyte")))
}
if v.Glusterfs != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.25, non-functional in v1.26+", fieldPath.Child("spec", "volumes").Index(i).Child("glusterfs")))
}
if v.Ephemeral != nil && v.Ephemeral.VolumeClaimTemplate != nil {
warnings = append(warnings, pvcutil.GetWarningsForPersistentVolumeClaimSpec(fieldPath.Child("spec", "volumes").Index(i).Child("ephemeral").Child("volumeClaimTemplate").Child("spec"), v.Ephemeral.VolumeClaimTemplate.Spec)...)
}
if v.CephFS != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.28, non-functional in v1.31+", fieldPath.Child("spec", "volumes").Index(i).Child("cephfs")))
}
if v.RBD != nil {
warnings = append(warnings, fmt.Sprintf("%s: deprecated in v1.28, non-functional in v1.31+", fieldPath.Child("spec", "volumes").Index(i).Child("rbd")))
}
}
// duplicate hostAliases (#91670, #58477)
if len(podSpec.HostAliases) > 1 {
items := sets.NewString()
for i, item := range podSpec.HostAliases {
if items.Has(item.IP) {
warnings = append(warnings, fmt.Sprintf("%s: duplicate ip %q", fieldPath.Child("spec", "hostAliases").Index(i).Child("ip"), item.IP))
} else {
items.Insert(item.IP)
}
}
}
// duplicate imagePullSecrets (#91629, #58477)
if len(podSpec.ImagePullSecrets) > 1 {
items := sets.NewString()
for i, item := range podSpec.ImagePullSecrets {
if items.Has(item.Name) {
warnings = append(warnings, fmt.Sprintf("%s: duplicate name %q", fieldPath.Child("spec", "imagePullSecrets").Index(i).Child("name"), item.Name))
} else {
items.Insert(item.Name)
}
}
}
// imagePullSecrets with empty name (#99454#issuecomment-787838112)
for i, item := range podSpec.ImagePullSecrets {
if len(item.Name) == 0 {
warnings = append(warnings, fmt.Sprintf("%s: invalid empty name %q", fieldPath.Child("spec", "imagePullSecrets").Index(i).Child("name"), item.Name))
}
}
// fractional memory/ephemeral-storage requests/limits (#79950, #49442, #18538)
if value, ok := podSpec.Overhead[api.ResourceMemory]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", fieldPath.Child("spec", "overhead").Key(string(api.ResourceMemory)), value.String()))
}
if value, ok := podSpec.Overhead[api.ResourceEphemeralStorage]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", fieldPath.Child("spec", "overhead").Key(string(api.ResourceEphemeralStorage)), value.String()))
}
// use of pod seccomp annotation without accompanying field
if podSpec.SecurityContext == nil || podSpec.SecurityContext.SeccompProfile == nil {
if _, exists := meta.Annotations[api.SeccompPodAnnotationKey]; exists {
warnings = append(warnings, fmt.Sprintf(`%s: non-functional in v1.27+; use the "seccompProfile" field instead`, fieldPath.Child("metadata", "annotations").Key(api.SeccompPodAnnotationKey)))
}
}
pods.VisitContainersWithPath(podSpec, fieldPath.Child("spec"), func(c *api.Container, p *field.Path) bool {
// use of container seccomp annotation without accompanying field
if c.SecurityContext == nil || c.SecurityContext.SeccompProfile == nil {
if _, exists := meta.Annotations[api.SeccompContainerAnnotationKeyPrefix+c.Name]; exists {
warnings = append(warnings, fmt.Sprintf(`%s: non-functional in v1.27+; use the "seccompProfile" field instead`, fieldPath.Child("metadata", "annotations").Key(api.SeccompContainerAnnotationKeyPrefix+c.Name)))
}
}
// fractional memory/ephemeral-storage requests/limits (#79950, #49442, #18538)
if value, ok := c.Resources.Limits[api.ResourceMemory]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", p.Child("resources", "limits").Key(string(api.ResourceMemory)), value.String()))
}
if value, ok := c.Resources.Requests[api.ResourceMemory]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", p.Child("resources", "requests").Key(string(api.ResourceMemory)), value.String()))
}
if value, ok := c.Resources.Limits[api.ResourceEphemeralStorage]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", p.Child("resources", "limits").Key(string(api.ResourceEphemeralStorage)), value.String()))
}
if value, ok := c.Resources.Requests[api.ResourceEphemeralStorage]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", p.Child("resources", "requests").Key(string(api.ResourceEphemeralStorage)), value.String()))
}
// duplicate containers[*].env (#86163, #93266, #58477)
if len(c.Env) > 1 {
items := sets.NewString()
for i, item := range c.Env {
if items.Has(item.Name) {
// a previous value exists, but it might be OK
bad := false
ref := fmt.Sprintf("$(%s)", item.Name) // what does a ref to this name look like
// if we are replacing it with a valueFrom, warn
if item.ValueFrom != nil {
bad = true
}
// if this is X="$(X)", warn
if item.Value == ref {
bad = true
}
// if the new value does not contain a reference to the old
// value (e.g. X="abc"; X="$(X)123"), warn
if !strings.Contains(item.Value, ref) {
bad = true
}
if bad {
warnings = append(warnings, fmt.Sprintf("%s: hides previous definition of %q", p.Child("env").Index(i), item.Name))
}
} else {
items.Insert(item.Name)
}
}
}
return true
})
// warn if the terminationGracePeriodSeconds is negative.
if podSpec.TerminationGracePeriodSeconds != nil && *podSpec.TerminationGracePeriodSeconds < 0 {
warnings = append(warnings, fmt.Sprintf("%s: must be >= 0; negative values are invalid and will be treated as 1", fieldPath.Child("spec", "terminationGracePeriodSeconds")))
}
if podSpec.Affinity != nil {
if affinity := podSpec.Affinity.PodAffinity; affinity != nil {
warnings = append(warnings, warningsForPodAffinityTerms(affinity.RequiredDuringSchedulingIgnoredDuringExecution, fieldPath.Child("spec", "affinity", "podAffinity", "requiredDuringSchedulingIgnoredDuringExecution"))...)
warnings = append(warnings, warningsForWeightedPodAffinityTerms(affinity.PreferredDuringSchedulingIgnoredDuringExecution, fieldPath.Child("spec", "affinity", "podAffinity", "preferredDuringSchedulingIgnoredDuringExecution"))...)
}
if affinity := podSpec.Affinity.PodAntiAffinity; affinity != nil {
warnings = append(warnings, warningsForPodAffinityTerms(affinity.RequiredDuringSchedulingIgnoredDuringExecution, fieldPath.Child("spec", "affinity", "podAntiAffinity", "requiredDuringSchedulingIgnoredDuringExecution"))...)
warnings = append(warnings, warningsForWeightedPodAffinityTerms(affinity.PreferredDuringSchedulingIgnoredDuringExecution, fieldPath.Child("spec", "affinity", "podAntiAffinity", "preferredDuringSchedulingIgnoredDuringExecution"))...)
}
}
return warnings
}
func warningsForPodAffinityTerms(terms []api.PodAffinityTerm, fieldPath *field.Path) []string {
var warnings []string
for i, t := range terms {
if t.LabelSelector == nil {
warnings = append(warnings, fmt.Sprintf("%s: a null labelSelector results in matching no pod", fieldPath.Index(i).Child("labelSelector")))
}
}
return warnings
}
func warningsForWeightedPodAffinityTerms(terms []api.WeightedPodAffinityTerm, fieldPath *field.Path) []string {
var warnings []string
for i, t := range terms {
// warn if labelSelector is empty which is no-match.
if t.PodAffinityTerm.LabelSelector == nil {
warnings = append(warnings, fmt.Sprintf("%s: a null labelSelector results in matching no pod", fieldPath.Index(i).Child("podAffinityTerm", "labelSelector")))
}
}
return warnings
}