-
Notifications
You must be signed in to change notification settings - Fork 1
feat: update pod status logic #214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
41b4abc
78260b6
fc27ef5
691528d
e82d2bb
b157415
9cf5e02
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,38 @@ const ( | |
OwnerKindStatefulSet = "StatefulSet" | ||
) | ||
|
||
// Helper Fns | ||
func hasPodReadyCondition(conditions []v1.PodCondition) bool { | ||
for _, condition := range conditions { | ||
if condition.Type == v1.PodReady && condition.Status == v1.ConditionTrue { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func isPodInitializedConditionTrue(status *v1.PodStatus) bool { | ||
for _, condition := range status.Conditions { | ||
if condition.Type != v1.PodInitialized { | ||
continue | ||
} | ||
|
||
return condition.Status == v1.ConditionTrue | ||
} | ||
return false | ||
} | ||
|
||
func isRestartableInitContainer(initContainer *v1.Container) bool { | ||
if initContainer == nil || initContainer.RestartPolicy == nil { | ||
return false | ||
} | ||
return *initContainer.RestartPolicy == v1.ContainerRestartPolicyAlways | ||
} | ||
|
||
func isPodPhaseTerminal(phase v1.PodPhase) bool { | ||
return phase == v1.PodFailed || phase == v1.PodSucceeded | ||
} | ||
|
||
// ---------------------------------- Pod "status" ---------------------------------- | ||
|
||
type PodStatusAction struct{} | ||
|
@@ -43,18 +75,46 @@ func NewPodStatusAction() PodStatusAction { | |
|
||
// Generates the Pod "status" facet. | ||
func (PodStatusAction) ComputeAttributes(pod v1.Pod) (attributes, error) { | ||
// based on https://github.com/kubernetes/kubernetes/blob/0d3b859af81e6a5f869a7766c8d45afd1c600b04/pkg/printers/internalversion/printers.go#L901 | ||
reason := string(pod.Status.Phase) | ||
// based on https://github.com/kubernetes/kubernetes/blob/cd3b5c57668a0a6e32057ef82dfab40e9b0bec5b/pkg/printers/internalversion/printers.go#L881 | ||
restarts := 0 | ||
restartableInitContainerRestarts := 0 | ||
totalContainers := len(pod.Spec.Containers) | ||
readyContainers := 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It still looks like these four variables are unused. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so here's the logic in datadog agent https://github.com/DataDog/datadog-agent/blob/7d47d25899c025c8b3e5ba91e488fa0f97f7a275/pkg/collector/corechecks/cluster/orchestrator/transformers/k8s/pod.go#L220 they also only use status but kept the rest of the logic so that they can just copy paste it over There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. think the argument for taking it out is that its unnecessary computation / more efficient which i agree with but its a slight preference vs. way better imo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. I'm happy with where you landed! If we need to change it again, I think it could be more clear to have a helper that returns all values and is very explicitly full copy/paste. Then we could call that and extra only what we want |
||
|
||
podPhase := pod.Status.Phase | ||
reason := string(podPhase) | ||
if pod.Status.Reason != "" { | ||
reason = pod.Status.Reason | ||
} | ||
|
||
// If the Pod carries {type:PodScheduled, reason:SchedulingGated}, set reason to 'SchedulingGated'. | ||
for _, condition := range pod.Status.Conditions { | ||
if condition.Type == v1.PodScheduled && condition.Reason == v1.PodReasonSchedulingGated { | ||
reason = v1.PodReasonSchedulingGated | ||
} | ||
} | ||
|
||
initContainers := make(map[string]*v1.Container) | ||
for i := range pod.Spec.InitContainers { | ||
initContainers[pod.Spec.InitContainers[i].Name] = &pod.Spec.InitContainers[i] | ||
if isRestartableInitContainer(&pod.Spec.InitContainers[i]) { | ||
totalContainers++ | ||
} | ||
} | ||
|
||
initializing := false | ||
for i := range pod.Status.InitContainerStatuses { | ||
container := pod.Status.InitContainerStatuses[i] | ||
restarts += int(container.RestartCount) | ||
switch { | ||
case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0: | ||
continue | ||
case isRestartableInitContainer(initContainers[container.Name]) && | ||
container.Started != nil && *container.Started: | ||
if container.Ready { | ||
readyContainers++ | ||
} | ||
continue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be in the |
||
case container.State.Terminated != nil: | ||
// initialization is failed | ||
if len(container.State.Terminated.Reason) == 0 { | ||
|
@@ -76,11 +136,13 @@ func (PodStatusAction) ComputeAttributes(pod v1.Pod) (attributes, error) { | |
} | ||
break | ||
} | ||
if !initializing { | ||
|
||
if !initializing || isPodInitializedConditionTrue(&pod.Status) { | ||
restarts = restartableInitContainerRestarts | ||
hasRunning := false | ||
for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- { | ||
container := pod.Status.ContainerStatuses[i] | ||
|
||
restarts += int(container.RestartCount) | ||
if container.State.Waiting != nil && container.State.Waiting.Reason != "" { | ||
reason = container.State.Waiting.Reason | ||
} else if container.State.Terminated != nil && container.State.Terminated.Reason != "" { | ||
|
@@ -93,12 +155,17 @@ func (PodStatusAction) ComputeAttributes(pod v1.Pod) (attributes, error) { | |
} | ||
} else if container.Ready && container.State.Running != nil { | ||
hasRunning = true | ||
readyContainers++ | ||
} | ||
} | ||
|
||
// change pod status back to "Running" if there is at least one container still reporting as "Running" status | ||
if reason == "Completed" && hasRunning { | ||
reason = "Running" | ||
if hasPodReadyCondition(pod.Status.Conditions) { | ||
reason = "Running" | ||
} else { | ||
reason = "NotReady" | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,133 +12,133 @@ func TestPodActions(t *testing.T) { | |
testBodyFilepath: "./testdata/podObjectEvent.json", | ||
}, | ||
), | ||
expectedResults: []queryWithResult{ | ||
{ | ||
path: "observe_transform.facets.status", | ||
expResult: "Terminating", | ||
}, | ||
}, | ||
}, | ||
{ // Tests that we don't override/drop other facets computed in OTTL | ||
name: "existingObserveTransformAttributes", | ||
inLogs: createResourceLogs( | ||
logWithResource{ | ||
testBodyFilepath: "./testdata/podObjectEvent.json", | ||
recordAttributes: map[string]any{ | ||
"observe_transform": map[string]interface{}{ | ||
"facets": map[string]interface{}{ | ||
"other_key": "test", | ||
}, | ||
}, | ||
"name": "existingObserveTransformAttributes", | ||
}, | ||
}, | ||
), | ||
expectedResults: []queryWithResult{ | ||
{ | ||
path: "observe_transform.facets.status", | ||
expResult: "Terminating", | ||
}, | ||
{ | ||
path: "observe_transform.facets.other_key", | ||
expResult: "test", | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Pod container counts", | ||
inLogs: createResourceLogs( | ||
logWithResource{ | ||
testBodyFilepath: "./testdata/podObjectEvent.json", | ||
}, | ||
), | ||
expectedResults: []queryWithResult{ | ||
{ | ||
path: "observe_transform.facets.restarts", | ||
expResult: int64(5), | ||
}, | ||
{ | ||
path: "observe_transform.facets.total_containers", | ||
expResult: int64(4), | ||
}, | ||
{ | ||
path: "observe_transform.facets.ready_containers", | ||
expResult: int64(3), | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Pod readiness gates", | ||
inLogs: createResourceLogs( | ||
logWithResource{ | ||
testBodyFilepath: "./testdata/podObjectEventWithReadinessGates.json", | ||
}, | ||
), | ||
expectedResults: []queryWithResult{ | ||
{ | ||
path: "observe_transform.facets.readinessGatesReady", | ||
expResult: int64(1), | ||
}, | ||
{ | ||
path: "observe_transform.facets.readinessGatesTotal", | ||
expResult: int64(2), | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Pod conditions", | ||
inLogs: createResourceLogs( | ||
logWithResource{ | ||
testBodyFilepath: "./testdata/podObjectEvent.json", | ||
}, | ||
), | ||
expectedResults: []queryWithResult{ | ||
// Conditions must be a map with 5 elements | ||
{ | ||
path: "observe_transform.facets.conditions | length(@)", | ||
expResult: float64(6), | ||
}, | ||
{ | ||
path: "observe_transform.facets.conditions.PodReadyToStartContainers", | ||
expResult: "False", | ||
}, | ||
{ | ||
path: "observe_transform.facets.conditions.Initialized", | ||
expResult: "True", | ||
}, | ||
{ | ||
path: "observe_transform.facets.conditions.Ready", | ||
expResult: "False", | ||
}, | ||
{ | ||
path: "observe_transform.facets.conditions.ContainersReady", | ||
expResult: "False", | ||
}, | ||
{ | ||
path: "observe_transform.facets.conditions.PodScheduled", | ||
expResult: "True", | ||
}, | ||
{ | ||
path: "observe_transform.facets.conditions.TestCondition", | ||
expResult: "Unknown", | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Pod conditions", | ||
inLogs: createResourceLogs( | ||
logWithResource{ | ||
testBodyFilepath: "./testdata/podTestEvent.json", | ||
}, | ||
), | ||
expectedResults: []queryWithResult{ | ||
// Conditions must be a map with 5 elements | ||
{ | ||
path: "observe_transform.facets.conditions | length(@)", | ||
expResult: float64(5), | ||
}, | ||
}, | ||
}, | ||
// { // Tests that we don't override/drop other facets computed in OTTL | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you replace these deleted tests with new ones? |
||
// name: "existingObserveTransformAttributes", | ||
// inLogs: createResourceLogs( | ||
// logWithResource{ | ||
// testBodyFilepath: "./testdata/podObjectEvent.json", | ||
// recordAttributes: map[string]any{ | ||
// "observe_transform": map[string]interface{}{ | ||
// "facets": map[string]interface{}{ | ||
// "other_key": "test", | ||
// }, | ||
// }, | ||
// "name": "existingObserveTransformAttributes", | ||
// }, | ||
// }, | ||
// ), | ||
// expectedResults: []queryWithResult{ | ||
// { | ||
// path: "observe_transform.facets.status", | ||
// expResult: "Terminating", | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.other_key", | ||
// expResult: "test", | ||
// }, | ||
// }, | ||
// }, | ||
// { | ||
// name: "Pod container counts", | ||
// inLogs: createResourceLogs( | ||
// logWithResource{ | ||
// testBodyFilepath: "./testdata/podObjectEvent.json", | ||
// }, | ||
// ), | ||
// expectedResults: []queryWithResult{ | ||
// { | ||
// path: "observe_transform.facets.restarts", | ||
// expResult: int64(5), | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.total_containers", | ||
// expResult: int64(4), | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.ready_containers", | ||
// expResult: int64(3), | ||
// }, | ||
// }, | ||
// }, | ||
// { | ||
// name: "Pod readiness gates", | ||
// inLogs: createResourceLogs( | ||
// logWithResource{ | ||
// testBodyFilepath: "./testdata/podObjectEventWithReadinessGates.json", | ||
// }, | ||
// ), | ||
// expectedResults: []queryWithResult{ | ||
// { | ||
// path: "observe_transform.facets.readinessGatesReady", | ||
// expResult: int64(1), | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.readinessGatesTotal", | ||
// expResult: int64(2), | ||
// }, | ||
// }, | ||
// }, | ||
// { | ||
// name: "Pod conditions", | ||
// inLogs: createResourceLogs( | ||
// logWithResource{ | ||
// testBodyFilepath: "./testdata/podObjectEvent.json", | ||
// }, | ||
// ), | ||
// expectedResults: []queryWithResult{ | ||
// // Conditions must be a map with 5 elements | ||
// { | ||
// path: "observe_transform.facets.conditions | length(@)", | ||
// expResult: float64(6), | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.conditions.PodReadyToStartContainers", | ||
// expResult: "False", | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.conditions.Initialized", | ||
// expResult: "True", | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.conditions.Ready", | ||
// expResult: "False", | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.conditions.ContainersReady", | ||
// expResult: "False", | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.conditions.PodScheduled", | ||
// expResult: "True", | ||
// }, | ||
// { | ||
// path: "observe_transform.facets.conditions.TestCondition", | ||
// expResult: "Unknown", | ||
// }, | ||
// }, | ||
// }, | ||
// { | ||
// name: "Pod conditions", | ||
// inLogs: createResourceLogs( | ||
// logWithResource{ | ||
// testBodyFilepath: "./testdata/podTestEvent.json", | ||
// }, | ||
// ), | ||
// expectedResults: []queryWithResult{ | ||
// // Conditions must be a map with 5 elements | ||
// { | ||
// path: "observe_transform.facets.conditions | length(@)", | ||
// expResult: float64(5), | ||
// }, | ||
// }, | ||
// }, | ||
} { | ||
runTest(t, test) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe change this to
to match
hasPodReadyCondition
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bumping this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is actually just helper code I copied from the same kubectl code, if its semantically the same i'd prefer to leave it in case that helper code changes in the future as well it'll be easier to copy/compare