mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
Add NominatedNodeName to PodStatus
This commit is contained in:
parent
235714e7f3
commit
384a86caa9
@ -2724,6 +2724,13 @@ type PodStatus struct {
|
|||||||
// A brief CamelCase message indicating details about why the pod is in this state. e.g. 'Evicted'
|
// A brief CamelCase message indicating details about why the pod is in this state. e.g. 'Evicted'
|
||||||
// +optional
|
// +optional
|
||||||
Reason string
|
Reason string
|
||||||
|
// nominatedNodeName is set when this pod preempts other pods on the node, but it cannot be
|
||||||
|
// scheduled right away as preemption victims receive their graceful termination periods.
|
||||||
|
// This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide
|
||||||
|
// to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to
|
||||||
|
// give the resources on this node to a higher priority pod that is created after preemption.
|
||||||
|
// +optional
|
||||||
|
NominatedNodeName string
|
||||||
|
|
||||||
// +optional
|
// +optional
|
||||||
HostIP string
|
HostIP string
|
||||||
|
@ -163,7 +163,8 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
|
|||||||
"spec.restartPolicy",
|
"spec.restartPolicy",
|
||||||
"spec.schedulerName",
|
"spec.schedulerName",
|
||||||
"status.phase",
|
"status.phase",
|
||||||
"status.podIP":
|
"status.podIP",
|
||||||
|
"status.nominatedNodeName":
|
||||||
return label, value, nil
|
return label, value, nil
|
||||||
// This is for backwards compatibility with old v1 clients which send spec.host
|
// This is for backwards compatibility with old v1 clients which send spec.host
|
||||||
case "spec.host":
|
case "spec.host":
|
||||||
|
@ -3374,6 +3374,12 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod) field.ErrorList {
|
|||||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("nodeName"), "may not be changed directly"))
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("nodeName"), "may not be changed directly"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newPod.Status.NominatedNodeName != oldPod.Status.NominatedNodeName && len(newPod.Status.NominatedNodeName) > 0 {
|
||||||
|
for _, msg := range ValidateNodeName(newPod.Status.NominatedNodeName, false) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("nominatedNodeName"), newPod.Status.NominatedNodeName, msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If pod should not restart, make sure the status update does not transition
|
// If pod should not restart, make sure the status update does not transition
|
||||||
// any terminated containers to a non-terminated state.
|
// any terminated containers to a non-terminated state.
|
||||||
allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec.RestartPolicy)...)
|
allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec.RestartPolicy)...)
|
||||||
|
@ -7756,6 +7756,129 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidatePodStatusUpdate(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
new core.Pod
|
||||||
|
old core.Pod
|
||||||
|
err string
|
||||||
|
test string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
NodeName: "node1",
|
||||||
|
},
|
||||||
|
Status: core.PodStatus{
|
||||||
|
NominatedNodeName: "node1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
NodeName: "node1",
|
||||||
|
},
|
||||||
|
Status: core.PodStatus{},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
"removed nominatedNodeName",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
NodeName: "node1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
NodeName: "node1",
|
||||||
|
},
|
||||||
|
Status: core.PodStatus{
|
||||||
|
NominatedNodeName: "node1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
"add valid nominatedNodeName",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
NodeName: "node1",
|
||||||
|
},
|
||||||
|
Status: core.PodStatus{
|
||||||
|
NominatedNodeName: "Node1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
NodeName: "node1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"nominatedNodeName",
|
||||||
|
"Add invalid nominatedNodeName",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
NodeName: "node1",
|
||||||
|
},
|
||||||
|
Status: core.PodStatus{
|
||||||
|
NominatedNodeName: "node1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
core.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
NodeName: "node1",
|
||||||
|
},
|
||||||
|
Status: core.PodStatus{
|
||||||
|
NominatedNodeName: "node2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
"Update nominatedNodeName",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test.new.ObjectMeta.ResourceVersion = "1"
|
||||||
|
test.old.ObjectMeta.ResourceVersion = "1"
|
||||||
|
errs := ValidatePodStatusUpdate(&test.new, &test.old)
|
||||||
|
if test.err == "" {
|
||||||
|
if len(errs) != 0 {
|
||||||
|
t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(errs) == 0 {
|
||||||
|
t.Errorf("unexpected valid: %s\nA: %+v\nB: %+v", test.test, test.new, test.old)
|
||||||
|
} else if actualErr := errs.ToAggregate().Error(); !strings.Contains(actualErr, test.err) {
|
||||||
|
t.Errorf("unexpected error message: %s\nExpected error: %s\nActual error: %s", test.test, test.err, actualErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func makeValidService() core.Service {
|
func makeValidService() core.Service {
|
||||||
return core.Service{
|
return core.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
@ -651,6 +651,9 @@ func describePod(pod *api.Pod, events *api.EventList) (string, error) {
|
|||||||
if controlledBy := printController(pod); len(controlledBy) > 0 {
|
if controlledBy := printController(pod); len(controlledBy) > 0 {
|
||||||
w.Write(LEVEL_0, "Controlled By:\t%s\n", controlledBy)
|
w.Write(LEVEL_0, "Controlled By:\t%s\n", controlledBy)
|
||||||
}
|
}
|
||||||
|
if len(pod.Status.NominatedNodeName) > 0 {
|
||||||
|
w.Write(LEVEL_0, "NominatedNodeName:\t%s\n", pod.Status.NominatedNodeName)
|
||||||
|
}
|
||||||
|
|
||||||
if len(pod.Spec.InitContainers) > 0 {
|
if len(pod.Spec.InitContainers) > 0 {
|
||||||
describeContainers("Init Containers", pod.Spec.InitContainers, pod.Status.InitContainerStatuses, EnvValueRetriever(pod), w, "")
|
describeContainers("Init Containers", pod.Spec.InitContainers, pod.Status.InitContainerStatuses, EnvValueRetriever(pod), w, "")
|
||||||
|
@ -81,6 +81,7 @@ func TestDescribePodNode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Status: api.PodStatus{
|
Status: api.PodStatus{
|
||||||
HostIP: "127.0.0.1",
|
HostIP: "127.0.0.1",
|
||||||
|
NominatedNodeName: "nodeA",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
|
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
|
||||||
@ -92,6 +93,9 @@ func TestDescribePodNode(t *testing.T) {
|
|||||||
if !strings.Contains(out, "all-in-one/127.0.0.1") {
|
if !strings.Contains(out, "all-in-one/127.0.0.1") {
|
||||||
t.Errorf("unexpected out: %s", out)
|
t.Errorf("unexpected out: %s", out)
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(out, "nodeA") {
|
||||||
|
t.Errorf("unexpected out: %s", out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDescribePodTolerations(t *testing.T) {
|
func TestDescribePodTolerations(t *testing.T) {
|
||||||
|
@ -609,6 +609,9 @@ func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1alpha1.Table
|
|||||||
nodeName = "<none>"
|
nodeName = "<none>"
|
||||||
}
|
}
|
||||||
row.Cells = append(row.Cells, podIP, nodeName)
|
row.Cells = append(row.Cells, podIP, nodeName)
|
||||||
|
if len(pod.Status.NominatedNodeName) > 0 {
|
||||||
|
row.Cells = append(row.Cells, pod.Status.NominatedNodeName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return []metav1alpha1.TableRow{row}, nil
|
return []metav1alpha1.TableRow{row}, nil
|
||||||
|
@ -1704,9 +1704,10 @@ func TestPrintPodwide(t *testing.T) {
|
|||||||
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||||
{RestartCount: 3},
|
{RestartCount: 3},
|
||||||
},
|
},
|
||||||
|
NominatedNodeName: "node1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]metav1alpha1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", 6, "<unknown>", "1.1.1.1", "test1"}}},
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", 6, "<unknown>", "1.1.1.1", "test1", "node1"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test when the NodeName and PodIP are none
|
// Test when the NodeName and PodIP are none
|
||||||
|
@ -243,6 +243,7 @@ func PodToSelectableFields(pod *api.Pod) fields.Set {
|
|||||||
podSpecificFieldsSet["spec.schedulerName"] = string(pod.Spec.SchedulerName)
|
podSpecificFieldsSet["spec.schedulerName"] = string(pod.Spec.SchedulerName)
|
||||||
podSpecificFieldsSet["status.phase"] = string(pod.Status.Phase)
|
podSpecificFieldsSet["status.phase"] = string(pod.Status.Phase)
|
||||||
podSpecificFieldsSet["status.podIP"] = string(pod.Status.PodIP)
|
podSpecificFieldsSet["status.podIP"] = string(pod.Status.PodIP)
|
||||||
|
podSpecificFieldsSet["status.nominatedNodeName"] = string(pod.Status.NominatedNodeName)
|
||||||
return generic.AddObjectMetaFieldsSet(podSpecificFieldsSet, &pod.ObjectMeta, true)
|
return generic.AddObjectMetaFieldsSet(podSpecificFieldsSet, &pod.ObjectMeta, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,20 @@ func TestMatchPod(t *testing.T) {
|
|||||||
fieldSelector: fields.ParseSelectorOrDie("status.podIP=4.3.2.1"),
|
fieldSelector: fields.ParseSelectorOrDie("status.podIP=4.3.2.1"),
|
||||||
expectMatch: false,
|
expectMatch: false,
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
|
in: &api.Pod{
|
||||||
|
Status: api.PodStatus{NominatedNodeName: "node1"},
|
||||||
|
},
|
||||||
|
fieldSelector: fields.ParseSelectorOrDie("status.nominatedNodeName=node1"),
|
||||||
|
expectMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: &api.Pod{
|
||||||
|
Status: api.PodStatus{NominatedNodeName: "node1"},
|
||||||
|
},
|
||||||
|
fieldSelector: fields.ParseSelectorOrDie("status.nominatedNodeName=node2"),
|
||||||
|
expectMatch: false,
|
||||||
|
}}
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
m := MatchPod(labels.Everything(), testCase.fieldSelector)
|
m := MatchPod(labels.Everything(), testCase.fieldSelector)
|
||||||
result, err := m.Matches(testCase.in)
|
result, err := m.Matches(testCase.in)
|
||||||
|
@ -3021,6 +3021,15 @@ type PodStatus struct {
|
|||||||
// e.g. 'Evicted'
|
// e.g. 'Evicted'
|
||||||
// +optional
|
// +optional
|
||||||
Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"`
|
Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"`
|
||||||
|
// nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be
|
||||||
|
// scheduled right away as preemption victims receive their graceful termination periods.
|
||||||
|
// This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide
|
||||||
|
// to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to
|
||||||
|
// give the resources on this node to a higher priority pod that is created after preemption.
|
||||||
|
// As a result, this field may be different than PodSpec.nodeName when the pod is
|
||||||
|
// scheduled.
|
||||||
|
// +optional
|
||||||
|
NominatedNodeName string `json:"nominatedNodeName,omitempty" protobuf:"bytes,11,opt,name=nominatedNodeName"`
|
||||||
|
|
||||||
// IP address of the host to which the pod is assigned. Empty if not yet scheduled.
|
// IP address of the host to which the pod is assigned. Empty if not yet scheduled.
|
||||||
// +optional
|
// +optional
|
||||||
|
Loading…
Reference in New Issue
Block a user