mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
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:
commit
6ec80eac1b
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
@ -330,146 +329,19 @@ func Convert_v1_ReplicationControllerSpec_To_api_ReplicationControllerSpec(in *v
|
|||||||
return nil
|
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 {
|
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 {
|
if err := autoConvert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in, out, s); err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in *v1.PodTemplateSpec, out *api.PodTemplateSpec, s conversion.Scope) error {
|
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 {
|
if err := autoConvert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in, out, s); err != nil {
|
||||||
return err
|
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
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: sometime after we move init container to stable, remove these conversions
|
// drop init container annotations so they don't take effect on legacy kubelets.
|
||||||
if len(out.Spec.InitContainers) > 0 || len(out.Status.InitContainerStatuses) > 0 {
|
// remove this once the oldest supported kubelet no longer honors the annotations over the field.
|
||||||
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
|
|
||||||
}
|
|
||||||
if len(out.Annotations) > 0 {
|
if len(out.Annotations) > 0 {
|
||||||
old := out.Annotations
|
old := out.Annotations
|
||||||
out.Annotations = make(map[string]string, len(old))
|
out.Annotations = make(map[string]string, len(old))
|
||||||
for k, v := range old {
|
for k, v := range old {
|
||||||
out.Annotations[k] = v
|
out.Annotations[k] = v
|
||||||
}
|
}
|
||||||
delete(out.Annotations, v1.PodInitContainersAnnotationKey)
|
delete(out.Annotations, "pod.beta.kubernetes.io/init-containers")
|
||||||
delete(out.Annotations, v1.PodInitContainersBetaAnnotationKey)
|
delete(out.Annotations, "pod.alpha.kubernetes.io/init-containers")
|
||||||
delete(out.Annotations, v1.PodInitContainerStatusesAnnotationKey)
|
delete(out.Annotations, "pod.beta.kubernetes.io/init-container-statuses")
|
||||||
delete(out.Annotations, v1.PodInitContainerStatusesBetaAnnotationKey)
|
delete(out.Annotations, "pod.alpha.kubernetes.io/init-container-statuses")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu, _ := resource.ParseQuantity("100Gi")
|
cpu, _ := resource.ParseQuantity("100m")
|
||||||
mem, _ := resource.ParseQuantity("100Mi")
|
mem, _ := resource.ParseQuantity("100Mi")
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -364,15 +364,12 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
|
|||||||
rc: v1.ReplicationController{
|
rc: v1.ReplicationController{
|
||||||
Spec: v1.ReplicationControllerSpec{
|
Spec: v1.ReplicationControllerSpec{
|
||||||
Template: &v1.PodTemplateSpec{
|
Template: &v1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
Spec: v1.PodSpec{
|
||||||
Annotations: map[string]string{
|
InitContainers: []v1.Container{
|
||||||
"pod.beta.kubernetes.io/init-containers": `
|
{
|
||||||
[
|
Name: "install",
|
||||||
{
|
Image: "busybox",
|
||||||
"name": "install",
|
},
|
||||||
"image": "busybox"
|
|
||||||
}
|
|
||||||
]`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -390,26 +387,23 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
|
|||||||
rc: v1.ReplicationController{
|
rc: v1.ReplicationController{
|
||||||
Spec: v1.ReplicationControllerSpec{
|
Spec: v1.ReplicationControllerSpec{
|
||||||
Template: &v1.PodTemplateSpec{
|
Template: &v1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
Spec: v1.PodSpec{
|
||||||
Annotations: map[string]string{
|
InitContainers: []v1.Container{
|
||||||
"pod.beta.kubernetes.io/init-containers": `
|
{
|
||||||
[
|
Name: "fun",
|
||||||
{
|
Image: "alpine",
|
||||||
"name": "fun",
|
Env: []v1.EnvVar{
|
||||||
"image": "alpine",
|
{
|
||||||
"env": [
|
Name: "MY_POD_IP",
|
||||||
{
|
ValueFrom: &v1.EnvVarSource{
|
||||||
"name": "MY_POD_IP",
|
FieldRef: &v1.ObjectFieldSelector{
|
||||||
"valueFrom": {
|
APIVersion: "",
|
||||||
"fieldRef": {
|
FieldPath: "status.podIP",
|
||||||
"apiVersion": "",
|
},
|
||||||
"fieldPath": "status.podIP"
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
|
||||||
}
|
|
||||||
]`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -437,20 +431,17 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
|
|||||||
rc: v1.ReplicationController{
|
rc: v1.ReplicationController{
|
||||||
Spec: v1.ReplicationControllerSpec{
|
Spec: v1.ReplicationControllerSpec{
|
||||||
Template: &v1.PodTemplateSpec{
|
Template: &v1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
Spec: v1.PodSpec{
|
||||||
Annotations: map[string]string{
|
InitContainers: []v1.Container{
|
||||||
"pod.beta.kubernetes.io/init-containers": `
|
{
|
||||||
[
|
Name: "fun",
|
||||||
{
|
Image: "alpine",
|
||||||
"name": "fun",
|
Ports: []v1.ContainerPort{
|
||||||
"image": "alpine",
|
{
|
||||||
"ports": [
|
Name: "default",
|
||||||
{
|
},
|
||||||
"name": "default"
|
},
|
||||||
}
|
},
|
||||||
]
|
|
||||||
}
|
|
||||||
]`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -473,25 +464,22 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
|
|||||||
rc: v1.ReplicationController{
|
rc: v1.ReplicationController{
|
||||||
Spec: v1.ReplicationControllerSpec{
|
Spec: v1.ReplicationControllerSpec{
|
||||||
Template: &v1.PodTemplateSpec{
|
Template: &v1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
Spec: v1.PodSpec{
|
||||||
Annotations: map[string]string{
|
InitContainers: []v1.Container{
|
||||||
"pod.beta.kubernetes.io/init-containers": `
|
{
|
||||||
[
|
Name: "fun",
|
||||||
{
|
Image: "alpine",
|
||||||
"name": "fun",
|
Resources: v1.ResourceRequirements{
|
||||||
"image": "alpine",
|
Limits: v1.ResourceList{
|
||||||
"resources": {
|
v1.ResourceCPU: resource.MustParse("100m"),
|
||||||
"limits": {
|
v1.ResourceMemory: resource.MustParse("100Mi"),
|
||||||
"cpu": "100Gi",
|
},
|
||||||
"memory": "100Mi"
|
Requests: v1.ResourceList{
|
||||||
},
|
v1.ResourceCPU: resource.MustParse("100m"),
|
||||||
"requests": {
|
v1.ResourceMemory: resource.MustParse("100Mi"),
|
||||||
"cpu": "100Gi",
|
},
|
||||||
"memory": "100Mi"
|
},
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
]`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -514,29 +502,30 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
|
|||||||
validators: []InitContainerValidator{assertResource},
|
validators: []InitContainerValidator{assertResource},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Prob",
|
name: "Probe",
|
||||||
rc: v1.ReplicationController{
|
rc: v1.ReplicationController{
|
||||||
Spec: v1.ReplicationControllerSpec{
|
Spec: v1.ReplicationControllerSpec{
|
||||||
Template: &v1.PodTemplateSpec{
|
Template: &v1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
Spec: v1.PodSpec{
|
||||||
Annotations: map[string]string{
|
InitContainers: []v1.Container{
|
||||||
"pod.beta.kubernetes.io/init-containers": `
|
{
|
||||||
[
|
Name: "fun",
|
||||||
{
|
Image: "alpine",
|
||||||
"name": "fun",
|
LivenessProbe: &v1.Probe{
|
||||||
"image": "alpine",
|
Handler: v1.Handler{
|
||||||
"livenessProbe": {
|
HTTPGet: &v1.HTTPGetAction{
|
||||||
"httpGet": {
|
Host: "localhost",
|
||||||
"host": "localhost"
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"readinessProbe": {
|
ReadinessProbe: &v1.Probe{
|
||||||
"httpGet": {
|
Handler: v1.Handler{
|
||||||
"host": "localhost"
|
HTTPGet: &v1.HTTPGetAction{
|
||||||
}
|
Host: "localhost",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]`,
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -577,27 +566,29 @@ func TestSetDefaultReplicationControllerInitContainers(t *testing.T) {
|
|||||||
rc: v1.ReplicationController{
|
rc: v1.ReplicationController{
|
||||||
Spec: v1.ReplicationControllerSpec{
|
Spec: v1.ReplicationControllerSpec{
|
||||||
Template: &v1.PodTemplateSpec{
|
Template: &v1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
Spec: v1.PodSpec{
|
||||||
Annotations: map[string]string{
|
InitContainers: []v1.Container{
|
||||||
"pod.beta.kubernetes.io/init-containers": `
|
{
|
||||||
[
|
Name: "fun",
|
||||||
{
|
Image: "alpine",
|
||||||
"name": "fun",
|
Ports: []v1.ContainerPort{
|
||||||
"image": "alpine",
|
{
|
||||||
"lifecycle": {
|
Name: "default",
|
||||||
"postStart": {
|
},
|
||||||
"httpGet": {
|
},
|
||||||
"host": "localhost"
|
Lifecycle: &v1.Lifecycle{
|
||||||
}
|
PostStart: &v1.Handler{
|
||||||
},
|
HTTPGet: &v1.HTTPGetAction{
|
||||||
"preStop": {
|
Host: "localhost",
|
||||||
"httpGet": {
|
},
|
||||||
"host": "localhost"
|
},
|
||||||
}
|
PreStop: &v1.Handler{
|
||||||
}
|
HTTPGet: &v1.HTTPGetAction{
|
||||||
}
|
Host: "localhost",
|
||||||
}
|
},
|
||||||
]`,
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package pod
|
package pod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"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)
|
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
|
// Visitor is called with each object name, and returns true if visiting should continue
|
||||||
type Visitor func(name string) (shouldContinue bool)
|
type Visitor func(name string) (shouldContinue bool)
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package pod
|
package pod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3237,6 +3237,11 @@ func autoConvert_v1_Pod_To_api_Pod(in *v1.Pod, out *api.Pod, s conversion.Scope)
|
|||||||
return nil
|
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 {
|
func autoConvert_api_Pod_To_v1_Pod(in *api.Pod, out *v1.Pod, s conversion.Scope) error {
|
||||||
out.ObjectMeta = in.ObjectMeta
|
out.ObjectMeta = in.ObjectMeta
|
||||||
if err := Convert_api_PodSpec_To_v1_PodSpec(&in.Spec, &out.Spec, s); err != nil {
|
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
|
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 {
|
func autoConvert_api_PodStatusResult_To_v1_PodStatusResult(in *api.PodStatusResult, out *v1.PodStatusResult, s conversion.Scope) error {
|
||||||
out.ObjectMeta = in.ObjectMeta
|
out.ObjectMeta = in.ObjectMeta
|
||||||
if err := Convert_api_PodStatus_To_v1_PodStatus(&in.Status, &out.Status, s); err != nil {
|
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
|
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 {
|
func autoConvert_v1_PodTemplate_To_api_PodTemplate(in *v1.PodTemplate, out *api.PodTemplate, s conversion.Scope) error {
|
||||||
out.ObjectMeta = in.ObjectMeta
|
out.ObjectMeta = in.ObjectMeta
|
||||||
if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
|
if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
|
||||||
|
@ -28,7 +28,6 @@ go_library(
|
|||||||
"//pkg/api/helper:go_default_library",
|
"//pkg/api/helper:go_default_library",
|
||||||
"//pkg/api/install:go_default_library",
|
"//pkg/api/install:go_default_library",
|
||||||
"//pkg/api/v1:go_default_library",
|
"//pkg/api/v1:go_default_library",
|
||||||
"//pkg/api/v1/pod:go_default_library",
|
|
||||||
"//pkg/api/validation:go_default_library",
|
"//pkg/api/validation:go_default_library",
|
||||||
"//pkg/kubelet/container:go_default_library",
|
"//pkg/kubelet/container:go_default_library",
|
||||||
"//pkg/kubelet/events:go_default_library",
|
"//pkg/kubelet/events:go_default_library",
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
|
||||||
"k8s.io/kubernetes/pkg/api/validation"
|
"k8s.io/kubernetes/pkg/api/validation"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/events"
|
"k8s.io/kubernetes/pkg/kubelet/events"
|
||||||
@ -256,17 +255,6 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
|
|||||||
}
|
}
|
||||||
|
|
||||||
update := change.(kubetypes.PodUpdate)
|
update := change.(kubetypes.PodUpdate)
|
||||||
// The InitContainers and InitContainerStatuses fields are lost during
|
|
||||||
// serialization and deserialization. They are conveyed via Annotations.
|
|
||||||
// Setting these fields here so that kubelet doesn't have to check for
|
|
||||||
// annotations.
|
|
||||||
if source == kubetypes.ApiserverSource {
|
|
||||||
for _, pod := range update.Pods {
|
|
||||||
if err := podutil.SetInitContainersAndStatuses(pod); err != nil {
|
|
||||||
glog.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch update.Op {
|
switch update.Op {
|
||||||
case kubetypes.ADD, kubetypes.UPDATE, kubetypes.DELETE:
|
case kubetypes.ADD, kubetypes.UPDATE, kubetypes.DELETE:
|
||||||
if update.Op == kubetypes.ADD {
|
if update.Op == kubetypes.ADD {
|
||||||
|
@ -440,9 +440,6 @@ func (m *manager) syncPod(uid types.UID, status versionedPodStatus) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
pod.Status = status.status
|
pod.Status = status.status
|
||||||
if err := podutil.SetInitContainersStatusesAnnotations(pod); err != nil {
|
|
||||||
glog.Error(err)
|
|
||||||
}
|
|
||||||
// TODO: handle conflict as a retry, make that easier too.
|
// TODO: handle conflict as a retry, make that easier too.
|
||||||
newPod, err := m.kubeClient.Core().Pods(pod.Namespace).UpdateStatus(pod)
|
newPod, err := m.kubeClient.Core().Pods(pod.Namespace).UpdateStatus(pod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2493,27 +2493,6 @@ const (
|
|||||||
TolerationOpEqual TolerationOperator = "Equal"
|
TolerationOpEqual TolerationOperator = "Equal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// This annotation key will be used to contain an array of v1 JSON encoded Containers
|
|
||||||
// for init containers. The annotation will be placed into the internal type and cleared.
|
|
||||||
// This key is only recognized by version >= 1.4.
|
|
||||||
PodInitContainersBetaAnnotationKey = "pod.beta.kubernetes.io/init-containers"
|
|
||||||
// This annotation key will be used to contain an array of v1 JSON encoded Containers
|
|
||||||
// for init containers. The annotation will be placed into the internal type and cleared.
|
|
||||||
// This key is recognized by version >= 1.3. For version 1.4 code, this key
|
|
||||||
// will have its value copied to the beta key.
|
|
||||||
PodInitContainersAnnotationKey = "pod.alpha.kubernetes.io/init-containers"
|
|
||||||
// This annotation key will be used to contain an array of v1 JSON encoded
|
|
||||||
// ContainerStatuses for init containers. The annotation will be placed into the internal
|
|
||||||
// type and cleared. This key is only recognized by version >= 1.4.
|
|
||||||
PodInitContainerStatusesBetaAnnotationKey = "pod.beta.kubernetes.io/init-container-statuses"
|
|
||||||
// This annotation key will be used to contain an array of v1 JSON encoded
|
|
||||||
// ContainerStatuses for init containers. The annotation will be placed into the internal
|
|
||||||
// type and cleared. This key is recognized by version >= 1.3. For version 1.4 code,
|
|
||||||
// this key will have its value copied to the beta key.
|
|
||||||
PodInitContainerStatusesAnnotationKey = "pod.alpha.kubernetes.io/init-container-statuses"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PodSpec is a description of a pod.
|
// PodSpec is a description of a pod.
|
||||||
type PodSpec struct {
|
type PodSpec struct {
|
||||||
// List of volumes that can be mounted by containers belonging to the pod.
|
// List of volumes that can be mounted by containers belonging to the pod.
|
||||||
|
@ -78,42 +78,25 @@ var _ = framework.KubeDescribe("InitContainer", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stable := true
|
framework.Logf("PodSpec: initContainers in spec.initContainers")
|
||||||
for i := 0; i < 2; i++ {
|
startedPod := podClient.Create(pod)
|
||||||
if !stable {
|
w, err := podClient.Watch(metav1.SingleObject(startedPod.ObjectMeta))
|
||||||
framework.Logf("PodSpec: initContainers in metadata.annotation")
|
Expect(err).NotTo(HaveOccurred(), "error watching a pod")
|
||||||
if err := podutil.SetInitContainersAnnotations(pod); err != nil {
|
wr := watch.NewRecorder(w)
|
||||||
Expect(err).To(BeNil())
|
event, err := watch.Until(framework.PodStartTimeout, wr, conditions.PodCompleted)
|
||||||
}
|
Expect(err).To(BeNil())
|
||||||
} else {
|
framework.CheckInvariants(wr.Events(), framework.ContainerInitInvariant)
|
||||||
framework.Logf("PodSpec: initContainers in spec.initContainers")
|
endPod := event.Object.(*v1.Pod)
|
||||||
}
|
Expect(endPod.Status.Phase).To(Equal(v1.PodSucceeded))
|
||||||
startedPod := podClient.Create(pod)
|
_, init := podutil.GetPodCondition(&endPod.Status, v1.PodInitialized)
|
||||||
w, err := podClient.Watch(metav1.SingleObject(startedPod.ObjectMeta))
|
Expect(init).NotTo(BeNil())
|
||||||
Expect(err).NotTo(HaveOccurred(), "error watching a pod")
|
Expect(init.Status).To(Equal(v1.ConditionTrue))
|
||||||
wr := watch.NewRecorder(w)
|
|
||||||
event, err := watch.Until(framework.PodStartTimeout, wr, conditions.PodCompleted)
|
|
||||||
Expect(err).To(BeNil())
|
|
||||||
framework.CheckInvariants(wr.Events(), framework.ContainerInitInvariant)
|
|
||||||
endPod := event.Object.(*v1.Pod)
|
|
||||||
if err := podutil.SetInitContainersAndStatuses(endPod); err != nil {
|
|
||||||
Expect(err).To(BeNil())
|
|
||||||
}
|
|
||||||
|
|
||||||
Expect(endPod.Status.Phase).To(Equal(v1.PodSucceeded))
|
Expect(len(endPod.Status.InitContainerStatuses)).To(Equal(2))
|
||||||
_, init := podutil.GetPodCondition(&endPod.Status, v1.PodInitialized)
|
for _, status := range endPod.Status.InitContainerStatuses {
|
||||||
Expect(init).NotTo(BeNil())
|
Expect(status.Ready).To(BeTrue())
|
||||||
Expect(init.Status).To(Equal(v1.ConditionTrue))
|
Expect(status.State.Terminated).NotTo(BeNil())
|
||||||
|
Expect(status.State.Terminated.ExitCode).To(BeZero())
|
||||||
Expect(len(endPod.Status.InitContainerStatuses)).To(Equal(2))
|
|
||||||
for _, status := range endPod.Status.InitContainerStatuses {
|
|
||||||
Expect(status.Ready).To(BeTrue())
|
|
||||||
Expect(status.State.Terminated).NotTo(BeNil())
|
|
||||||
Expect(status.State.Terminated.ExitCode).To(BeZero())
|
|
||||||
}
|
|
||||||
stable = false
|
|
||||||
name := "pod-init-" + string(uuid.NewUUID())
|
|
||||||
pod.Name = name
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -158,42 +141,25 @@ var _ = framework.KubeDescribe("InitContainer", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stable := true
|
framework.Logf("PodSpec: initContainers in spec.initContainers")
|
||||||
for i := 0; i < 2; i++ {
|
startedPod := podClient.Create(pod)
|
||||||
if !stable {
|
w, err := podClient.Watch(metav1.SingleObject(startedPod.ObjectMeta))
|
||||||
framework.Logf("PodSpec: initContainers in metadata.annotation")
|
Expect(err).NotTo(HaveOccurred(), "error watching a pod")
|
||||||
if err := podutil.SetInitContainersAnnotations(pod); err != nil {
|
wr := watch.NewRecorder(w)
|
||||||
Expect(err).To(BeNil())
|
event, err := watch.Until(framework.PodStartTimeout, wr, conditions.PodRunning)
|
||||||
}
|
Expect(err).To(BeNil())
|
||||||
} else {
|
framework.CheckInvariants(wr.Events(), framework.ContainerInitInvariant)
|
||||||
framework.Logf("PodSpec: initContainers in spec.initContainers")
|
endPod := event.Object.(*v1.Pod)
|
||||||
}
|
Expect(endPod.Status.Phase).To(Equal(v1.PodRunning))
|
||||||
startedPod := podClient.Create(pod)
|
_, init := podutil.GetPodCondition(&endPod.Status, v1.PodInitialized)
|
||||||
w, err := podClient.Watch(metav1.SingleObject(startedPod.ObjectMeta))
|
Expect(init).NotTo(BeNil())
|
||||||
Expect(err).NotTo(HaveOccurred(), "error watching a pod")
|
Expect(init.Status).To(Equal(v1.ConditionTrue))
|
||||||
wr := watch.NewRecorder(w)
|
|
||||||
event, err := watch.Until(framework.PodStartTimeout, wr, conditions.PodRunning)
|
|
||||||
Expect(err).To(BeNil())
|
|
||||||
framework.CheckInvariants(wr.Events(), framework.ContainerInitInvariant)
|
|
||||||
endPod := event.Object.(*v1.Pod)
|
|
||||||
|
|
||||||
Expect(endPod.Status.Phase).To(Equal(v1.PodRunning))
|
Expect(len(endPod.Status.InitContainerStatuses)).To(Equal(2))
|
||||||
_, init := podutil.GetPodCondition(&endPod.Status, v1.PodInitialized)
|
for _, status := range endPod.Status.InitContainerStatuses {
|
||||||
Expect(init).NotTo(BeNil())
|
Expect(status.Ready).To(BeTrue())
|
||||||
Expect(init.Status).To(Equal(v1.ConditionTrue))
|
Expect(status.State.Terminated).NotTo(BeNil())
|
||||||
if err := podutil.SetInitContainersAndStatuses(endPod); err != nil {
|
Expect(status.State.Terminated.ExitCode).To(BeZero())
|
||||||
Expect(err).To(BeNil())
|
|
||||||
}
|
|
||||||
|
|
||||||
Expect(len(endPod.Status.InitContainerStatuses)).To(Equal(2))
|
|
||||||
for _, status := range endPod.Status.InitContainerStatuses {
|
|
||||||
Expect(status.Ready).To(BeTrue())
|
|
||||||
Expect(status.State.Terminated).NotTo(BeNil())
|
|
||||||
Expect(status.State.Terminated.ExitCode).To(BeZero())
|
|
||||||
}
|
|
||||||
stable = false
|
|
||||||
name := "pod-init-" + string(uuid.NewUUID())
|
|
||||||
pod.Name = name
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -239,95 +205,72 @@ var _ = framework.KubeDescribe("InitContainer", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stable := true
|
framework.Logf("PodSpec: initContainers in spec.initContainers")
|
||||||
for i := 0; i < 2; i++ {
|
startedPod := podClient.Create(pod)
|
||||||
if !stable {
|
w, err := podClient.Watch(metav1.SingleObject(startedPod.ObjectMeta))
|
||||||
framework.Logf("PodSpec: initContainers in metadata.annotation")
|
Expect(err).NotTo(HaveOccurred(), "error watching a pod")
|
||||||
if err := podutil.SetInitContainersAnnotations(pod); err != nil {
|
|
||||||
Expect(err).To(BeNil())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
framework.Logf("PodSpec: initContainers in spec.initContainers")
|
|
||||||
}
|
|
||||||
startedPod := podClient.Create(pod)
|
|
||||||
w, err := podClient.Watch(metav1.SingleObject(startedPod.ObjectMeta))
|
|
||||||
Expect(err).NotTo(HaveOccurred(), "error watching a pod")
|
|
||||||
|
|
||||||
wr := watch.NewRecorder(w)
|
wr := watch.NewRecorder(w)
|
||||||
event, err := watch.Until(
|
event, err := watch.Until(
|
||||||
framework.PodStartTimeout, wr,
|
framework.PodStartTimeout, wr,
|
||||||
// check for the first container to fail at least once
|
// check for the first container to fail at least once
|
||||||
func(evt watch.Event) (bool, error) {
|
func(evt watch.Event) (bool, error) {
|
||||||
switch t := evt.Object.(type) {
|
switch t := evt.Object.(type) {
|
||||||
case *v1.Pod:
|
case *v1.Pod:
|
||||||
if err := podutil.SetInitContainersAndStatuses(t); err != nil {
|
for _, status := range t.Status.ContainerStatuses {
|
||||||
Expect(err).To(BeNil())
|
|
||||||
}
|
|
||||||
for _, status := range t.Status.ContainerStatuses {
|
|
||||||
if status.State.Waiting == nil {
|
|
||||||
return false, fmt.Errorf("container %q should not be out of waiting: %#v", status.Name, status)
|
|
||||||
}
|
|
||||||
if status.State.Waiting.Reason != "PodInitializing" {
|
|
||||||
return false, fmt.Errorf("container %q should have reason PodInitializing: %#v", status.Name, status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(t.Status.InitContainerStatuses) != 2 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
status := t.Status.InitContainerStatuses[1]
|
|
||||||
if status.State.Waiting == nil {
|
if status.State.Waiting == nil {
|
||||||
return false, fmt.Errorf("second init container should not be out of waiting: %#v", status)
|
return false, fmt.Errorf("container %q should not be out of waiting: %#v", status.Name, status)
|
||||||
}
|
}
|
||||||
if status.State.Waiting.Reason != "PodInitializing" {
|
if status.State.Waiting.Reason != "PodInitializing" {
|
||||||
return false, fmt.Errorf("second init container should have reason PodInitializing: %#v", status)
|
return false, fmt.Errorf("container %q should have reason PodInitializing: %#v", status.Name, status)
|
||||||
}
|
}
|
||||||
status = t.Status.InitContainerStatuses[0]
|
|
||||||
if status.State.Terminated != nil && status.State.Terminated.ExitCode == 0 {
|
|
||||||
return false, fmt.Errorf("first init container should have exitCode != 0: %#v", status)
|
|
||||||
}
|
|
||||||
// continue until we see an attempt to restart the pod
|
|
||||||
return status.LastTerminationState.Terminated != nil, nil
|
|
||||||
default:
|
|
||||||
return false, fmt.Errorf("unexpected object: %#v", t)
|
|
||||||
}
|
}
|
||||||
},
|
if len(t.Status.InitContainerStatuses) != 2 {
|
||||||
// verify we get two restarts
|
return false, nil
|
||||||
func(evt watch.Event) (bool, error) {
|
|
||||||
switch t := evt.Object.(type) {
|
|
||||||
case *v1.Pod:
|
|
||||||
if err := podutil.SetInitContainersAndStatuses(t); err != nil {
|
|
||||||
Expect(err).To(BeNil())
|
|
||||||
}
|
|
||||||
status := t.Status.InitContainerStatuses[0]
|
|
||||||
if status.RestartCount < 3 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
framework.Logf("init container has failed twice: %#v", t)
|
|
||||||
// TODO: more conditions
|
|
||||||
return true, nil
|
|
||||||
default:
|
|
||||||
return false, fmt.Errorf("unexpected object: %#v", t)
|
|
||||||
}
|
}
|
||||||
},
|
status := t.Status.InitContainerStatuses[1]
|
||||||
)
|
if status.State.Waiting == nil {
|
||||||
Expect(err).To(BeNil())
|
return false, fmt.Errorf("second init container should not be out of waiting: %#v", status)
|
||||||
framework.CheckInvariants(wr.Events(), framework.ContainerInitInvariant)
|
}
|
||||||
endPod := event.Object.(*v1.Pod)
|
if status.State.Waiting.Reason != "PodInitializing" {
|
||||||
if err := podutil.SetInitContainersAndStatuses(endPod); err != nil {
|
return false, fmt.Errorf("second init container should have reason PodInitializing: %#v", status)
|
||||||
Expect(err).To(BeNil())
|
}
|
||||||
}
|
status = t.Status.InitContainerStatuses[0]
|
||||||
|
if status.State.Terminated != nil && status.State.Terminated.ExitCode == 0 {
|
||||||
Expect(endPod.Status.Phase).To(Equal(v1.PodPending))
|
return false, fmt.Errorf("first init container should have exitCode != 0: %#v", status)
|
||||||
_, init := podutil.GetPodCondition(&endPod.Status, v1.PodInitialized)
|
}
|
||||||
Expect(init).NotTo(BeNil())
|
// continue until we see an attempt to restart the pod
|
||||||
Expect(init.Status).To(Equal(v1.ConditionFalse))
|
return status.LastTerminationState.Terminated != nil, nil
|
||||||
Expect(init.Reason).To(Equal("ContainersNotInitialized"))
|
default:
|
||||||
Expect(init.Message).To(Equal("containers with incomplete status: [init1 init2]"))
|
return false, fmt.Errorf("unexpected object: %#v", t)
|
||||||
Expect(len(endPod.Status.InitContainerStatuses)).To(Equal(2))
|
}
|
||||||
stable = false
|
},
|
||||||
name := "pod-init-" + string(uuid.NewUUID())
|
// verify we get two restarts
|
||||||
pod.Name = name
|
func(evt watch.Event) (bool, error) {
|
||||||
}
|
switch t := evt.Object.(type) {
|
||||||
|
case *v1.Pod:
|
||||||
|
status := t.Status.InitContainerStatuses[0]
|
||||||
|
if status.RestartCount < 3 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
framework.Logf("init container has failed twice: %#v", t)
|
||||||
|
// TODO: more conditions
|
||||||
|
return true, nil
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("unexpected object: %#v", t)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
framework.CheckInvariants(wr.Events(), framework.ContainerInitInvariant)
|
||||||
|
endPod := event.Object.(*v1.Pod)
|
||||||
|
Expect(endPod.Status.Phase).To(Equal(v1.PodPending))
|
||||||
|
_, init := podutil.GetPodCondition(&endPod.Status, v1.PodInitialized)
|
||||||
|
Expect(init).NotTo(BeNil())
|
||||||
|
Expect(init.Status).To(Equal(v1.ConditionFalse))
|
||||||
|
Expect(init.Reason).To(Equal("ContainersNotInitialized"))
|
||||||
|
Expect(init.Message).To(Equal("containers with incomplete status: [init1 init2]"))
|
||||||
|
Expect(len(endPod.Status.InitContainerStatuses)).To(Equal(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should not start app containers and fail the pod if init containers fail on a RestartNever pod", func() {
|
It("should not start app containers and fail the pod if init containers fail on a RestartNever pod", func() {
|
||||||
@ -373,81 +316,65 @@ var _ = framework.KubeDescribe("InitContainer", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stable := true
|
framework.Logf("PodSpec: initContainers in spec.initContainers")
|
||||||
for i := 0; i < 2; i++ {
|
startedPod := podClient.Create(pod)
|
||||||
if !stable {
|
|
||||||
framework.Logf("PodSpec: initContainers in metadata.annotation")
|
|
||||||
if err := podutil.SetInitContainersAnnotations(pod); err != nil {
|
|
||||||
Expect(err).To(BeNil())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
framework.Logf("PodSpec: initContainers in spec.initContainers")
|
|
||||||
}
|
|
||||||
startedPod := podClient.Create(pod)
|
|
||||||
|
|
||||||
w, err := podClient.Watch(metav1.SingleObject(startedPod.ObjectMeta))
|
w, err := podClient.Watch(metav1.SingleObject(startedPod.ObjectMeta))
|
||||||
Expect(err).NotTo(HaveOccurred(), "error watching a pod")
|
Expect(err).NotTo(HaveOccurred(), "error watching a pod")
|
||||||
|
|
||||||
wr := watch.NewRecorder(w)
|
wr := watch.NewRecorder(w)
|
||||||
event, err := watch.Until(
|
event, err := watch.Until(
|
||||||
framework.PodStartTimeout, wr,
|
framework.PodStartTimeout, wr,
|
||||||
// check for the second container to fail at least once
|
// check for the second container to fail at least once
|
||||||
func(evt watch.Event) (bool, error) {
|
func(evt watch.Event) (bool, error) {
|
||||||
switch t := evt.Object.(type) {
|
switch t := evt.Object.(type) {
|
||||||
case *v1.Pod:
|
case *v1.Pod:
|
||||||
if err := podutil.SetInitContainersAndStatuses(t); err != nil {
|
for _, status := range t.Status.ContainerStatuses {
|
||||||
Expect(err).To(BeNil())
|
if status.State.Waiting == nil {
|
||||||
|
return false, fmt.Errorf("container %q should not be out of waiting: %#v", status.Name, status)
|
||||||
}
|
}
|
||||||
for _, status := range t.Status.ContainerStatuses {
|
if status.State.Waiting.Reason != "PodInitializing" {
|
||||||
if status.State.Waiting == nil {
|
return false, fmt.Errorf("container %q should have reason PodInitializing: %#v", status.Name, status)
|
||||||
return false, fmt.Errorf("container %q should not be out of waiting: %#v", status.Name, status)
|
|
||||||
}
|
|
||||||
if status.State.Waiting.Reason != "PodInitializing" {
|
|
||||||
return false, fmt.Errorf("container %q should have reason PodInitializing: %#v", status.Name, status)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if len(t.Status.InitContainerStatuses) != 2 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
status := t.Status.InitContainerStatuses[0]
|
|
||||||
if status.State.Terminated == nil {
|
|
||||||
if status.State.Waiting != nil && status.State.Waiting.Reason != "PodInitializing" {
|
|
||||||
return false, fmt.Errorf("second init container should have reason PodInitializing: %#v", status)
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
if status.State.Terminated != nil && status.State.Terminated.ExitCode != 0 {
|
|
||||||
return false, fmt.Errorf("first init container should have exitCode != 0: %#v", status)
|
|
||||||
}
|
|
||||||
status = t.Status.InitContainerStatuses[1]
|
|
||||||
if status.State.Terminated == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
if status.State.Terminated.ExitCode == 0 {
|
|
||||||
return false, fmt.Errorf("second init container should have failed: %#v", status)
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
default:
|
|
||||||
return false, fmt.Errorf("unexpected object: %#v", t)
|
|
||||||
}
|
}
|
||||||
},
|
if len(t.Status.InitContainerStatuses) != 2 {
|
||||||
conditions.PodCompleted,
|
return false, nil
|
||||||
)
|
}
|
||||||
Expect(err).To(BeNil())
|
status := t.Status.InitContainerStatuses[0]
|
||||||
framework.CheckInvariants(wr.Events(), framework.ContainerInitInvariant)
|
if status.State.Terminated == nil {
|
||||||
endPod := event.Object.(*v1.Pod)
|
if status.State.Waiting != nil && status.State.Waiting.Reason != "PodInitializing" {
|
||||||
|
return false, fmt.Errorf("second init container should have reason PodInitializing: %#v", status)
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if status.State.Terminated != nil && status.State.Terminated.ExitCode != 0 {
|
||||||
|
return false, fmt.Errorf("first init container should have exitCode != 0: %#v", status)
|
||||||
|
}
|
||||||
|
status = t.Status.InitContainerStatuses[1]
|
||||||
|
if status.State.Terminated == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if status.State.Terminated.ExitCode == 0 {
|
||||||
|
return false, fmt.Errorf("second init container should have failed: %#v", status)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("unexpected object: %#v", t)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
conditions.PodCompleted,
|
||||||
|
)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
framework.CheckInvariants(wr.Events(), framework.ContainerInitInvariant)
|
||||||
|
endPod := event.Object.(*v1.Pod)
|
||||||
|
|
||||||
Expect(endPod.Status.Phase).To(Equal(v1.PodFailed))
|
Expect(endPod.Status.Phase).To(Equal(v1.PodFailed))
|
||||||
_, init := podutil.GetPodCondition(&endPod.Status, v1.PodInitialized)
|
_, init := podutil.GetPodCondition(&endPod.Status, v1.PodInitialized)
|
||||||
Expect(init).NotTo(BeNil())
|
Expect(init).NotTo(BeNil())
|
||||||
Expect(init.Status).To(Equal(v1.ConditionFalse))
|
Expect(init.Status).To(Equal(v1.ConditionFalse))
|
||||||
Expect(init.Reason).To(Equal("ContainersNotInitialized"))
|
Expect(init.Reason).To(Equal("ContainersNotInitialized"))
|
||||||
Expect(init.Message).To(Equal("containers with incomplete status: [init2]"))
|
Expect(init.Message).To(Equal("containers with incomplete status: [init2]"))
|
||||||
Expect(len(endPod.Status.InitContainerStatuses)).To(Equal(2))
|
Expect(len(endPod.Status.InitContainerStatuses)).To(Equal(2))
|
||||||
Expect(endPod.Status.ContainerStatuses[0].State.Waiting).ToNot(BeNil())
|
Expect(endPod.Status.ContainerStatuses[0].State.Waiting).ToNot(BeNil())
|
||||||
stable = false
|
|
||||||
name := "pod-init-" + string(uuid.NewUUID())
|
|
||||||
pod.Name = name
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -9,47 +9,48 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: mysql
|
app: mysql
|
||||||
annotations:
|
|
||||||
pod.beta.kubernetes.io/init-containers: '[
|
|
||||||
{
|
|
||||||
"name": "init-mysql",
|
|
||||||
"image": "mysql:5.7",
|
|
||||||
"command": ["bash", "-c", "
|
|
||||||
set -ex\n
|
|
||||||
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1\n
|
|
||||||
ordinal=${BASH_REMATCH[1]}\n
|
|
||||||
echo [mysqld] > /mnt/conf.d/server-id.cnf\n
|
|
||||||
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf\n
|
|
||||||
if [[ $ordinal -eq 0 ]]; then\n
|
|
||||||
cp /mnt/config-map/master.cnf /mnt/conf.d/\n
|
|
||||||
else\n
|
|
||||||
cp /mnt/config-map/slave.cnf /mnt/conf.d/\n
|
|
||||||
fi\n
|
|
||||||
"],
|
|
||||||
"volumeMounts": [
|
|
||||||
{"name": "conf", "mountPath": "/mnt/conf.d"},
|
|
||||||
{"name": "config-map", "mountPath": "/mnt/config-map"}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "clone-mysql",
|
|
||||||
"image": "gcr.io/google-samples/xtrabackup:1.0",
|
|
||||||
"command": ["bash", "-c", "
|
|
||||||
set -ex\n
|
|
||||||
[[ -d /var/lib/mysql/mysql ]] && exit 0\n
|
|
||||||
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1\n
|
|
||||||
ordinal=${BASH_REMATCH[1]}\n
|
|
||||||
[[ $ordinal -eq 0 ]] && exit 0\n
|
|
||||||
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql\n
|
|
||||||
xtrabackup --prepare --target-dir=/var/lib/mysql\n
|
|
||||||
"],
|
|
||||||
"volumeMounts": [
|
|
||||||
{"name": "data", "mountPath": "/var/lib/mysql", "subPath": "mysql"},
|
|
||||||
{"name": "conf", "mountPath": "/etc/mysql/conf.d"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]'
|
|
||||||
spec:
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: init-mysql
|
||||||
|
image: mysql:5.7
|
||||||
|
command:
|
||||||
|
- bash
|
||||||
|
- "-c"
|
||||||
|
- |
|
||||||
|
set -ex
|
||||||
|
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
|
||||||
|
ordinal=${BASH_REMATCH[1]}
|
||||||
|
echo [mysqld] > /mnt/conf.d/server-id.cnf
|
||||||
|
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
|
||||||
|
if [[ $ordinal -eq 0 ]]; then
|
||||||
|
cp /mnt/config-map/master.cnf /mnt/conf.d/
|
||||||
|
else
|
||||||
|
cp /mnt/config-map/slave.cnf /mnt/conf.d/
|
||||||
|
fi
|
||||||
|
volumeMounts:
|
||||||
|
- name: conf
|
||||||
|
mountPath: /mnt/conf.d
|
||||||
|
- name: config-map
|
||||||
|
mountPath: /mnt/config-map
|
||||||
|
- name: clone-mysql
|
||||||
|
image: gcr.io/google-samples/xtrabackup:1.0
|
||||||
|
command:
|
||||||
|
- bash
|
||||||
|
- "-c"
|
||||||
|
- |
|
||||||
|
set -ex
|
||||||
|
[[ -d /var/lib/mysql/mysql ]] && exit 0
|
||||||
|
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
|
||||||
|
ordinal=${BASH_REMATCH[1]}
|
||||||
|
[[ $ordinal -eq 0 ]] && exit 0
|
||||||
|
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
|
||||||
|
xtrabackup --prepare --target-dir=/var/lib/mysql
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /var/lib/mysql
|
||||||
|
subPath: mysql
|
||||||
|
- name: conf
|
||||||
|
mountPath: /etc/mysql/conf.d
|
||||||
containers:
|
containers:
|
||||||
- name: mysql
|
- name: mysql
|
||||||
image: mysql:5.7.15
|
image: mysql:5.7.15
|
||||||
@ -138,10 +139,9 @@ spec:
|
|||||||
volumeClaimTemplates:
|
volumeClaimTemplates:
|
||||||
- metadata:
|
- metadata:
|
||||||
name: data
|
name: data
|
||||||
annotations:
|
|
||||||
volume.alpha.kubernetes.io/storage-class: default
|
|
||||||
spec:
|
spec:
|
||||||
accessModes: ["ReadWriteOnce"]
|
accessModes: ["ReadWriteOnce"]
|
||||||
|
storageClassName: default
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: 10Gi
|
storage: 10Gi
|
||||||
|
Loading…
Reference in New Issue
Block a user