Include sidecar containers requests and limits (#2848)

* include sidecar container metrics

* apply code review suggestions
mine
Eric Bissonnette 2024-10-13 10:06:52 -04:00 committed by GitHub
parent 6fc151bda2
commit 8a16b57c09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 432 additions and 19 deletions

View File

@ -132,7 +132,7 @@ func (p Pod) Render(o interface{}, ns string, row *model1.Row) error {
if pwm.MX != nil {
ccmx = pwm.MX.Containers
}
c, r := gatherCoMX(po.Spec.Containers, ccmx)
c, r := gatherCoMX(&po.Spec, ccmx)
phase := p.Phase(&po)
row.ID = client.MetaFQN(po.ObjectMeta)
@ -223,7 +223,11 @@ func (p *PodWithMetrics) DeepCopyObject() runtime.Object {
return p
}
func gatherCoMX(cc []v1.Container, ccmx []mv1beta1.ContainerMetrics) (c, r metric) {
func gatherCoMX(spec *v1.PodSpec, ccmx []mv1beta1.ContainerMetrics) (c, r metric) {
cc := make([]v1.Container, 0, len(spec.InitContainers)+len(spec.Containers))
cc = append(cc, filterRestartableInitCO(spec.InitContainers)...)
cc = append(cc, spec.Containers...)
rcpu, rmem := cosRequests(cc)
r.cpu, r.mem = rcpu.MilliValue(), rmem.Value()
@ -490,3 +494,13 @@ func hasPodReadyCondition(conditions []v1.PodCondition) bool {
func restartableInitCO(p *v1.ContainerRestartPolicy) bool {
return p != nil && *p == v1.ContainerRestartPolicyAlways
}
func filterRestartableInitCO(cc []v1.Container) []v1.Container {
rcc := make([]v1.Container, 0, len(cc))
for _, c := range cc {
if c.RestartPolicy != nil && *c.RestartPolicy == v1.ContainerRestartPolicyAlways {
rcc = append(rcc, c)
}
}
return rcc
}

View File

@ -295,16 +295,78 @@ func Test_restartableInitCO(t *testing.T) {
}
}
func Test_filterRestartableInitCO(t *testing.T) {
always := v1.ContainerRestartPolicyAlways
uu := map[string]struct {
cc []v1.Container
ecc []v1.Container
}{
"empty": {
cc: []v1.Container{},
ecc: []v1.Container{},
},
"restartable": {
cc: []v1.Container{
{
Name: "c1",
RestartPolicy: &always,
},
},
ecc: []v1.Container{
{
Name: "c1",
RestartPolicy: &always,
},
},
},
"not-restartable": {
cc: []v1.Container{
{
Name: "c1",
},
},
ecc: []v1.Container{},
},
"mixed": {
cc: []v1.Container{
{
Name: "c1",
},
{
Name: "c2",
RestartPolicy: &always,
},
},
ecc: []v1.Container{
{
Name: "c2",
RestartPolicy: &always,
},
},
},
}
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
assert.Equal(t, u.ecc, filterRestartableInitCO(u.cc))
})
}
}
func Test_gatherPodMx(t *testing.T) {
uu := map[string]struct {
cc []v1.Container
spec *v1.PodSpec
mx []mv1beta1.ContainerMetrics
c, r metric
perc string
}{
"single": {
cc: []v1.Container{
makeContainer("c1", false, "10m", "1Mi", "20m", "2Mi"),
spec: &v1.PodSpec{
Containers: []v1.Container{
makeContainer("c1", false, "10m", "1Mi", "20m", "2Mi"),
},
},
mx: []mv1beta1.ContainerMetrics{
makeCoMX("c1", "1m", "22Mi"),
@ -322,10 +384,12 @@ func Test_gatherPodMx(t *testing.T) {
perc: "10",
},
"multi": {
cc: []v1.Container{
makeContainer("c1", false, "11m", "22Mi", "111m", "44Mi"),
makeContainer("c2", false, "93m", "1402Mi", "0m", "2804Mi"),
makeContainer("c3", false, "11m", "34Mi", "0m", "69Mi"),
spec: &v1.PodSpec{
Containers: []v1.Container{
makeContainer("c1", false, "11m", "22Mi", "111m", "44Mi"),
makeContainer("c2", false, "93m", "1402Mi", "0m", "2804Mi"),
makeContainer("c3", false, "11m", "34Mi", "0m", "69Mi"),
},
},
r: metric{
cpu: 11 + 93 + 11,
@ -344,12 +408,37 @@ func Test_gatherPodMx(t *testing.T) {
},
perc: "46",
},
"sidecar": {
spec: &v1.PodSpec{
Containers: []v1.Container{
makeContainer("c1", false, "11m", "22Mi", "111m", "44Mi"),
},
InitContainers: []v1.Container{
makeContainer("c2", true, "93m", "1402Mi", "0m", "2804Mi"),
},
},
r: metric{
cpu: 11 + 93,
mem: (22 + 1402) * client.MegaByte,
lcpu: 111 + 0,
lmem: (44 + 2804) * client.MegaByte,
},
mx: []mv1beta1.ContainerMetrics{
makeCoMX("c1", "1m", "22Mi"),
makeCoMX("c2", "51m", "1275Mi"),
},
c: metric{
cpu: 1 + 51,
mem: (22 + 1275) * client.MegaByte,
},
perc: "50",
},
}
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
c, r := gatherCoMX(u.cc, u.mx)
c, r := gatherCoMX(u.spec, u.mx)
assert.Equal(t, u.c.cpu, c.cpu)
assert.Equal(t, u.c.mem, c.mem)
assert.Equal(t, u.c.lcpu, c.lcpu)
@ -427,18 +516,19 @@ func Test_podRequests(t *testing.T) {
// Helpers...
func makeContainer(n string, init bool, rc, rm, lc, lm string) v1.Container {
func makeContainer(n string, restartable bool, rc, rm, lc, lm string) v1.Container {
always := v1.ContainerRestartPolicyAlways
var res v1.ResourceRequirements
if init {
res = v1.ResourceRequirements{}
} else {
res = v1.ResourceRequirements{
Requests: makeRes(rc, rm),
Limits: makeRes(lc, lm),
}
var rp *v1.ContainerRestartPolicy
res = v1.ResourceRequirements{
Requests: makeRes(rc, rm),
Limits: makeRes(lc, lm),
}
if restartable {
rp = &always
}
return v1.Container{Name: n, Resources: res}
return v1.Container{Name: n, Resources: res, RestartPolicy: rp}
}
func makeRes(c, m string) v1.ResourceList {

View File

@ -198,6 +198,22 @@ func TestPodInitRender(t *testing.T) {
assert.Equal(t, e, r.Fields[:19])
}
func TestPodSidecarRender(t *testing.T) {
pom := render.PodWithMetrics{
Raw: load(t, "po_sidecar"),
MX: makePodMX("sleep", "100m", "40Mi"),
}
var po render.Pod
r := model1.NewRow(14)
err := po.Render(&pom, "", &r)
assert.Nil(t, err)
assert.Equal(t, "default/sleep", r.ID)
e := model1.Fields{"default", "sleep", "0", "●", "1/1", "Running", "0", "100", "40", "50:250", "50:80", "200", "40", "80", "50", "10.244.0.8", "kind-control-plane", "<none>", "<none>"}
assert.Equal(t, e, r.Fields[:19])
}
func TestCheckPodStatus(t *testing.T) {
uu := map[string]struct {
pod v1.Pod

293
internal/render/testdata/po_sidecar.json vendored Normal file
View File

@ -0,0 +1,293 @@
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"creationTimestamp": "2024-08-24T01:54:32Z",
"name": "sleep",
"namespace": "default",
"resourceVersion": "17852",
"uid": "35079257-0ffb-4b09-b2c1-3c0d416f2523"
},
"spec": {
"containers": [
{
"command": [
"/bin/sleep",
"60"
],
"image": "istio/base",
"imagePullPolicy": "Always",
"name": "sleep",
"resources": {
"limits": {
"cpu": "230m",
"memory": "40Mi"
},
"requests": {
"cpu": "30m",
"memory": "10Mi"
}
},
"terminationMessagePath": "/dev/termination-log",
"terminationMessagePolicy": "File",
"volumeMounts": [
{
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"name": "kube-api-access-mphcq",
"readOnly": true
}
]
}
],
"dnsPolicy": "ClusterFirst",
"enableServiceLinks": true,
"initContainers": [
{
"command": [
"/bin/sleep",
"1"
],
"image": "istio/base",
"imagePullPolicy": "Always",
"name": "init",
"resources": {
"limits": {
"cpu": "333m",
"memory": "333Mi"
},
"requests": {
"cpu": "333m",
"memory": "333Mi"
}
},
"terminationMessagePath": "/dev/termination-log",
"terminationMessagePolicy": "File",
"volumeMounts": [
{
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"name": "kube-api-access-mphcq",
"readOnly": true
}
]
},
{
"command": [
"/bin/sleep",
"60"
],
"image": "istio/base",
"imagePullPolicy": "Always",
"name": "sidecar",
"resources": {
"limits": {
"cpu": "20m",
"memory": "40Mi"
},
"requests": {
"cpu": "20m",
"memory": "40Mi"
}
},
"restartPolicy": "Always",
"terminationMessagePath": "/dev/termination-log",
"terminationMessagePolicy": "File",
"volumeMounts": [
{
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"name": "kube-api-access-mphcq",
"readOnly": true
}
]
}
],
"nodeName": "kind-control-plane",
"preemptionPolicy": "PreemptLowerPriority",
"priority": 0,
"restartPolicy": "Never",
"schedulerName": "default-scheduler",
"securityContext": {},
"serviceAccount": "default",
"serviceAccountName": "default",
"terminationGracePeriodSeconds": 30,
"tolerations": [
{
"effect": "NoExecute",
"key": "node.kubernetes.io/not-ready",
"operator": "Exists",
"tolerationSeconds": 300
},
{
"effect": "NoExecute",
"key": "node.kubernetes.io/unreachable",
"operator": "Exists",
"tolerationSeconds": 300
}
],
"volumes": [
{
"name": "kube-api-access-mphcq",
"projected": {
"defaultMode": 420,
"sources": [
{
"serviceAccountToken": {
"expirationSeconds": 3607,
"path": "token"
}
},
{
"configMap": {
"items": [
{
"key": "ca.crt",
"path": "ca.crt"
}
],
"name": "kube-root-ca.crt"
}
},
{
"downwardAPI": {
"items": [
{
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.namespace"
},
"path": "namespace"
}
]
}
}
]
}
}
]
},
"status": {
"conditions": [
{
"lastProbeTime": null,
"lastTransitionTime": "2024-08-24T01:54:36Z",
"status": "True",
"type": "PodReadyToStartContainers"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2024-08-24T01:54:38Z",
"status": "True",
"type": "Initialized"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2024-08-24T01:54:39Z",
"status": "True",
"type": "Ready"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2024-08-24T01:54:39Z",
"status": "True",
"type": "ContainersReady"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2024-08-24T01:54:32Z",
"status": "True",
"type": "PodScheduled"
}
],
"containerStatuses": [
{
"containerID": "containerd://a1848056a183e40afe3189fc4920bd565930180ebdf2f9e2daf778ea8105f93e",
"image": "docker.io/istio/base:latest",
"imageID": "docker.io/istio/base@sha256:61673929bc39a86dca7d978b27fc632d3e590bc59cd8b2f386408751d007c91e",
"lastState": {},
"name": "sleep",
"ready": true,
"restartCount": 0,
"started": true,
"state": {
"running": {
"startedAt": "2024-08-24T01:54:38Z"
}
},
"volumeMounts": [
{
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"name": "kube-api-access-mphcq",
"readOnly": true,
"recursiveReadOnly": "Disabled"
}
]
}
],
"hostIP": "172.18.0.2",
"hostIPs": [
{
"ip": "172.18.0.2"
}
],
"initContainerStatuses": [
{
"containerID": "containerd://75295261e5d751382c9a6ffa4477b84af2934686c360dcba2d8a6b9bc0f8cada",
"image": "docker.io/istio/base:latest",
"imageID": "docker.io/istio/base@sha256:61673929bc39a86dca7d978b27fc632d3e590bc59cd8b2f386408751d007c91e",
"lastState": {},
"name": "init",
"ready": true,
"restartCount": 0,
"started": false,
"state": {
"terminated": {
"containerID": "containerd://75295261e5d751382c9a6ffa4477b84af2934686c360dcba2d8a6b9bc0f8cada",
"exitCode": 0,
"finishedAt": "2024-08-24T01:54:37Z",
"reason": "Completed",
"startedAt": "2024-08-24T01:54:35Z"
}
},
"volumeMounts": [
{
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"name": "kube-api-access-mphcq",
"readOnly": true,
"recursiveReadOnly": "Disabled"
}
]
},
{
"containerID": "containerd://7a0d216a09630040c5b165c42cc9d2a95d541d95b6ac0a5ca604bf767d1b2cf0",
"image": "docker.io/istio/base:latest",
"imageID": "docker.io/istio/base@sha256:61673929bc39a86dca7d978b27fc632d3e590bc59cd8b2f386408751d007c91e",
"lastState": {},
"name": "sidecar",
"ready": true,
"restartCount": 0,
"started": true,
"state": {
"running": {
"startedAt": "2024-08-24T01:54:38Z"
}
},
"volumeMounts": [
{
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"name": "kube-api-access-mphcq",
"readOnly": true,
"recursiveReadOnly": "Disabled"
}
]
}
],
"phase": "Running",
"podIP": "10.244.0.8",
"podIPs": [
{
"ip": "10.244.0.8"
}
],
"qosClass": "Burstable",
"startTime": "2024-08-24T01:54:32Z"
}
}