Merge pull request #51816 from liggitt/xiangpengzhao-remove-initc-anno

Automatic merge from submit-queue

Remove deprecated init-container in annotations

fixes #50655
fixes #51816 
closes #41004
fixes #51816 

Builds on #50654 and drops the initContainer annotations on conversion to prevent bypassing API server validation/security and targeting version-skewed kubelets that still honor the annotations

```release-note
The deprecated alpha and beta initContainer annotations are no longer supported. Init containers must be specified using the initContainers field in the pod spec.
```
This commit is contained in:
Kubernetes Submit Queue
2017-09-03 17:35:11 -07:00
committed by GitHub
11 changed files with 309 additions and 734 deletions

View File

@@ -17,7 +17,6 @@ limitations under the License.
package v1
import (
"encoding/json"
"fmt"
"reflect"
@@ -330,146 +329,19 @@ func Convert_v1_ReplicationControllerSpec_To_api_ReplicationControllerSpec(in *v
return nil
}
func Convert_api_PodStatusResult_To_v1_PodStatusResult(in *api.PodStatusResult, out *v1.PodStatusResult, s conversion.Scope) error {
if err := autoConvert_api_PodStatusResult_To_v1_PodStatusResult(in, out, s); err != nil {
return err
}
if old := out.Annotations; old != nil {
out.Annotations = make(map[string]string, len(old))
for k, v := range old {
out.Annotations[k] = v
}
}
if len(out.Status.InitContainerStatuses) > 0 {
if out.Annotations == nil {
out.Annotations = make(map[string]string)
}
value, err := json.Marshal(out.Status.InitContainerStatuses)
if err != nil {
return err
}
out.Annotations[v1.PodInitContainerStatusesAnnotationKey] = string(value)
out.Annotations[v1.PodInitContainerStatusesBetaAnnotationKey] = string(value)
} else {
delete(out.Annotations, v1.PodInitContainerStatusesAnnotationKey)
delete(out.Annotations, v1.PodInitContainerStatusesBetaAnnotationKey)
}
return nil
}
func Convert_v1_PodStatusResult_To_api_PodStatusResult(in *v1.PodStatusResult, out *api.PodStatusResult, s conversion.Scope) error {
// TODO: sometime after we move init container to stable, remove these conversions
// If there is a beta annotation, copy to alpha key.
// See commit log for PR #31026 for why we do this.
if valueBeta, okBeta := in.Annotations[v1.PodInitContainerStatusesBetaAnnotationKey]; okBeta {
in.Annotations[v1.PodInitContainerStatusesAnnotationKey] = valueBeta
}
// Move the annotation to the internal repr. field
if value, ok := in.Annotations[v1.PodInitContainerStatusesAnnotationKey]; ok {
var values []v1.ContainerStatus
if err := json.Unmarshal([]byte(value), &values); err != nil {
return err
}
// Conversion from external to internal version exists more to
// satisfy the needs of the decoder than it does to be a general
// purpose tool. And Decode always creates an intermediate object
// to decode to. Thus the caller of UnsafeConvertToVersion is
// taking responsibility to ensure mutation of in is not exposed
// back to the caller.
in.Status.InitContainerStatuses = values
}
if err := autoConvert_v1_PodStatusResult_To_api_PodStatusResult(in, out, s); err != nil {
return err
}
if len(out.Annotations) > 0 {
old := out.Annotations
out.Annotations = make(map[string]string, len(old))
for k, v := range old {
out.Annotations[k] = v
}
delete(out.Annotations, v1.PodInitContainerStatusesAnnotationKey)
delete(out.Annotations, v1.PodInitContainerStatusesBetaAnnotationKey)
}
return nil
}
func Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in *api.PodTemplateSpec, out *v1.PodTemplateSpec, s conversion.Scope) error {
if err := autoConvert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in, out, s); err != nil {
return err
}
// TODO: sometime after we move init container to stable, remove these conversions.
if old := out.Annotations; old != nil {
out.Annotations = make(map[string]string, len(old))
for k, v := range old {
out.Annotations[k] = v
}
}
if len(out.Spec.InitContainers) > 0 {
if out.Annotations == nil {
out.Annotations = make(map[string]string)
}
value, err := json.Marshal(out.Spec.InitContainers)
if err != nil {
return err
}
out.Annotations[v1.PodInitContainersAnnotationKey] = string(value)
out.Annotations[v1.PodInitContainersBetaAnnotationKey] = string(value)
} else {
delete(out.Annotations, v1.PodInitContainersAnnotationKey)
delete(out.Annotations, v1.PodInitContainersBetaAnnotationKey)
}
return nil
}
func Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in *v1.PodTemplateSpec, out *api.PodTemplateSpec, s conversion.Scope) error {
// TODO: sometime after we move init container to stable, remove these conversions
// If there is a beta annotation, copy to alpha key.
// See commit log for PR #31026 for why we do this.
if valueBeta, okBeta := in.Annotations[v1.PodInitContainersBetaAnnotationKey]; okBeta {
in.Annotations[v1.PodInitContainersAnnotationKey] = valueBeta
}
// Move the annotation to the internal repr. field
if value, ok := in.Annotations[v1.PodInitContainersAnnotationKey]; ok {
var values []v1.Container
if err := json.Unmarshal([]byte(value), &values); err != nil {
return err
}
// Conversion from external to internal version exists more to
// satisfy the needs of the decoder than it does to be a general
// purpose tool. And Decode always creates an intermediate object
// to decode to. Thus the caller of UnsafeConvertToVersion is
// taking responsibility to ensure mutation of in is not exposed
// back to the caller.
in.Spec.InitContainers = values
// Call defaulters explicitly until annotations are removed
tmpPodTemp := &v1.PodTemplate{
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
HostNetwork: in.Spec.HostNetwork,
InitContainers: values,
},
},
}
SetObjectDefaults_PodTemplate(tmpPodTemp)
in.Spec.InitContainers = tmpPodTemp.Template.Spec.InitContainers
}
if err := autoConvert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in, out, s); err != nil {
return err
}
if len(out.Annotations) > 0 {
old := out.Annotations
out.Annotations = make(map[string]string, len(old))
for k, v := range old {
out.Annotations[k] = v
}
delete(out.Annotations, v1.PodInitContainersAnnotationKey)
delete(out.Annotations, v1.PodInitContainersBetaAnnotationKey)
}
return nil
}
@@ -522,101 +394,20 @@ func Convert_api_Pod_To_v1_Pod(in *api.Pod, out *v1.Pod, s conversion.Scope) err
return err
}
// TODO: sometime after we move init container to stable, remove these conversions
if len(out.Spec.InitContainers) > 0 || len(out.Status.InitContainerStatuses) > 0 {
old := out.Annotations
out.Annotations = make(map[string]string, len(old))
for k, v := range old {
out.Annotations[k] = v
}
delete(out.Annotations, v1.PodInitContainersAnnotationKey)
delete(out.Annotations, v1.PodInitContainersBetaAnnotationKey)
delete(out.Annotations, v1.PodInitContainerStatusesAnnotationKey)
delete(out.Annotations, v1.PodInitContainerStatusesBetaAnnotationKey)
}
if len(out.Spec.InitContainers) > 0 {
value, err := json.Marshal(out.Spec.InitContainers)
if err != nil {
return err
}
out.Annotations[v1.PodInitContainersAnnotationKey] = string(value)
out.Annotations[v1.PodInitContainersBetaAnnotationKey] = string(value)
}
if len(out.Status.InitContainerStatuses) > 0 {
value, err := json.Marshal(out.Status.InitContainerStatuses)
if err != nil {
return err
}
out.Annotations[v1.PodInitContainerStatusesAnnotationKey] = string(value)
out.Annotations[v1.PodInitContainerStatusesBetaAnnotationKey] = string(value)
}
return nil
}
func Convert_v1_Pod_To_api_Pod(in *v1.Pod, out *api.Pod, s conversion.Scope) error {
// If there is a beta annotation, copy to alpha key.
// See commit log for PR #31026 for why we do this.
if valueBeta, okBeta := in.Annotations[v1.PodInitContainersBetaAnnotationKey]; okBeta {
in.Annotations[v1.PodInitContainersAnnotationKey] = valueBeta
}
// TODO: sometime after we move init container to stable, remove these conversions
// Move the annotation to the internal repr. field
if value, ok := in.Annotations[v1.PodInitContainersAnnotationKey]; ok {
var values []v1.Container
if err := json.Unmarshal([]byte(value), &values); err != nil {
return err
}
// Conversion from external to internal version exists more to
// satisfy the needs of the decoder than it does to be a general
// purpose tool. And Decode always creates an intermediate object
// to decode to. Thus the caller of UnsafeConvertToVersion is
// taking responsibility to ensure mutation of in is not exposed
// back to the caller.
in.Spec.InitContainers = values
// Call defaulters explicitly until annotations are removed
tmpPod := &v1.Pod{
Spec: v1.PodSpec{
HostNetwork: in.Spec.HostNetwork,
InitContainers: values,
},
}
SetObjectDefaults_Pod(tmpPod)
in.Spec.InitContainers = tmpPod.Spec.InitContainers
}
// If there is a beta annotation, copy to alpha key.
// See commit log for PR #31026 for why we do this.
if valueBeta, okBeta := in.Annotations[v1.PodInitContainerStatusesBetaAnnotationKey]; okBeta {
in.Annotations[v1.PodInitContainerStatusesAnnotationKey] = valueBeta
}
if value, ok := in.Annotations[v1.PodInitContainerStatusesAnnotationKey]; ok {
var values []v1.ContainerStatus
if err := json.Unmarshal([]byte(value), &values); err != nil {
return err
}
// Conversion from external to internal version exists more to
// satisfy the needs of the decoder than it does to be a general
// purpose tool. And Decode always creates an intermediate object
// to decode to. Thus the caller of UnsafeConvertToVersion is
// taking responsibility to ensure mutation of in is not exposed
// back to the caller.
in.Status.InitContainerStatuses = values
}
if err := autoConvert_v1_Pod_To_api_Pod(in, out, s); err != nil {
return err
}
// drop init container annotations so they don't take effect on legacy kubelets.
// remove this once the oldest supported kubelet no longer honors the annotations over the field.
if len(out.Annotations) > 0 {
old := out.Annotations
out.Annotations = make(map[string]string, len(old))
for k, v := range old {
out.Annotations[k] = v
}
delete(out.Annotations, v1.PodInitContainersAnnotationKey)
delete(out.Annotations, v1.PodInitContainersBetaAnnotationKey)
delete(out.Annotations, v1.PodInitContainerStatusesAnnotationKey)
delete(out.Annotations, v1.PodInitContainerStatusesBetaAnnotationKey)
delete(out.Annotations, "pod.beta.kubernetes.io/init-containers")
delete(out.Annotations, "pod.alpha.kubernetes.io/init-containers")
delete(out.Annotations, "pod.beta.kubernetes.io/init-container-statuses")
delete(out.Annotations, "pod.alpha.kubernetes.io/init-container-statuses")
}
return nil
}

