k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/measurement/util/pods.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "fmt" 21 22 corev1 "k8s.io/api/core/v1" 23 "k8s.io/apimachinery/pkg/util/sets" 24 ) 25 26 const ( 27 nonExist = "NonExist" 28 ) 29 30 type Status int 31 32 const ( 33 Unknown Status = iota 34 Terminating 35 RunningAndReady 36 RunningButNotReady 37 PendingScheduled 38 PendingNotScheduled 39 Inactive 40 ) 41 42 var _statusName = [...]string{"Unknown", "Terminating", "RunningAndReady", 43 "RunningButNotReady", "PendingScheduled", "PendingNotScheduled", "Inactive"} 44 45 func (i Status) String() string { 46 if int(i) >= len(_statusName) { 47 return "Unknown" 48 } 49 return _statusName[i] 50 } 51 52 // PodsStartupStatus represents status of a pods group. 53 type PodsStartupStatus struct { 54 Expected int 55 Terminating int 56 Running int 57 Scheduled int 58 RunningButNotReady int 59 Waiting int 60 Pending int 61 Unknown int 62 Inactive int 63 Created int 64 RunningUpdated int 65 LastIsPodUpdatedError error 66 } 67 68 // String returns string representation for podsStartupStatus. 69 func (s *PodsStartupStatus) String() string { 70 return fmt.Sprintf("Pods: %d out of %d created, %d running (%d updated), %d pending scheduled, %d not scheduled, %d inactive, %d terminating, %d unknown, %d runningButNotReady ", 71 s.Created, s.Expected, s.Running, s.RunningUpdated, s.Pending, s.Waiting, s.Inactive, s.Terminating, s.Unknown, s.RunningButNotReady) 72 } 73 74 func podStatus(p *corev1.Pod) Status { 75 if p.DeletionTimestamp != nil { 76 return Terminating 77 } 78 if p.Status.Phase == corev1.PodRunning { 79 ready := false 80 for _, c := range p.Status.Conditions { 81 if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue { 82 ready = true 83 break 84 } 85 } 86 if ready { 87 // Only count a pod is running when it is also ready. 88 return RunningAndReady 89 } 90 return RunningButNotReady 91 } 92 if p.Status.Phase == corev1.PodPending { 93 if p.Spec.NodeName == "" { 94 return PendingNotScheduled 95 } 96 return PendingScheduled 97 } 98 if p.Status.Phase == corev1.PodSucceeded || p.Status.Phase == corev1.PodFailed { 99 return Inactive 100 } 101 return Unknown 102 } 103 104 // ComputePodsStartupStatus computes PodsStartupStatus for a group of pods. 105 // TODO(mborsz): Migrate to podStatus instead of recalculating per pod status here. 106 func ComputePodsStartupStatus(pods []*corev1.Pod, expected int, isPodUpdated func(*corev1.Pod) error) PodsStartupStatus { 107 startupStatus := PodsStartupStatus{ 108 Expected: expected, 109 } 110 for _, p := range pods { 111 if p.DeletionTimestamp != nil { 112 startupStatus.Terminating++ 113 continue 114 } 115 startupStatus.Created++ 116 if p.Status.Phase == corev1.PodRunning { 117 ready := false 118 for _, c := range p.Status.Conditions { 119 if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue { 120 ready = true 121 break 122 } 123 } 124 if ready { 125 // Only count a pod is running when it is also ready. 126 startupStatus.Running++ 127 if isPodUpdated == nil { 128 startupStatus.RunningUpdated++ 129 } else { 130 if err := isPodUpdated(p); err != nil { 131 startupStatus.LastIsPodUpdatedError = err 132 } else { 133 startupStatus.RunningUpdated++ 134 } 135 } 136 } else { 137 startupStatus.RunningButNotReady++ 138 } 139 } else if p.Status.Phase == corev1.PodPending { 140 if p.Spec.NodeName == "" { 141 startupStatus.Waiting++ 142 } else { 143 startupStatus.Pending++ 144 } 145 } else if p.Status.Phase == corev1.PodSucceeded || p.Status.Phase == corev1.PodFailed { 146 startupStatus.Inactive++ 147 } else if p.Status.Phase == corev1.PodUnknown { 148 startupStatus.Unknown++ 149 } 150 if p.Spec.NodeName != "" { 151 startupStatus.Scheduled++ 152 } 153 } 154 return startupStatus 155 } 156 157 type podDiffInfo struct { 158 oldHostname string 159 oldPhase string 160 hostname string 161 phase string 162 } 163 164 // PodDiff represets diff between old and new group of pods. 165 type PodDiff map[string]*podDiffInfo 166 167 // Print formats and prints the give PodDiff. 168 func (p PodDiff) String(ignorePhases sets.String) string { 169 ret := "" 170 for name, info := range p { 171 if ignorePhases.Has(info.phase) { 172 continue 173 } 174 if info.phase == nonExist { 175 ret += fmt.Sprintf("Pod %v was deleted, had phase %v and host %v\n", name, info.oldPhase, info.oldHostname) 176 continue 177 } 178 phaseChange, hostChange := false, false 179 msg := fmt.Sprintf("Pod %v ", name) 180 if info.oldPhase != info.phase { 181 phaseChange = true 182 if info.oldPhase == nonExist { 183 msg += fmt.Sprintf("in phase %v ", info.phase) 184 } else { 185 msg += fmt.Sprintf("went from phase: %v -> %v ", info.oldPhase, info.phase) 186 } 187 } 188 if info.oldHostname != info.hostname { 189 hostChange = true 190 if info.oldHostname == nonExist || info.oldHostname == "" { 191 msg += fmt.Sprintf("assigned host %v ", info.hostname) 192 } else { 193 msg += fmt.Sprintf("went from host: %v -> %v ", info.oldHostname, info.hostname) 194 } 195 } 196 if phaseChange || hostChange { 197 ret += msg + "\n" 198 } 199 } 200 return ret 201 } 202 203 // DeletedPods returns a slice of pods that were present at the beginning 204 // and then disappeared. 205 func (p PodDiff) DeletedPods() []string { 206 var deletedPods []string 207 for podName, podDiffInfo := range p { 208 if podDiffInfo.hostname == nonExist { 209 deletedPods = append(deletedPods, podName) 210 } 211 } 212 return deletedPods 213 } 214 215 // AddedPods returns a slice of pods that were added. 216 func (p PodDiff) AddedPods() []string { 217 var addedPods []string 218 for podName, podDiffInfo := range p { 219 if podDiffInfo.oldHostname == nonExist { 220 addedPods = append(addedPods, podName) 221 } 222 } 223 return addedPods 224 } 225 226 // DiffPods computes a PodDiff given 2 lists of pods. 227 func DiffPods(oldPods []*corev1.Pod, curPods []*corev1.Pod) PodDiff { 228 podDiffInfoMap := PodDiff{} 229 230 // New pods will show up in the curPods list but not in oldPods. They have oldhostname/phase == nonexist. 231 for _, pod := range curPods { 232 podDiffInfoMap[pod.Name] = &podDiffInfo{hostname: pod.Spec.NodeName, phase: string(pod.Status.Phase), oldHostname: nonExist, oldPhase: nonExist} 233 } 234 235 // Deleted pods will show up in the oldPods list but not in curPods. They have a hostname/phase == nonexist. 236 for _, pod := range oldPods { 237 if info, ok := podDiffInfoMap[pod.Name]; ok { 238 info.oldHostname, info.oldPhase = pod.Spec.NodeName, string(pod.Status.Phase) 239 } else { 240 podDiffInfoMap[pod.Name] = &podDiffInfo{hostname: nonExist, phase: nonExist, oldHostname: pod.Spec.NodeName, oldPhase: string(pod.Status.Phase)} 241 } 242 } 243 return podDiffInfoMap 244 } 245 246 type PodInfo struct { 247 Name string 248 Hostname string 249 Phase string 250 Status Status 251 Namespace string 252 } 253 254 func (p *PodInfo) String() string { 255 return fmt.Sprintf("{%v %v %v %v %v}", p.Namespace, p.Name, p.Phase, p.Status.String(), p.Hostname) 256 } 257 258 // PodsStatus is a collection of current pod phases and node assignments data. 259 type PodsStatus struct { 260 Info []*PodInfo 261 } 262 263 // ComputePodsStatus computes PodsStatus for a group of pods. 264 func ComputePodsStatus(pods []*corev1.Pod) *PodsStatus { 265 ps := &PodsStatus{ 266 Info: make([]*PodInfo, len(pods)), 267 } 268 for i := range pods { 269 ps.Info[i] = &PodInfo{ 270 Name: pods[i].Name, 271 Hostname: pods[i].Spec.NodeName, 272 Phase: string(pods[i].Status.Phase), 273 Status: podStatus(pods[i]), 274 Namespace: pods[i].Namespace, 275 } 276 } 277 return ps 278 } 279 280 // String returns string representation of a PodsStatus. 281 func (ps *PodsStatus) String() string { 282 return fmt.Sprintf("%v", ps.Info) 283 } 284 285 func (ps *PodsStatus) NotRunningAndReady() *PodsStatus { 286 res := &PodsStatus{ 287 Info: make([]*PodInfo, 0), 288 } 289 290 for _, info := range ps.Info { 291 if info.Status != RunningAndReady { 292 res.Info = append(res.Info, info) 293 } 294 } 295 296 return res 297 }