mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Introduce PodHasNetwork condition for pods
Signed-off-by: Deep Debroy <ddebroy@gmail.com>
This commit is contained in:
parent
42786afae0
commit
dfdf8245bb
@ -610,6 +610,13 @@ const (
|
|||||||
// Enables controlling pod ranking on replicaset scale-down.
|
// Enables controlling pod ranking on replicaset scale-down.
|
||||||
PodDeletionCost featuregate.Feature = "PodDeletionCost"
|
PodDeletionCost featuregate.Feature = "PodDeletionCost"
|
||||||
|
|
||||||
|
// owner: @ddebroy
|
||||||
|
// alpha: v1.25
|
||||||
|
//
|
||||||
|
// Enables reporting of PodHasNetwork condition in pod status after pod
|
||||||
|
// sandbox creation and network configuration completes successfully
|
||||||
|
PodHasNetworkCondition featuregate.Feature = "PodHasNetworkCondition"
|
||||||
|
|
||||||
// owner: @egernst
|
// owner: @egernst
|
||||||
// alpha: v1.16
|
// alpha: v1.16
|
||||||
// beta: v1.18
|
// beta: v1.18
|
||||||
@ -974,6 +981,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
PodDeletionCost: {Default: true, PreRelease: featuregate.Beta},
|
PodDeletionCost: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
|
PodHasNetworkCondition: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
PodOverhead: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.26
|
PodOverhead: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.26
|
||||||
|
|
||||||
PodSecurity: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
PodSecurity: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||||
|
@ -1430,14 +1430,12 @@ func getPhase(spec *v1.PodSpec, info []v1.ContainerStatus) v1.PodPhase {
|
|||||||
// internal pod status. This method should only be called from within sync*Pod methods.
|
// internal pod status. This method should only be called from within sync*Pod methods.
|
||||||
func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodStatus {
|
func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodStatus {
|
||||||
klog.V(3).InfoS("Generating pod status", "pod", klog.KObj(pod))
|
klog.V(3).InfoS("Generating pod status", "pod", klog.KObj(pod))
|
||||||
|
|
||||||
// use the previous pod status, or the api status, as the basis for this pod
|
// use the previous pod status, or the api status, as the basis for this pod
|
||||||
oldPodStatus, found := kl.statusManager.GetPodStatus(pod.UID)
|
oldPodStatus, found := kl.statusManager.GetPodStatus(pod.UID)
|
||||||
if !found {
|
if !found {
|
||||||
oldPodStatus = pod.Status
|
oldPodStatus = pod.Status
|
||||||
}
|
}
|
||||||
s := kl.convertStatusToAPIStatus(pod, podStatus, oldPodStatus)
|
s := kl.convertStatusToAPIStatus(pod, podStatus, oldPodStatus)
|
||||||
|
|
||||||
// calculate the next phase and preserve reason
|
// calculate the next phase and preserve reason
|
||||||
allStatus := append(append([]v1.ContainerStatus{}, s.ContainerStatuses...), s.InitContainerStatuses...)
|
allStatus := append(append([]v1.ContainerStatus{}, s.ContainerStatuses...), s.InitContainerStatuses...)
|
||||||
s.Phase = getPhase(&pod.Spec, allStatus)
|
s.Phase = getPhase(&pod.Spec, allStatus)
|
||||||
@ -1499,6 +1497,9 @@ func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.Po
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set all Kubelet-owned conditions
|
// set all Kubelet-owned conditions
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.PodHasNetworkCondition) {
|
||||||
|
s.Conditions = append(s.Conditions, status.GeneratePodHasNetworkCondition(pod, podStatus))
|
||||||
|
}
|
||||||
s.Conditions = append(s.Conditions, status.GeneratePodInitializedCondition(&pod.Spec, s.InitContainerStatuses, s.Phase))
|
s.Conditions = append(s.Conditions, status.GeneratePodInitializedCondition(&pod.Spec, s.InitContainerStatuses, s.Phase))
|
||||||
s.Conditions = append(s.Conditions, status.GeneratePodReadyCondition(&pod.Spec, s.Conditions, s.ContainerStatuses, s.Phase))
|
s.Conditions = append(s.Conditions, status.GeneratePodReadyCondition(&pod.Spec, s.Conditions, s.ContainerStatuses, s.Phase))
|
||||||
s.Conditions = append(s.Conditions, status.GenerateContainersReadyCondition(&pod.Spec, s.ContainerStatuses, s.Phase))
|
s.Conditions = append(s.Conditions, status.GenerateContainersReadyCondition(&pod.Spec, s.ContainerStatuses, s.Phase))
|
||||||
@ -1506,7 +1507,6 @@ func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.Po
|
|||||||
Type: v1.PodScheduled,
|
Type: v1.PodScheduled,
|
||||||
Status: v1.ConditionTrue,
|
Status: v1.ConditionTrue,
|
||||||
})
|
})
|
||||||
|
|
||||||
// set HostIP and initialize PodIP/PodIPs for host network pods
|
// set HostIP and initialize PodIP/PodIPs for host network pods
|
||||||
if kl.kubeClient != nil {
|
if kl.kubeClient != nil {
|
||||||
hostIPs, err := kl.getHostIPsAnyWay()
|
hostIPs, err := kl.getHostIPsAnyWay()
|
||||||
|
@ -46,7 +46,11 @@ import (
|
|||||||
// api.Registry.GroupOrDie(v1.GroupName).GroupVersions[0].String() is changed
|
// api.Registry.GroupOrDie(v1.GroupName).GroupVersions[0].String() is changed
|
||||||
// to "v1"?
|
// to "v1"?
|
||||||
|
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cri/streaming/portforward"
|
"k8s.io/kubernetes/pkg/kubelet/cri/streaming/portforward"
|
||||||
@ -2478,18 +2482,30 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
RestartPolicy: v1.RestartPolicyAlways,
|
RestartPolicy: v1.RestartPolicyAlways,
|
||||||
}
|
}
|
||||||
|
sandboxReadyStatus := &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Network: &runtimeapi.PodSandboxNetworkStatus{
|
||||||
|
Ip: "10.0.0.10",
|
||||||
|
},
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_READY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
now := metav1.Now()
|
now := metav1.Now()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
pod *v1.Pod
|
pod *v1.Pod
|
||||||
currentStatus *kubecontainer.PodStatus
|
currentStatus *kubecontainer.PodStatus
|
||||||
unreadyContainer []string
|
unreadyContainer []string
|
||||||
previousStatus v1.PodStatus
|
previousStatus v1.PodStatus
|
||||||
expected v1.PodStatus
|
expected v1.PodStatus
|
||||||
|
expectedPodHasNetworkCondition v1.PodCondition
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no current status, with previous statuses and deletion",
|
name: "current status ready, with previous statuses and deletion",
|
||||||
pod: &v1.Pod{
|
pod: &v1.Pod{
|
||||||
Spec: desiredState,
|
Spec: desiredState,
|
||||||
Status: v1.PodStatus{
|
Status: v1.PodStatus{
|
||||||
@ -2500,7 +2516,7 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "my-pod", DeletionTimestamp: &now},
|
ObjectMeta: metav1.ObjectMeta{Name: "my-pod", DeletionTimestamp: &now},
|
||||||
},
|
},
|
||||||
currentStatus: &kubecontainer.PodStatus{},
|
currentStatus: sandboxReadyStatus,
|
||||||
previousStatus: v1.PodStatus{
|
previousStatus: v1.PodStatus{
|
||||||
ContainerStatuses: []v1.ContainerStatus{
|
ContainerStatuses: []v1.ContainerStatus{
|
||||||
runningState("containerA"),
|
runningState("containerA"),
|
||||||
@ -2522,9 +2538,13 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
ready(waitingWithLastTerminationUnknown("containerB", 0)),
|
ready(waitingWithLastTerminationUnknown("containerB", 0)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
expectedPodHasNetworkCondition: v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no current status, with previous statuses and no deletion",
|
name: "current status ready, with previous statuses and no deletion",
|
||||||
pod: &v1.Pod{
|
pod: &v1.Pod{
|
||||||
Spec: desiredState,
|
Spec: desiredState,
|
||||||
Status: v1.PodStatus{
|
Status: v1.PodStatus{
|
||||||
@ -2534,7 +2554,7 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentStatus: &kubecontainer.PodStatus{},
|
currentStatus: sandboxReadyStatus,
|
||||||
previousStatus: v1.PodStatus{
|
previousStatus: v1.PodStatus{
|
||||||
ContainerStatuses: []v1.ContainerStatus{
|
ContainerStatuses: []v1.ContainerStatus{
|
||||||
runningState("containerA"),
|
runningState("containerA"),
|
||||||
@ -2556,6 +2576,10 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
ready(waitingWithLastTerminationUnknown("containerB", 1)),
|
ready(waitingWithLastTerminationUnknown("containerB", 1)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
expectedPodHasNetworkCondition: v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "terminal phase cannot be changed (apiserver previous is succeeded)",
|
name: "terminal phase cannot be changed (apiserver previous is succeeded)",
|
||||||
@ -2591,6 +2615,10 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
ready(waitingWithLastTerminationUnknown("containerB", 1)),
|
ready(waitingWithLastTerminationUnknown("containerB", 1)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
expectedPodHasNetworkCondition: v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "terminal phase from previous status must remain terminal, restartAlways",
|
name: "terminal phase from previous status must remain terminal, restartAlways",
|
||||||
@ -2632,6 +2660,10 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
Reason: "Test",
|
Reason: "Test",
|
||||||
Message: "test",
|
Message: "test",
|
||||||
},
|
},
|
||||||
|
expectedPodHasNetworkCondition: v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "terminal phase from previous status must remain terminal, restartNever",
|
name: "terminal phase from previous status must remain terminal, restartNever",
|
||||||
@ -2680,6 +2712,10 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
Reason: "Test",
|
Reason: "Test",
|
||||||
Message: "test",
|
Message: "test",
|
||||||
},
|
},
|
||||||
|
expectedPodHasNetworkCondition: v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "running can revert to pending",
|
name: "running can revert to pending",
|
||||||
@ -2693,7 +2729,7 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentStatus: &kubecontainer.PodStatus{},
|
currentStatus: sandboxReadyStatus,
|
||||||
previousStatus: v1.PodStatus{
|
previousStatus: v1.PodStatus{
|
||||||
ContainerStatuses: []v1.ContainerStatus{
|
ContainerStatuses: []v1.ContainerStatus{
|
||||||
waitingState("containerA"),
|
waitingState("containerA"),
|
||||||
@ -2715,6 +2751,10 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
ready(waitingStateWithReason("containerB", "ContainerCreating")),
|
ready(waitingStateWithReason("containerB", "ContainerCreating")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
expectedPodHasNetworkCondition: v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reason and message are preserved when phase doesn't change",
|
name: "reason and message are preserved when phase doesn't change",
|
||||||
@ -2729,6 +2769,7 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentStatus: &kubecontainer.PodStatus{
|
currentStatus: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: sandboxReadyStatus.SandboxStatuses,
|
||||||
ContainerStatuses: []*kubecontainer.Status{
|
ContainerStatuses: []*kubecontainer.Status{
|
||||||
{
|
{
|
||||||
ID: kubecontainer.ContainerID{ID: "foo"},
|
ID: kubecontainer.ContainerID{ID: "foo"},
|
||||||
@ -2764,6 +2805,10 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
ready(withID(runningStateWithStartedAt("containerB", time.Unix(1, 0).UTC()), "://foo")),
|
ready(withID(runningStateWithStartedAt("containerB", time.Unix(1, 0).UTC()), "://foo")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
expectedPodHasNetworkCondition: v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reason and message are cleared when phase changes",
|
name: "reason and message are cleared when phase changes",
|
||||||
@ -2778,6 +2823,7 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentStatus: &kubecontainer.PodStatus{
|
currentStatus: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: sandboxReadyStatus.SandboxStatuses,
|
||||||
ContainerStatuses: []*kubecontainer.Status{
|
ContainerStatuses: []*kubecontainer.Status{
|
||||||
{
|
{
|
||||||
ID: kubecontainer.ContainerID{ID: "c1"},
|
ID: kubecontainer.ContainerID{ID: "c1"},
|
||||||
@ -2817,22 +2863,32 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
ready(withID(runningStateWithStartedAt("containerB", time.Unix(2, 0).UTC()), "://c2")),
|
ready(withID(runningStateWithStartedAt("containerB", time.Unix(2, 0).UTC()), "://c2")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
expectedPodHasNetworkCondition: v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
for _, enablePodHasNetworkCondition := range []bool{false, true} {
|
||||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
t.Run(test.name, func(t *testing.T) {
|
||||||
defer testKubelet.Cleanup()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodHasNetworkCondition, enablePodHasNetworkCondition)()
|
||||||
kl := testKubelet.kubelet
|
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
||||||
kl.statusManager.SetPodStatus(test.pod, test.previousStatus)
|
defer testKubelet.Cleanup()
|
||||||
for _, name := range test.unreadyContainer {
|
kl := testKubelet.kubelet
|
||||||
kl.readinessManager.Set(kubecontainer.BuildContainerID("", findContainerStatusByName(test.expected, name).ContainerID), results.Failure, test.pod)
|
kl.statusManager.SetPodStatus(test.pod, test.previousStatus)
|
||||||
}
|
for _, name := range test.unreadyContainer {
|
||||||
actual := kl.generateAPIPodStatus(test.pod, test.currentStatus)
|
kl.readinessManager.Set(kubecontainer.BuildContainerID("", findContainerStatusByName(test.expected, name).ContainerID), results.Failure, test.pod)
|
||||||
if !apiequality.Semantic.DeepEqual(test.expected, actual) {
|
}
|
||||||
t.Fatalf("Unexpected status: %s", diff.ObjectReflectDiff(actual, test.expected))
|
actual := kl.generateAPIPodStatus(test.pod, test.currentStatus)
|
||||||
}
|
if enablePodHasNetworkCondition {
|
||||||
})
|
test.expected.Conditions = append([]v1.PodCondition{test.expectedPodHasNetworkCondition}, test.expected.Conditions...)
|
||||||
|
}
|
||||||
|
if !apiequality.Semantic.DeepEqual(test.expected, actual) {
|
||||||
|
t.Fatalf("Unexpected status: %s", diff.ObjectReflectDiff(actual, test.expected))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,40 +324,3 @@ func (m *kubeGenericRuntimeManager) getSeccompProfile(annotations map[string]str
|
|||||||
ProfileType: runtimeapi.SecurityProfile_Unconfined,
|
ProfileType: runtimeapi.SecurityProfile_Unconfined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipcNamespaceForPod(pod *v1.Pod) runtimeapi.NamespaceMode {
|
|
||||||
if pod != nil && pod.Spec.HostIPC {
|
|
||||||
return runtimeapi.NamespaceMode_NODE
|
|
||||||
}
|
|
||||||
return runtimeapi.NamespaceMode_POD
|
|
||||||
}
|
|
||||||
|
|
||||||
func networkNamespaceForPod(pod *v1.Pod) runtimeapi.NamespaceMode {
|
|
||||||
if pod != nil && pod.Spec.HostNetwork {
|
|
||||||
return runtimeapi.NamespaceMode_NODE
|
|
||||||
}
|
|
||||||
return runtimeapi.NamespaceMode_POD
|
|
||||||
}
|
|
||||||
|
|
||||||
func pidNamespaceForPod(pod *v1.Pod) runtimeapi.NamespaceMode {
|
|
||||||
if pod != nil {
|
|
||||||
if pod.Spec.HostPID {
|
|
||||||
return runtimeapi.NamespaceMode_NODE
|
|
||||||
}
|
|
||||||
if pod.Spec.ShareProcessNamespace != nil && *pod.Spec.ShareProcessNamespace {
|
|
||||||
return runtimeapi.NamespaceMode_POD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Note that PID does not default to the zero value for v1.Pod
|
|
||||||
return runtimeapi.NamespaceMode_CONTAINER
|
|
||||||
}
|
|
||||||
|
|
||||||
// namespacesForPod returns the runtimeapi.NamespaceOption for a given pod.
|
|
||||||
// An empty or nil pod can be used to get the namespace defaults for v1.Pod.
|
|
||||||
func namespacesForPod(pod *v1.Pod) *runtimeapi.NamespaceOption {
|
|
||||||
return &runtimeapi.NamespaceOption{
|
|
||||||
Ipc: ipcNamespaceForPod(pod),
|
|
||||||
Network: networkNamespaceForPod(pod),
|
|
||||||
Pid: pidNamespaceForPod(pod),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -919,69 +919,3 @@ func TestGetSeccompProfileDefaultSeccomp(t *testing.T) {
|
|||||||
func getLocal(v string) *string {
|
func getLocal(v string) *string {
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNamespacesForPod(t *testing.T) {
|
|
||||||
for desc, test := range map[string]struct {
|
|
||||||
input *v1.Pod
|
|
||||||
expected *runtimeapi.NamespaceOption
|
|
||||||
}{
|
|
||||||
"nil pod -> default v1 namespaces": {
|
|
||||||
nil,
|
|
||||||
&runtimeapi.NamespaceOption{
|
|
||||||
Ipc: runtimeapi.NamespaceMode_POD,
|
|
||||||
Network: runtimeapi.NamespaceMode_POD,
|
|
||||||
Pid: runtimeapi.NamespaceMode_CONTAINER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"v1.Pod default namespaces": {
|
|
||||||
&v1.Pod{},
|
|
||||||
&runtimeapi.NamespaceOption{
|
|
||||||
Ipc: runtimeapi.NamespaceMode_POD,
|
|
||||||
Network: runtimeapi.NamespaceMode_POD,
|
|
||||||
Pid: runtimeapi.NamespaceMode_CONTAINER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"Host Namespaces": {
|
|
||||||
&v1.Pod{
|
|
||||||
Spec: v1.PodSpec{
|
|
||||||
HostIPC: true,
|
|
||||||
HostNetwork: true,
|
|
||||||
HostPID: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&runtimeapi.NamespaceOption{
|
|
||||||
Ipc: runtimeapi.NamespaceMode_NODE,
|
|
||||||
Network: runtimeapi.NamespaceMode_NODE,
|
|
||||||
Pid: runtimeapi.NamespaceMode_NODE,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"Shared Process Namespace (feature enabled)": {
|
|
||||||
&v1.Pod{
|
|
||||||
Spec: v1.PodSpec{
|
|
||||||
ShareProcessNamespace: &[]bool{true}[0],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&runtimeapi.NamespaceOption{
|
|
||||||
Ipc: runtimeapi.NamespaceMode_POD,
|
|
||||||
Network: runtimeapi.NamespaceMode_POD,
|
|
||||||
Pid: runtimeapi.NamespaceMode_POD,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"Shared Process Namespace, redundant flag (feature enabled)": {
|
|
||||||
&v1.Pod{
|
|
||||||
Spec: v1.PodSpec{
|
|
||||||
ShareProcessNamespace: &[]bool{false}[0],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&runtimeapi.NamespaceOption{
|
|
||||||
Ipc: runtimeapi.NamespaceMode_POD,
|
|
||||||
Network: runtimeapi.NamespaceMode_POD,
|
|
||||||
Pid: runtimeapi.NamespaceMode_CONTAINER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Logf("TestCase: %s", desc)
|
|
||||||
actual := namespacesForPod(test.input)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -47,6 +47,7 @@ import (
|
|||||||
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"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/images"
|
"k8s.io/kubernetes/pkg/kubelet/images"
|
||||||
|
runtimeutil "k8s.io/kubernetes/pkg/kubelet/kuberuntime/util"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/logs"
|
"k8s.io/kubernetes/pkg/kubelet/logs"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||||
@ -462,48 +463,6 @@ type podActions struct {
|
|||||||
EphemeralContainersToStart []int
|
EphemeralContainersToStart []int
|
||||||
}
|
}
|
||||||
|
|
||||||
// podSandboxChanged checks whether the spec of the pod is changed and returns
|
|
||||||
// (changed, new attempt, original sandboxID if exist).
|
|
||||||
func (m *kubeGenericRuntimeManager) podSandboxChanged(pod *v1.Pod, podStatus *kubecontainer.PodStatus) (bool, uint32, string) {
|
|
||||||
if len(podStatus.SandboxStatuses) == 0 {
|
|
||||||
klog.V(2).InfoS("No sandbox for pod can be found. Need to start a new one", "pod", klog.KObj(pod))
|
|
||||||
return true, 0, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
readySandboxCount := 0
|
|
||||||
for _, s := range podStatus.SandboxStatuses {
|
|
||||||
if s.State == runtimeapi.PodSandboxState_SANDBOX_READY {
|
|
||||||
readySandboxCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needs to create a new sandbox when readySandboxCount > 1 or the ready sandbox is not the latest one.
|
|
||||||
sandboxStatus := podStatus.SandboxStatuses[0]
|
|
||||||
if readySandboxCount > 1 {
|
|
||||||
klog.V(2).InfoS("Multiple sandboxes are ready for Pod. Need to reconcile them", "pod", klog.KObj(pod))
|
|
||||||
|
|
||||||
return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id
|
|
||||||
}
|
|
||||||
if sandboxStatus.State != runtimeapi.PodSandboxState_SANDBOX_READY {
|
|
||||||
klog.V(2).InfoS("No ready sandbox for pod can be found. Need to start a new one", "pod", klog.KObj(pod))
|
|
||||||
return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needs to create a new sandbox when network namespace changed.
|
|
||||||
if sandboxStatus.GetLinux().GetNamespaces().GetOptions().GetNetwork() != networkNamespaceForPod(pod) {
|
|
||||||
klog.V(2).InfoS("Sandbox for pod has changed. Need to start a new one", "pod", klog.KObj(pod))
|
|
||||||
return true, sandboxStatus.Metadata.Attempt + 1, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needs to create a new sandbox when the sandbox does not have an IP address.
|
|
||||||
if !kubecontainer.IsHostNetworkPod(pod) && sandboxStatus.Network != nil && sandboxStatus.Network.Ip == "" {
|
|
||||||
klog.V(2).InfoS("Sandbox for pod has no IP address. Need to start a new one", "pod", klog.KObj(pod))
|
|
||||||
return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, sandboxStatus.Metadata.Attempt, sandboxStatus.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerChanged(container *v1.Container, containerStatus *kubecontainer.Status) (uint64, uint64, bool) {
|
func containerChanged(container *v1.Container, containerStatus *kubecontainer.Status) (uint64, uint64, bool) {
|
||||||
expectedHash := kubecontainer.HashContainer(container)
|
expectedHash := kubecontainer.HashContainer(container)
|
||||||
return expectedHash, containerStatus.Hash, containerStatus.Hash != expectedHash
|
return expectedHash, containerStatus.Hash, containerStatus.Hash != expectedHash
|
||||||
@ -525,7 +484,7 @@ func containerSucceeded(c *v1.Container, podStatus *kubecontainer.PodStatus) boo
|
|||||||
func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *kubecontainer.PodStatus) podActions {
|
func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *kubecontainer.PodStatus) podActions {
|
||||||
klog.V(5).InfoS("Syncing Pod", "pod", klog.KObj(pod))
|
klog.V(5).InfoS("Syncing Pod", "pod", klog.KObj(pod))
|
||||||
|
|
||||||
createPodSandbox, attempt, sandboxID := m.podSandboxChanged(pod, podStatus)
|
createPodSandbox, attempt, sandboxID := runtimeutil.PodSandboxChanged(pod, podStatus)
|
||||||
changes := podActions{
|
changes := podActions{
|
||||||
KillPod: createPodSandbox,
|
KillPod: createPodSandbox,
|
||||||
CreateSandbox: createPodSandbox,
|
CreateSandbox: createPodSandbox,
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
runtimeutil "k8s.io/kubernetes/pkg/kubelet/kuberuntime/util"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util"
|
"k8s.io/kubernetes/pkg/kubelet/util"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||||
@ -194,7 +195,7 @@ func (m *kubeGenericRuntimeManager) generatePodSandboxLinuxConfig(pod *v1.Pod) (
|
|||||||
if sc.RunAsGroup != nil && runtime.GOOS != "windows" {
|
if sc.RunAsGroup != nil && runtime.GOOS != "windows" {
|
||||||
lc.SecurityContext.RunAsGroup = &runtimeapi.Int64Value{Value: int64(*sc.RunAsGroup)}
|
lc.SecurityContext.RunAsGroup = &runtimeapi.Int64Value{Value: int64(*sc.RunAsGroup)}
|
||||||
}
|
}
|
||||||
lc.SecurityContext.NamespaceOptions = namespacesForPod(pod)
|
lc.SecurityContext.NamespaceOptions = runtimeutil.NamespacesForPod(pod)
|
||||||
|
|
||||||
if sc.FSGroup != nil && runtime.GOOS != "windows" {
|
if sc.FSGroup != nil && runtime.GOOS != "windows" {
|
||||||
lc.SecurityContext.SupplementalGroups = append(lc.SecurityContext.SupplementalGroups, int64(*sc.FSGroup))
|
lc.SecurityContext.SupplementalGroups = append(lc.SecurityContext.SupplementalGroups, int64(*sc.FSGroup))
|
||||||
|
@ -19,6 +19,7 @@ package kuberuntime
|
|||||||
import (
|
import (
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
|
runtimeutil "k8s.io/kubernetes/pkg/kubelet/kuberuntime/util"
|
||||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||||
"k8s.io/kubernetes/pkg/securitycontext"
|
"k8s.io/kubernetes/pkg/securitycontext"
|
||||||
)
|
)
|
||||||
@ -52,7 +53,7 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Po
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set namespace options and supplemental groups.
|
// set namespace options and supplemental groups.
|
||||||
synthesized.NamespaceOptions = namespacesForPod(pod)
|
synthesized.NamespaceOptions = runtimeutil.NamespacesForPod(pod)
|
||||||
podSc := pod.Spec.SecurityContext
|
podSc := pod.Spec.SecurityContext
|
||||||
if podSc != nil {
|
if podSc != nil {
|
||||||
if podSc.FSGroup != nil {
|
if podSc.FSGroup != nil {
|
||||||
|
108
pkg/kubelet/kuberuntime/util/util.go
Normal file
108
pkg/kubelet/kuberuntime/util/util.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PodSandboxChanged checks whether the spec of the pod is changed and returns
|
||||||
|
// (changed, new attempt, original sandboxID if exist).
|
||||||
|
func PodSandboxChanged(pod *v1.Pod, podStatus *kubecontainer.PodStatus) (bool, uint32, string) {
|
||||||
|
if len(podStatus.SandboxStatuses) == 0 {
|
||||||
|
klog.V(2).InfoS("No sandbox for pod can be found. Need to start a new one", "pod", klog.KObj(pod))
|
||||||
|
return true, 0, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
readySandboxCount := 0
|
||||||
|
for _, s := range podStatus.SandboxStatuses {
|
||||||
|
if s.State == runtimeapi.PodSandboxState_SANDBOX_READY {
|
||||||
|
readySandboxCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs to create a new sandbox when readySandboxCount > 1 or the ready sandbox is not the latest one.
|
||||||
|
sandboxStatus := podStatus.SandboxStatuses[0]
|
||||||
|
if readySandboxCount > 1 {
|
||||||
|
klog.V(2).InfoS("Multiple sandboxes are ready for Pod. Need to reconcile them", "pod", klog.KObj(pod))
|
||||||
|
return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id
|
||||||
|
}
|
||||||
|
if sandboxStatus.State != runtimeapi.PodSandboxState_SANDBOX_READY {
|
||||||
|
klog.V(2).InfoS("No ready sandbox for pod can be found. Need to start a new one", "pod", klog.KObj(pod))
|
||||||
|
return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs to create a new sandbox when network namespace changed.
|
||||||
|
if sandboxStatus.GetLinux().GetNamespaces().GetOptions().GetNetwork() != NetworkNamespaceForPod(pod) {
|
||||||
|
klog.V(2).InfoS("Sandbox for pod has changed. Need to start a new one", "pod", klog.KObj(pod))
|
||||||
|
return true, sandboxStatus.Metadata.Attempt + 1, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs to create a new sandbox when the sandbox does not have an IP address.
|
||||||
|
if !kubecontainer.IsHostNetworkPod(pod) && sandboxStatus.Network != nil && sandboxStatus.Network.Ip == "" {
|
||||||
|
klog.V(2).InfoS("Sandbox for pod has no IP address. Need to start a new one", "pod", klog.KObj(pod))
|
||||||
|
return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, sandboxStatus.Metadata.Attempt, sandboxStatus.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// IpcNamespaceForPod returns the runtimeapi.NamespaceMode
|
||||||
|
// for the IPC namespace of a pod
|
||||||
|
func IpcNamespaceForPod(pod *v1.Pod) runtimeapi.NamespaceMode {
|
||||||
|
if pod != nil && pod.Spec.HostIPC {
|
||||||
|
return runtimeapi.NamespaceMode_NODE
|
||||||
|
}
|
||||||
|
return runtimeapi.NamespaceMode_POD
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkNamespaceForPod returns the runtimeapi.NamespaceMode
|
||||||
|
// for the network namespace of a pod
|
||||||
|
func NetworkNamespaceForPod(pod *v1.Pod) runtimeapi.NamespaceMode {
|
||||||
|
if pod != nil && pod.Spec.HostNetwork {
|
||||||
|
return runtimeapi.NamespaceMode_NODE
|
||||||
|
}
|
||||||
|
return runtimeapi.NamespaceMode_POD
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidNamespaceForPod returns the runtimeapi.NamespaceMode
|
||||||
|
// for the PID namespace of a pod
|
||||||
|
func PidNamespaceForPod(pod *v1.Pod) runtimeapi.NamespaceMode {
|
||||||
|
if pod != nil {
|
||||||
|
if pod.Spec.HostPID {
|
||||||
|
return runtimeapi.NamespaceMode_NODE
|
||||||
|
}
|
||||||
|
if pod.Spec.ShareProcessNamespace != nil && *pod.Spec.ShareProcessNamespace {
|
||||||
|
return runtimeapi.NamespaceMode_POD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Note that PID does not default to the zero value for v1.Pod
|
||||||
|
return runtimeapi.NamespaceMode_CONTAINER
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamespacesForPod returns the runtimeapi.NamespaceOption for a given pod.
|
||||||
|
// An empty or nil pod can be used to get the namespace defaults for v1.Pod.
|
||||||
|
func NamespacesForPod(pod *v1.Pod) *runtimeapi.NamespaceOption {
|
||||||
|
return &runtimeapi.NamespaceOption{
|
||||||
|
Ipc: IpcNamespaceForPod(pod),
|
||||||
|
Network: NetworkNamespaceForPod(pod),
|
||||||
|
Pid: PidNamespaceForPod(pod),
|
||||||
|
}
|
||||||
|
}
|
229
pkg/kubelet/kuberuntime/util/util_test.go
Normal file
229
pkg/kubelet/kuberuntime/util/util_test.go
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPodSandboxChanged(t *testing.T) {
|
||||||
|
for desc, test := range map[string]struct {
|
||||||
|
pod *v1.Pod
|
||||||
|
status *kubecontainer.PodStatus
|
||||||
|
expectedChanged bool
|
||||||
|
expectedAttempt uint32
|
||||||
|
expectedSandboxID string
|
||||||
|
}{
|
||||||
|
"Pod with no existing sandboxes": {
|
||||||
|
pod: &v1.Pod{},
|
||||||
|
status: &kubecontainer.PodStatus{},
|
||||||
|
expectedChanged: true,
|
||||||
|
expectedAttempt: 0,
|
||||||
|
expectedSandboxID: "",
|
||||||
|
},
|
||||||
|
"Pod with multiple ready sandbox statuses": {
|
||||||
|
pod: &v1.Pod{},
|
||||||
|
status: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Id: "sandboxID2",
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(1)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_READY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: "sandboxID1",
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_READY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedChanged: true,
|
||||||
|
expectedAttempt: 2,
|
||||||
|
expectedSandboxID: "sandboxID2",
|
||||||
|
},
|
||||||
|
"Pod with no ready sandbox statuses": {
|
||||||
|
pod: &v1.Pod{},
|
||||||
|
status: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Id: "sandboxID2",
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(1)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_NOTREADY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: "sandboxID1",
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_NOTREADY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedChanged: true,
|
||||||
|
expectedAttempt: 2,
|
||||||
|
expectedSandboxID: "sandboxID2",
|
||||||
|
},
|
||||||
|
"Pod with ready sandbox status but network namespace mismatch": {
|
||||||
|
pod: &v1.Pod{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Id: "sandboxID1",
|
||||||
|
Linux: &runtimeapi.LinuxPodSandboxStatus{
|
||||||
|
Namespaces: &runtimeapi.Namespace{
|
||||||
|
Options: &runtimeapi.NamespaceOption{
|
||||||
|
Network: runtimeapi.NamespaceMode_POD,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_READY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedChanged: true,
|
||||||
|
expectedAttempt: 1,
|
||||||
|
expectedSandboxID: "",
|
||||||
|
},
|
||||||
|
"Pod with ready sandbox status but no IP": {
|
||||||
|
pod: &v1.Pod{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
HostNetwork: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Id: "sandboxID1",
|
||||||
|
Network: &runtimeapi.PodSandboxNetworkStatus{
|
||||||
|
Ip: "",
|
||||||
|
},
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_READY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedChanged: true,
|
||||||
|
expectedAttempt: 1,
|
||||||
|
expectedSandboxID: "sandboxID1",
|
||||||
|
},
|
||||||
|
"Pod with ready sandbox status with IP": {
|
||||||
|
pod: &v1.Pod{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
HostNetwork: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Id: "sandboxID1",
|
||||||
|
Network: &runtimeapi.PodSandboxNetworkStatus{
|
||||||
|
Ip: "10.0.0.10",
|
||||||
|
},
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_READY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedChanged: false,
|
||||||
|
expectedAttempt: 0,
|
||||||
|
expectedSandboxID: "sandboxID1",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(desc, func(t *testing.T) {
|
||||||
|
changed, attempt, id := PodSandboxChanged(test.pod, test.status)
|
||||||
|
require.Equal(t, test.expectedChanged, changed)
|
||||||
|
require.Equal(t, test.expectedAttempt, attempt)
|
||||||
|
require.Equal(t, test.expectedSandboxID, id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespacesForPod(t *testing.T) {
|
||||||
|
for desc, test := range map[string]struct {
|
||||||
|
input *v1.Pod
|
||||||
|
expected *runtimeapi.NamespaceOption
|
||||||
|
}{
|
||||||
|
"nil pod -> default v1 namespaces": {
|
||||||
|
input: nil,
|
||||||
|
expected: &runtimeapi.NamespaceOption{
|
||||||
|
Ipc: runtimeapi.NamespaceMode_POD,
|
||||||
|
Network: runtimeapi.NamespaceMode_POD,
|
||||||
|
Pid: runtimeapi.NamespaceMode_CONTAINER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"v1.Pod default namespaces": {
|
||||||
|
input: &v1.Pod{},
|
||||||
|
expected: &runtimeapi.NamespaceOption{
|
||||||
|
Ipc: runtimeapi.NamespaceMode_POD,
|
||||||
|
Network: runtimeapi.NamespaceMode_POD,
|
||||||
|
Pid: runtimeapi.NamespaceMode_CONTAINER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Host Namespaces": {
|
||||||
|
input: &v1.Pod{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
HostIPC: true,
|
||||||
|
HostNetwork: true,
|
||||||
|
HostPID: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &runtimeapi.NamespaceOption{
|
||||||
|
Ipc: runtimeapi.NamespaceMode_NODE,
|
||||||
|
Network: runtimeapi.NamespaceMode_NODE,
|
||||||
|
Pid: runtimeapi.NamespaceMode_NODE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Shared Process Namespace (feature enabled)": {
|
||||||
|
input: &v1.Pod{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
ShareProcessNamespace: &[]bool{true}[0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &runtimeapi.NamespaceOption{
|
||||||
|
Ipc: runtimeapi.NamespaceMode_POD,
|
||||||
|
Network: runtimeapi.NamespaceMode_POD,
|
||||||
|
Pid: runtimeapi.NamespaceMode_POD,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Shared Process Namespace, redundant flag (feature enabled)": {
|
||||||
|
input: &v1.Pod{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
ShareProcessNamespace: &[]bool{false}[0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &runtimeapi.NamespaceOption{
|
||||||
|
Ipc: runtimeapi.NamespaceMode_POD,
|
||||||
|
Network: runtimeapi.NamespaceMode_POD,
|
||||||
|
Pid: runtimeapi.NamespaceMode_CONTAINER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(desc, func(t *testing.T) {
|
||||||
|
actual := NamespacesForPod(test.input)
|
||||||
|
require.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,9 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
runtimeutil "k8s.io/kubernetes/pkg/kubelet/kuberuntime/util"
|
||||||
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -195,6 +198,24 @@ func GeneratePodInitializedCondition(spec *v1.PodSpec, containerStatuses []v1.Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GeneratePodHasNetworkCondition(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodCondition {
|
||||||
|
newSandboxNeeded, _, _ := runtimeutil.PodSandboxChanged(pod, podStatus)
|
||||||
|
// if a new sandbox does not need to be created for a pod, it indicates that
|
||||||
|
// a sandbox for the pod with networking configured already exists.
|
||||||
|
// Otherwise, the kubelet needs to invoke the container runtime to create a
|
||||||
|
// fresh sandbox and configure networking for the sandbox.
|
||||||
|
if !newSandboxNeeded {
|
||||||
|
return v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v1.PodCondition{
|
||||||
|
Type: kubetypes.PodHasNetwork,
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func generateContainersReadyConditionForTerminalPhase(podPhase v1.PodPhase) v1.PodCondition {
|
func generateContainersReadyConditionForTerminalPhase(podPhase v1.PodPhase) v1.PodCondition {
|
||||||
condition := v1.PodCondition{
|
condition := v1.PodCondition{
|
||||||
Type: v1.ContainersReady,
|
Type: v1.ContainersReady,
|
||||||
|
@ -21,7 +21,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateContainersReadyCondition(t *testing.T) {
|
func TestGenerateContainersReadyCondition(t *testing.T) {
|
||||||
@ -417,6 +422,77 @@ func TestGeneratePodInitializedCondition(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGeneratePodHasNetworkCondition(t *testing.T) {
|
||||||
|
for desc, test := range map[string]struct {
|
||||||
|
pod *v1.Pod
|
||||||
|
status *kubecontainer.PodStatus
|
||||||
|
expected v1.PodCondition
|
||||||
|
}{
|
||||||
|
"Empty pod status": {
|
||||||
|
pod: &v1.Pod{},
|
||||||
|
status: &kubecontainer.PodStatus{},
|
||||||
|
expected: v1.PodCondition{
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Pod sandbox status not ready": {
|
||||||
|
pod: &v1.Pod{},
|
||||||
|
status: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_NOTREADY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: v1.PodCondition{
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Pod sandbox status ready but no IP configured": {
|
||||||
|
pod: &v1.Pod{},
|
||||||
|
status: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Network: &runtimeapi.PodSandboxNetworkStatus{
|
||||||
|
Ip: "",
|
||||||
|
},
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_READY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: v1.PodCondition{
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Pod sandbox status ready and IP configured": {
|
||||||
|
pod: &v1.Pod{},
|
||||||
|
status: &kubecontainer.PodStatus{
|
||||||
|
SandboxStatuses: []*runtimeapi.PodSandboxStatus{
|
||||||
|
{
|
||||||
|
Network: &runtimeapi.PodSandboxNetworkStatus{
|
||||||
|
Ip: "10.0.0.10",
|
||||||
|
},
|
||||||
|
Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)},
|
||||||
|
State: runtimeapi.PodSandboxState_SANDBOX_READY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: v1.PodCondition{
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(desc, func(t *testing.T) {
|
||||||
|
test.expected.Type = kubetypes.PodHasNetwork
|
||||||
|
condition := GeneratePodHasNetworkCondition(test.pod, test.status)
|
||||||
|
require.Equal(t, test.expected.Type, condition.Type)
|
||||||
|
require.Equal(t, test.expected.Status, condition.Status)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getPodCondition(conditionType v1.PodConditionType, status v1.ConditionStatus, reason, message string) v1.PodCondition {
|
func getPodCondition(conditionType v1.PodConditionType, status v1.ConditionStatus, reason, message string) v1.PodCondition {
|
||||||
return v1.PodCondition{
|
return v1.PodCondition{
|
||||||
Type: conditionType,
|
Type: conditionType,
|
||||||
|
@ -483,6 +483,9 @@ func (m *manager) updateStatusInternal(pod *v1.Pod, status v1.PodStatus, forceUp
|
|||||||
// Set InitializedCondition.LastTransitionTime.
|
// Set InitializedCondition.LastTransitionTime.
|
||||||
updateLastTransitionTime(&status, &oldStatus, v1.PodInitialized)
|
updateLastTransitionTime(&status, &oldStatus, v1.PodInitialized)
|
||||||
|
|
||||||
|
// Set PodHasNetwork.LastTransitionTime.
|
||||||
|
updateLastTransitionTime(&status, &oldStatus, kubetypes.PodHasNetwork)
|
||||||
|
|
||||||
// Set PodScheduledCondition.LastTransitionTime.
|
// Set PodScheduledCondition.LastTransitionTime.
|
||||||
updateLastTransitionTime(&status, &oldStatus, v1.PodScheduled)
|
updateLastTransitionTime(&status, &oldStatus, v1.PodScheduled)
|
||||||
|
|
||||||
|
@ -43,3 +43,13 @@ const (
|
|||||||
LimitedSwap = "LimitedSwap"
|
LimitedSwap = "LimitedSwap"
|
||||||
UnlimitedSwap = "UnlimitedSwap"
|
UnlimitedSwap = "UnlimitedSwap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Alpha conditions managed by Kubelet that are not yet part of the API. The
|
||||||
|
// entries here should be moved to staging/src/k8s.io.api/core/v1/types.go
|
||||||
|
// once the feature managing the condition graduates to Beta.
|
||||||
|
const (
|
||||||
|
// PodHasNetwork indicates networking has been configured successfully for the
|
||||||
|
// pod and IP address(es) assigned. Images for containers specified in the pod
|
||||||
|
// spec can be pulled and containers launched after this condition is true.
|
||||||
|
PodHasNetwork = "PodHasNetwork"
|
||||||
|
)
|
||||||
|
@ -18,6 +18,8 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PodConditionsByKubelet is the list of pod conditions owned by kubelet
|
// PodConditionsByKubelet is the list of pod conditions owned by kubelet
|
||||||
@ -35,5 +37,10 @@ func PodConditionByKubelet(conditionType v1.PodConditionType) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.PodHasNetworkCondition) {
|
||||||
|
if conditionType == PodHasNetwork {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,19 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPodConditionByKubelet(t *testing.T) {
|
func TestPodConditionByKubelet(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodHasNetworkCondition, true)()
|
||||||
trueCases := []v1.PodConditionType{
|
trueCases := []v1.PodConditionType{
|
||||||
v1.PodScheduled,
|
v1.PodScheduled,
|
||||||
v1.PodReady,
|
v1.PodReady,
|
||||||
v1.PodInitialized,
|
v1.PodInitialized,
|
||||||
v1.ContainersReady,
|
v1.ContainersReady,
|
||||||
|
PodHasNetwork,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range trueCases {
|
for _, tc := range trueCases {
|
||||||
|
Loading…
Reference in New Issue
Block a user