API for adding init containers

This commit is contained in:
Clayton Coleman 2016-03-28 22:13:16 -04:00
parent fee8a55943
commit 6685715c4c
No known key found for this signature in database
GPG Key ID: 3D16906B4F1C5CB3
6 changed files with 224 additions and 3 deletions

View File

@ -131,6 +131,17 @@ func UpdatePodCondition(status *PodStatus, condition *PodCondition) bool {
}
}
// GetPodCondition extracts the provided condition from the given status and returns that.
// Returns nil if the condition is not present.
func GetPodCondition(status PodStatus, t PodConditionType) *PodCondition {
for i, c := range status.Conditions {
if c.Type == t {
return &status.Conditions[i]
}
}
return nil
}
// IsNodeReady returns true if a node is ready; false otherwise.
func IsNodeReady(node *Node) bool {
for _, c := range node.Status.Conditions {

View File

@ -67,6 +67,9 @@ func TestUniversalDeserializer(t *testing.T) {
func TestProtobufRoundTrip(t *testing.T) {
obj := &v1.Pod{}
apitesting.FuzzerFor(t, v1.SchemeGroupVersion, rand.NewSource(benchmarkSeed)).Fuzz(obj)
// InitContainers are turned into annotations by conversion.
obj.Spec.InitContainers = nil
obj.Status.InitContainerStatuses = nil
data, err := obj.Marshal()
if err != nil {
t.Fatal(err)
@ -77,7 +80,7 @@ func TestProtobufRoundTrip(t *testing.T) {
}
if !api.Semantic.Equalities.DeepEqual(out, obj) {
t.Logf("marshal\n%s", hex.Dump(data))
t.Fatalf("Unmarshal is unequal\n%s", diff.ObjectGoPrintSideBySide(out, obj))
t.Fatalf("Unmarshal is unequal\n%s", diff.ObjectGoPrintDiff(out, obj))
}
}

View File

@ -1076,6 +1076,8 @@ const (
// PodReady means the pod is able to service requests and should be added to the
// load balancing pools of all matching services.
PodReady PodConditionType = "Ready"
// PodInitialized means that all init containers in the pod have started successfully.
PodInitialized PodConditionType = "Initialized"
)
type PodCondition struct {
@ -1309,7 +1311,9 @@ type PreferredSchedulingTerm struct {
// PodSpec is a description of a pod
type PodSpec struct {
Volumes []Volume `json:"volumes"`
// Required: there must be at least one container in a pod.
// List of initialization containers belonging to the pod.
InitContainers []Container `json:"-"`
// List of containers belonging to the pod.
Containers []Container `json:"containers"`
RestartPolicy RestartPolicy `json:"restartPolicy,omitempty"`
// Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request.
@ -1416,6 +1420,11 @@ type PodStatus struct {
// This is before the Kubelet pulled the container image(s) for the pod.
StartTime *unversioned.Time `json:"startTime,omitempty"`
// The list has one entry per init container in the manifest. The most recent successful
// init container will have ready = true, the most recently started container will have
// startTime set.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses
InitContainerStatuses []ContainerStatus `json:"-"`
// The list has one entry per container in the manifest. Each entry is
// currently the output of `docker inspect`. This output format is *not*
// final and should not be relied upon.

View File

@ -17,6 +17,7 @@ limitations under the License.
package v1
import (
"encoding/json"
"fmt"
inf "gopkg.in/inf.v0"
@ -258,6 +259,75 @@ func Convert_v1_ReplicationControllerSpec_To_api_ReplicationControllerSpec(in *R
return nil
}
func Convert_api_PodStatusResult_To_v1_PodStatusResult(in *api.PodStatusResult, out *PodStatusResult, s conversion.Scope) error {
if err := autoConvert_api_PodStatusResult_To_v1_PodStatusResult(in, out, s); err != nil {
return err
}
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[PodInitContainerStatusesAnnotationKey] = string(value)
} else {
delete(in.Annotations, PodInitContainerStatusesAnnotationKey)
}
return nil
}
func Convert_v1_PodStatusResult_To_api_PodStatusResult(in *PodStatusResult, out *api.PodStatusResult, s conversion.Scope) error {
// TODO: when we move init container to beta, remove these conversions
if value := in.Annotations[PodInitContainerStatusesAnnotationKey]; len(value) > 0 {
delete(in.Annotations, PodInitContainerStatusesAnnotationKey)
var values []ContainerStatus
if err := json.Unmarshal([]byte(value), &values); err != nil {
return err
}
in.Status.InitContainerStatuses = values
}
return autoConvert_v1_PodStatusResult_To_api_PodStatusResult(in, out, s)
}
func Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in *api.PodTemplateSpec, out *PodTemplateSpec, s conversion.Scope) error {
if err := autoConvert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in, out, s); err != nil {
return err
}
// TODO: when we move init container to beta, remove these conversions
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[PodInitContainersAnnotationKey] = string(value)
} else {
delete(out.Annotations, PodInitContainersAnnotationKey)
}
return nil
}
func Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in *PodTemplateSpec, out *api.PodTemplateSpec, s conversion.Scope) error {
// TODO: when we move init container to beta, remove these conversions
if value := in.Annotations[PodInitContainersAnnotationKey]; len(value) > 0 {
delete(in.Annotations, PodInitContainersAnnotationKey)
var values []Container
if err := json.Unmarshal([]byte(value), &values); err != nil {
return err
}
in.Spec.InitContainers = values
}
return autoConvert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in, out, s)
}
// The following two PodSpec conversions are done here to support ServiceAccount
// as an alias for ServiceAccountName.
func Convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conversion.Scope) error {
@ -271,6 +341,16 @@ func Convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conversi
} else {
out.Volumes = nil
}
if in.InitContainers != nil {
out.InitContainers = make([]Container, len(in.InitContainers))
for i := range in.InitContainers {
if err := Convert_api_Container_To_v1_Container(&in.InitContainers[i], &out.InitContainers[i], s); err != nil {
return err
}
}
} else {
out.InitContainers = nil
}
if in.Containers != nil {
out.Containers = make([]Container, len(in.Containers))
for i := range in.Containers {
@ -346,6 +426,16 @@ func Convert_v1_PodSpec_To_api_PodSpec(in *PodSpec, out *api.PodSpec, s conversi
} else {
out.Volumes = nil
}
if in.InitContainers != nil {
out.InitContainers = make([]api.Container, len(in.InitContainers))
for i := range in.InitContainers {
if err := Convert_v1_Container_To_api_Container(&in.InitContainers[i], &out.InitContainers[i], s); err != nil {
return err
}
}
} else {
out.InitContainers = nil
}
if in.Containers != nil {
out.Containers = make([]api.Container, len(in.Containers))
for i := range in.Containers {
@ -419,6 +509,33 @@ func Convert_api_Pod_To_v1_Pod(in *api.Pod, out *Pod, s conversion.Scope) error
if err := autoConvert_api_Pod_To_v1_Pod(in, out, s); err != nil {
return err
}
// TODO: when we move init container to beta, remove these conversions
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[PodInitContainersAnnotationKey] = string(value)
} else {
delete(out.Annotations, PodInitContainersAnnotationKey)
}
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[PodInitContainerStatusesAnnotationKey] = string(value)
} else {
delete(in.Annotations, PodInitContainerStatusesAnnotationKey)
}
// We need to reset certain fields for mirror pods from pre-v1.1 kubelet
// (#15960).
// TODO: Remove this code after we drop support for v1.0 kubelets.
@ -434,6 +551,24 @@ func Convert_api_Pod_To_v1_Pod(in *api.Pod, out *Pod, s conversion.Scope) error
}
func Convert_v1_Pod_To_api_Pod(in *Pod, out *api.Pod, s conversion.Scope) error {
// TODO: when we move init container to beta, remove these conversions
if value := in.Annotations[PodInitContainersAnnotationKey]; len(value) > 0 {
delete(in.Annotations, PodInitContainersAnnotationKey)
var values []Container
if err := json.Unmarshal([]byte(value), &values); err != nil {
return err
}
in.Spec.InitContainers = values
}
if value := in.Annotations[PodInitContainerStatusesAnnotationKey]; len(value) > 0 {
delete(in.Annotations, PodInitContainerStatusesAnnotationKey)
var values []ContainerStatus
if err := json.Unmarshal([]byte(value), &values); err != nil {
return err
}
in.Status.InitContainerStatuses = values
}
return autoConvert_v1_Pod_To_api_Pod(in, out, s)
}

View File

@ -1106,7 +1106,7 @@ type Container struct {
// Cannot be updated.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#resources
Resources ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,8,opt,name=resources"`
// Pod volumes to mount into the container's filesyste.
// Pod volumes to mount into the container's filesystem.
// Cannot be updated.
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,9,rep,name=volumeMounts"`
// Periodic probe of container liveness.
@ -1541,11 +1541,36 @@ type PreferredSchedulingTerm struct {
Preference NodeSelectorTerm `json:"preference" protobuf:"bytes,2,opt,name=preference"`
}
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.
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.
PodInitContainerStatusesAnnotationKey = "pod.alpha.kubernetes.io/init-container-statuses"
)
// PodSpec is a description of a pod.
type PodSpec struct {
// List of volumes that can be mounted by containers belonging to the pod.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md
Volumes []Volume `json:"volumes,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,1,rep,name=volumes"`
// List of initialization containers belonging to the pod.
// Init containers are executed in order prior to containers being started. If any
// init container fails, the pod is considered to have failed and is handled according
// to its restartPolicy. The name for an init container or normal container must be
// unique among all containers.
// Init containers may not have Lifecycle actions, Readiness probes, or Liveness probes.
// The resourceRequirements of an init container are taken into account during scheduling
// by finding the highest request/limit for each resource type, and then using the max of
// of that value or the sum of the normal containers. Limits are applied to init containers
// in a similar fashion.
// Init containers cannot currently be added or removed.
// Init containers are in alpha state and may change without notice.
// Cannot be updated.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/containers.md
InitContainers []Container `json:"-" patchStrategy:"merge" patchMergeKey:"name"`
// List of containers belonging to the pod.
// Containers cannot currently be added or removed.
// There must be at least one container in a Pod.
@ -1679,6 +1704,12 @@ type PodStatus struct {
// This is before the Kubelet pulled the container image(s) for the pod.
StartTime *unversioned.Time `json:"startTime,omitempty" protobuf:"bytes,7,opt,name=startTime"`
// The list has one entry per init container in the manifest. The most recent successful
// init container will have ready = true, the most recently started container will have
// startTime set.
// Init containers are in alpha state and may change without notice.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses
InitContainerStatuses []ContainerStatus `json:"-"`
// The list has one entry per container in the manifest. Each entry is currently the output
// of `docker inspect`.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses

View File

@ -1326,6 +1326,37 @@ func validatePullPolicy(policy api.PullPolicy, fldPath *field.Path) field.ErrorL
return allErrors
}
func validateInitContainers(containers, otherContainers []api.Container, volumes sets.String, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
if len(containers) > 0 {
allErrs = append(allErrs, validateContainers(containers, volumes, fldPath)...)
}
allNames := sets.String{}
for _, ctr := range otherContainers {
allNames.Insert(ctr.Name)
}
for i, ctr := range containers {
idxPath := fldPath.Index(i)
if allNames.Has(ctr.Name) {
allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), ctr.Name))
}
if len(ctr.Name) > 0 {
allNames.Insert(ctr.Name)
}
if ctr.Lifecycle != nil {
allErrs = append(allErrs, field.Invalid(idxPath.Child("lifecycle"), ctr.Lifecycle, "must not be set for init containers"))
}
if ctr.LivenessProbe != nil {
allErrs = append(allErrs, field.Invalid(idxPath.Child("livenessProbe"), ctr.LivenessProbe, "must not be set for init containers"))
}
if ctr.ReadinessProbe != nil {
allErrs = append(allErrs, field.Invalid(idxPath.Child("readinessProbe"), ctr.ReadinessProbe, "must not be set for init containers"))
}
}
return allErrs
}
func validateContainers(containers []api.Container, volumes sets.String, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@ -1451,6 +1482,7 @@ func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
allVolumes, vErrs := validateVolumes(spec.Volumes, fldPath.Child("volumes"))
allErrs = append(allErrs, vErrs...)
allErrs = append(allErrs, validateContainers(spec.Containers, allVolumes, fldPath.Child("containers"))...)
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, allVolumes, fldPath.Child("initContainers"))...)
allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)