View File

@@ -350,7 +350,7 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
return nil
}
cpu, _ := resource.ParseQuantity("100Gi")
cpu, _ := resource.ParseQuantity("100m")
mem, _ := resource.ParseQuantity("100Mi")
tests := []struct {
@@ -364,15 +364,12 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
rc: v1.ReplicationController{
Spec: v1.ReplicationControllerSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"pod.beta.kubernetes.io/init-containers": `
[
{
"name": "install",
"image": "busybox"
}
]`,
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Name: "install",
Image: "busybox",
},
},
},
},
@@ -390,26 +387,23 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
rc: v1.ReplicationController{
Spec: v1.ReplicationControllerSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"pod.beta.kubernetes.io/init-containers": `
[
{
"name": "fun",
"image": "alpine",
"env": [
{
"name": "MY_POD_IP",
"valueFrom": {
"fieldRef": {
"apiVersion": "",
"fieldPath": "status.podIP"
}
}
}
]
}
]`,
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Name: "fun",
Image: "alpine",
Env: []v1.EnvVar{
{
Name: "MY_POD_IP",
ValueFrom: &v1.EnvVarSource{
FieldRef: &v1.ObjectFieldSelector{
APIVersion: "",
FieldPath: "status.podIP",
},
},
},
},
},
},
},
},
@@ -437,20 +431,17 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
rc: v1.ReplicationController{
Spec: v1.ReplicationControllerSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"pod.beta.kubernetes.io/init-containers": `
[
{
"name": "fun",
"image": "alpine",
"ports": [
{
"name": "default"
}
]
}
]`,
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Name: "fun",
Image: "alpine",
Ports: []v1.ContainerPort{
{
Name: "default",
},
},
},
},
},
},
@@ -473,25 +464,22 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
rc: v1.ReplicationController{
Spec: v1.ReplicationControllerSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"pod.beta.kubernetes.io/init-containers": `
[
{
"name": "fun",
"image": "alpine",
"resources": {
"limits": {
"cpu": "100Gi",
"memory": "100Mi"
},
"requests": {
"cpu": "100Gi",
"memory": "100Mi"
}
}
}
]`,
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Name: "fun",
Image: "alpine",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
v1.ResourceMemory: resource.MustParse("100Mi"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
v1.ResourceMemory: resource.MustParse("100Mi"),
},
},
},
},
},
},
@@ -514,29 +502,30 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
validators: []InitContainerValidator{assertResource},
},
{
name: "Prob",
name: "Probe",
rc: v1.ReplicationController{
Spec: v1.ReplicationControllerSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"pod.beta.kubernetes.io/init-containers": `
[
{
"name": "fun",
"image": "alpine",
"livenessProbe": {
"httpGet": {
"host": "localhost"
}
},
"readinessProbe": {
"httpGet": {
"host": "localhost"
}
}
}
]`,
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Name: "fun",
Image: "alpine",
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Host: "localhost",
},
},
},
ReadinessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Host: "localhost",
},
},
},
},
},
},
},
@@ -577,27 +566,29 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
rc: v1.ReplicationController{
Spec: v1.ReplicationControllerSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"pod.beta.kubernetes.io/init-containers": `
[
{
"name": "fun",
"image": "alpine",
"lifecycle": {
"postStart": {
"httpGet": {
"host": "localhost"
}
},
"preStop": {
"httpGet": {
"host": "localhost"
}
}
}
}
]`,
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Name: "fun",
Image: "alpine",
Ports: []v1.ContainerPort{
{
Name: "default",
},
},
Lifecycle: &v1.Lifecycle{
PostStart: &v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Host: "localhost",
},
},
PreStop: &v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Host: "localhost",
},
},
},
},
},
},
},

