pkg/scheduler/metrics.go (187 lines of code) (raw):

// Copyright 2022 Google LLC // // 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 scheduler import ( "reflect" "time" "github.com/GoogleCloudPlatform/gke-prober/pkg/common" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" ) type mapValue struct { Key map[string]string Value int } type MapCounter interface { Increment(m map[string]string, c int) Dump() []mapValue } type mapCounter struct { values []mapValue } func newMapCounter() *mapCounter { return &mapCounter{ values: []mapValue{}, } } func (mc *mapCounter) Increment(m map[string]string, c int) { for i, v := range mc.values { if reflect.DeepEqual(v.Key, m) { mc.values[i].Value = v.Value + c return } } mc.values = append(mc.values, mapValue{Key: m, Value: c}) } func (mc *mapCounter) Dump() []mapValue { return mc.values } func readyCondition(conditions []v1.NodeCondition) string { for _, c := range conditions { if c.Type == v1.NodeReady { return string(c.Status) } } return "unknown" } func nodeConditions(l []*v1.Node) *mapCounter { mc := newMapCounter() for _, node := range l { nodepool := getLabel(node.Labels, "cloud.google.com/gke-nodepool") zone := getLabel(node.Labels, "topology.kubernetes.io/zone") for t, s := range conditionStatuses(node.Status.Conditions) { labels := map[string]string{ "nodepool": nodepool, "zone": zone, "type": t, "status": s, } mc.Increment(labels, 1) } } return mc } func conditionStatuses(conditions []v1.NodeCondition) map[string]string { m := map[string]string{} for _, c := range conditions { m[string(c.Type)] = string(c.Status) } return m } func nodeAvailabilities(l []*v1.Node) (*mapCounter, int) { availableNodes := 0 mc := newMapCounter() for _, node := range l { ready, schedulable, doneWarming := nodeAvailability(node) available := ready && schedulable && doneWarming labels := map[string]string{ "nodepool": getLabel(node.Labels, "cloud.google.com/gke-nodepool"), "zone": getLabel(node.Labels, "topology.kubernetes.io/zone"), "ready": boolToStr(ready), "schedulable": boolToStr(schedulable), "done_warming": boolToStr(doneWarming), "available": boolToStr(available), } if available { availableNodes++ } mc.Increment(labels, 1) } return mc, availableNodes } func nodesAvailable(l []*v1.Node) int { availableNodes := 0 for _, node := range l { ready := nodeIsReady(node) scheduleable := nodeIsScheduleable(node) doneWarming := nodeIsDoneWarming(node) if ready && scheduleable && doneWarming { availableNodes++ } } return availableNodes } func nodeIsReady(node *v1.Node) bool { return "True" == readyCondition(node.Status.Conditions) } func nodeIsScheduleable(node *v1.Node) bool { for _, t := range node.Spec.Taints { if t.Key == v1.TaintNodeUnschedulable { return false } } return true } func nodeIsDoneWarming(node *v1.Node) bool { creationTime := node.ObjectMeta.CreationTimestamp now := time.Now() return now.Sub(creationTime.Time) >= common.WarmingPeriod } func nodeAvailability(node *v1.Node) (ready, scheduleable, doneWarming bool) { return nodeIsReady(node), nodeIsScheduleable(node), nodeIsDoneWarming(node) } // Return label or "Unknown" func getLabel(labels map[string]string, key string) string { value := "Unknown" if _, ok := labels[key]; ok { value = labels[key] } return value } func daemonSetPodCountByAddon(l []*appsv1.DaemonSet) map[common.Addon]int { podCounts := map[common.Addon]int{} for _, d := range l { if d.Status.DesiredNumberScheduled != 0 { if a, ok := common.AddonFromDaemonSet(d); ok { podCounts[a] = podCounts[a] + 1 } } } return podCounts } func deploymentPodCountByAddon(l []*appsv1.Deployment) map[common.Addon]int { podCounts := map[common.Addon]int{} for _, d := range l { if *d.Spec.Replicas > 0 { if a, ok := common.AddonFromDeployment(d); ok { podCounts[a] = podCounts[a] + int(*d.Spec.Replicas) } } } return podCounts } func podsByAddon(pods []*v1.Pod) map[common.Addon][]*v1.Pod { addonPods := map[common.Addon][]*v1.Pod{} for _, pod := range pods { addon, ok := common.AddonFromPod(pod) if !ok { continue } if _, ok := addonPods[addon]; !ok { addonPods[addon] = []*v1.Pod{} } addonPods[addon] = append(addonPods[addon], pod) } return addonPods } func podIsRunning(pod *v1.Pod) bool { for _, c := range pod.Status.Conditions { if c.Type == v1.PodReady { return c.Status == "True" } } return false } // formatter specifying capitalized values, aligning with internal k8s style func boolToStr(b bool) string { if b { return "True" } return "False" } func addAddonLabels(addon common.Addon, m map[string]string) { m["addon"] = addon.Name m["version"] = addon.Version m["controller"] = addon.Kind } func addonLabels(addon common.Addon) map[string]string { m := map[string]string{} addAddonLabels(addon, m) return m }