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
commit 6ec80eac1b
11 changed files with 309 additions and 734 deletions

View File

@ -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
} }

View File

@ -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",
} },
]`, },
},
},
}, },
}, },
}, },

View File

@ -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)

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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",

View File

@ -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 {

View File

@ -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 {

View File

@ -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.

View File

@ -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
}
}) })
}) })

View File

@ -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