View File

@@ -17,7 +17,6 @@ limitations under the License.
package pod
import (
"encoding/json"
"fmt"
"time"
@@ -49,67 +48,6 @@ func FindPort(pod *v1.Pod, svcPort *v1.ServicePort) (int, error) {
return 0, fmt.Errorf("no suitable port for manifest: %s", pod.UID)
}
// TODO: remove this function when init containers becomes a stable feature
func SetInitContainersAndStatuses(pod *v1.Pod) error {
var initContainersAnnotation string
initContainersAnnotation = pod.Annotations[v1.PodInitContainersAnnotationKey]
initContainersAnnotation = pod.Annotations[v1.PodInitContainersBetaAnnotationKey]
if len(initContainersAnnotation) > 0 {
var values []v1.Container
if err := json.Unmarshal([]byte(initContainersAnnotation), &values); err != nil {
return err
}
pod.Spec.InitContainers = values
}
var initContainerStatusesAnnotation string
initContainerStatusesAnnotation = pod.Annotations[v1.PodInitContainerStatusesAnnotationKey]
initContainerStatusesAnnotation = pod.Annotations[v1.PodInitContainerStatusesBetaAnnotationKey]
if len(initContainerStatusesAnnotation) > 0 {
var values []v1.ContainerStatus
if err := json.Unmarshal([]byte(initContainerStatusesAnnotation), &values); err != nil {
return err
}
pod.Status.InitContainerStatuses = values
}
return nil
}
// TODO: remove this function when init containers becomes a stable feature
func SetInitContainersAnnotations(pod *v1.Pod) error {
if len(pod.Spec.InitContainers) > 0 {
value, err := json.Marshal(pod.Spec.InitContainers)
if err != nil {
return err
}
if pod.Annotations == nil {
pod.Annotations = make(map[string]string)
}
pod.Annotations[v1.PodInitContainersAnnotationKey] = string(value)
pod.Annotations[v1.PodInitContainersBetaAnnotationKey] = string(value)
}
return nil
}
// TODO: remove this function when init containers becomes a stable feature
func SetInitContainersStatusesAnnotations(pod *v1.Pod) error {
if len(pod.Status.InitContainerStatuses) > 0 {
value, err := json.Marshal(pod.Status.InitContainerStatuses)
if err != nil {
return err
}
if pod.Annotations == nil {
pod.Annotations = make(map[string]string)
}
pod.Annotations[v1.PodInitContainerStatusesAnnotationKey] = string(value)
pod.Annotations[v1.PodInitContainerStatusesBetaAnnotationKey] = string(value)
} else {
delete(pod.Annotations, v1.PodInitContainerStatusesAnnotationKey)
delete(pod.Annotations, v1.PodInitContainerStatusesBetaAnnotationKey)
}
return nil
}
// Visitor is called with each object name, and returns true if visiting should continue
type Visitor func(name string) (shouldContinue bool)

View File

@@ -17,7 +17,6 @@ limitations under the License.
package pod
import (
"encoding/json"
"reflect"
"strings"
"testing"
@@ -405,52 +404,3 @@ func TestIsPodAvailable(t *testing.T) {
}
}
}
func TestSetInitContainersStatusesAnnotations(t *testing.T) {
testStatuses := []v1.ContainerStatus{
{
Name: "test",
},
}
value, _ := json.Marshal(testStatuses)
testAnnotation := string(value)
tests := []struct {
name string
pod *v1.Pod
annotations map[string]string
}{
{
name: "Populate annotations from status",
pod: &v1.Pod{
Status: v1.PodStatus{
InitContainerStatuses: testStatuses,
},
},
annotations: map[string]string{
v1.PodInitContainerStatusesAnnotationKey: testAnnotation,
v1.PodInitContainerStatusesBetaAnnotationKey: testAnnotation,
},
},
{
name: "Clear annotations if no status",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
v1.PodInitContainerStatusesAnnotationKey: testAnnotation,
v1.PodInitContainerStatusesBetaAnnotationKey: testAnnotation,
},
},
Status: v1.PodStatus{
InitContainerStatuses: []v1.ContainerStatus{},
},
},
annotations: map[string]string{},
},
}
for _, test := range tests {
SetInitContainersStatusesAnnotations(test.pod)
if !reflect.DeepEqual(test.pod.Annotations, test.annotations) {
t.Errorf("%v, actual = %v, expected = %v", test.name, test.pod.Annotations, test.annotations)
}
}
}

View File

@@ -3237,6 +3237,11 @@ func autoConvert_v1_Pod_To_api_Pod(in *v1.Pod, out *api.Pod, s conversion.Scope)
return nil
}
// Convert_v1_Pod_To_api_Pod is an autogenerated conversion function.
func Convert_v1_Pod_To_api_Pod(in *v1.Pod, out *api.Pod, s conversion.Scope) error {
return autoConvert_v1_Pod_To_api_Pod(in, out, s)
}
func autoConvert_api_Pod_To_v1_Pod(in *api.Pod, out *v1.Pod, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_api_PodSpec_To_v1_PodSpec(&in.Spec, &out.Spec, s); err != nil {
@@ -3737,6 +3742,11 @@ func autoConvert_v1_PodStatusResult_To_api_PodStatusResult(in *v1.PodStatusResul
return nil
}
// Convert_v1_PodStatusResult_To_api_PodStatusResult is an autogenerated conversion function.
func Convert_v1_PodStatusResult_To_api_PodStatusResult(in *v1.PodStatusResult, out *api.PodStatusResult, s conversion.Scope) error {
return autoConvert_v1_PodStatusResult_To_api_PodStatusResult(in, out, s)
}
func autoConvert_api_PodStatusResult_To_v1_PodStatusResult(in *api.PodStatusResult, out *v1.PodStatusResult, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_api_PodStatus_To_v1_PodStatus(&in.Status, &out.Status, s); err != nil {
@@ -3745,6 +3755,11 @@ func autoConvert_api_PodStatusResult_To_v1_PodStatusResult(in *api.PodStatusResu
return nil
}
// Convert_api_PodStatusResult_To_v1_PodStatusResult is an autogenerated conversion function.
func Convert_api_PodStatusResult_To_v1_PodStatusResult(in *api.PodStatusResult, out *v1.PodStatusResult, s conversion.Scope) error {
return autoConvert_api_PodStatusResult_To_v1_PodStatusResult(in, out, s)
}
func autoConvert_v1_PodTemplate_To_api_PodTemplate(in *v1.PodTemplate, out *api.PodTemplate, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {