mirror of
https://github.com/ysoftdevs/gardener-extension-shoot-fleet-agent.git
synced 2026-03-27 03:21:36 +01:00
426 lines
11 KiB
Go
426 lines
11 KiB
Go
package summary
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rancher/wrangler/pkg/data"
|
|
"github.com/rancher/wrangler/pkg/data/convert"
|
|
"github.com/rancher/wrangler/pkg/kv"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
kstatus "sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
|
)
|
|
|
|
const (
|
|
kindSep = ", Kind="
|
|
)
|
|
|
|
var (
|
|
// True ==
|
|
// False == error
|
|
// Unknown == transitioning
|
|
TransitioningUnknown = map[string]string{
|
|
"Active": "activating",
|
|
"AddonDeploy": "provisioning",
|
|
"AgentDeployed": "provisioning",
|
|
"BackingNamespaceCreated": "configuring",
|
|
"Built": "building",
|
|
"CertsGenerated": "provisioning",
|
|
"ConfigOK": "configuring",
|
|
"Created": "creating",
|
|
"CreatorMadeOwner": "configuring",
|
|
"DefaultNamespaceAssigned": "configuring",
|
|
"DefaultNetworkPolicyCreated": "configuring",
|
|
"DefaultProjectCreated": "configuring",
|
|
"DockerProvisioned": "provisioning",
|
|
"Deployed": "deploying",
|
|
"Drained": "draining",
|
|
"Downloaded": "downloading",
|
|
"etcd": "provisioning",
|
|
"Inactive": "deactivating",
|
|
"Initialized": "initializing",
|
|
"Installed": "installing",
|
|
"NodesCreated": "provisioning",
|
|
"Pending": "pending",
|
|
"PodScheduled": "scheduling",
|
|
"Provisioned": "provisioning",
|
|
"Refreshed": "refreshed",
|
|
"Registered": "registering",
|
|
"Removed": "removing",
|
|
"Saved": "saving",
|
|
"Updated": "updating",
|
|
"Updating": "updating",
|
|
"Upgraded": "upgrading",
|
|
"Waiting": "waiting",
|
|
"InitialRolesPopulated": "activating",
|
|
"ScalingActive": "pending",
|
|
"AbleToScale": "pending",
|
|
"RunCompleted": "running",
|
|
"Processed": "processed",
|
|
}
|
|
|
|
// True == error
|
|
// False ==
|
|
// Unknown ==
|
|
ErrorTrue = map[string]bool{
|
|
"OutOfDisk": true,
|
|
"MemoryPressure": true,
|
|
"DiskPressure": true,
|
|
"NetworkUnavailable": true,
|
|
"KernelHasNoDeadlock": true,
|
|
"Unschedulable": true,
|
|
"ReplicaFailure": true,
|
|
}
|
|
|
|
// True ==
|
|
// False == error
|
|
// Unknown ==
|
|
ErrorFalse = map[string]bool{
|
|
"Failed": true,
|
|
"Progressing": true,
|
|
}
|
|
|
|
// True ==
|
|
// False == transitioning
|
|
// Unknown == error
|
|
TransitioningFalse = map[string]string{
|
|
"Completed": "activating",
|
|
"Ready": "unavailable",
|
|
"Available": "updating",
|
|
"Progressing": "inactive",
|
|
}
|
|
|
|
Summarizers []Summarizer
|
|
)
|
|
|
|
type Summarizer func(obj data.Object, conditions []Condition, summary Summary) Summary
|
|
|
|
func init() {
|
|
Summarizers = []Summarizer{
|
|
checkStatusSummary,
|
|
checkErrors,
|
|
checkTransitioning,
|
|
checkActive,
|
|
checkPhase,
|
|
checkInitializing,
|
|
checkRemoving,
|
|
checkStandard,
|
|
checkLoadBalancer,
|
|
checkPod,
|
|
checkPodSelector,
|
|
checkOwner,
|
|
checkApplyOwned,
|
|
checkCattleTypes,
|
|
}
|
|
}
|
|
|
|
func checkOwner(obj data.Object, conditions []Condition, summary Summary) Summary {
|
|
ustr := &unstructured.Unstructured{
|
|
Object: obj,
|
|
}
|
|
for _, ownerref := range ustr.GetOwnerReferences() {
|
|
rel := Relationship{
|
|
Name: ownerref.Name,
|
|
Kind: ownerref.Kind,
|
|
APIVersion: ownerref.APIVersion,
|
|
Type: "owner",
|
|
Inbound: true,
|
|
}
|
|
if ownerref.Controller != nil && *ownerref.Controller {
|
|
rel.ControlledBy = true
|
|
}
|
|
|
|
summary.Relationships = append(summary.Relationships, rel)
|
|
}
|
|
|
|
return summary
|
|
}
|
|
|
|
func checkStatusSummary(obj data.Object, conditions []Condition, summary Summary) Summary {
|
|
summaryObj := obj.Map("status", "display")
|
|
if len(summaryObj) == 0 {
|
|
summaryObj = obj.Map("status", "summary")
|
|
if len(summaryObj) == 0 {
|
|
return summary
|
|
}
|
|
}
|
|
obj = summaryObj
|
|
|
|
if _, ok := obj["state"]; ok {
|
|
summary.State = obj.String("state")
|
|
}
|
|
if _, ok := obj["transitioning"]; ok {
|
|
summary.Transitioning = obj.Bool("transitioning")
|
|
}
|
|
if _, ok := obj["error"]; ok {
|
|
summary.Error = obj.Bool("error")
|
|
}
|
|
if _, ok := obj["message"]; ok {
|
|
summary.Message = append(summary.Message, obj.String("message"))
|
|
}
|
|
|
|
return summary
|
|
}
|
|
|
|
func checkStandard(obj data.Object, conditions []Condition, summary Summary) Summary {
|
|
if summary.State != "" {
|
|
return summary
|
|
}
|
|
|
|
// this is a hack to not call the standard summarizers on norman mapped objects
|
|
if strings.HasPrefix(obj.String("type"), "/") {
|
|
return summary
|
|
}
|
|
|
|
result, err := kstatus.Compute(&unstructured.Unstructured{Object: obj})
|
|
if err != nil {
|
|
return summary
|
|
}
|
|
|
|
switch result.Status {
|
|
case kstatus.InProgressStatus:
|
|
summary.State = "in-progress"
|
|
summary.Message = append(summary.Message, result.Message)
|
|
summary.Transitioning = true
|
|
case kstatus.FailedStatus:
|
|
summary.State = "failed"
|
|
summary.Message = append(summary.Message, result.Message)
|
|
summary.Error = true
|
|
case kstatus.CurrentStatus:
|
|
summary.State = "active"
|
|
summary.Message = append(summary.Message, result.Message)
|
|
case kstatus.TerminatingStatus:
|
|
summary.State = "removing"
|
|
summary.Message = append(summary.Message, result.Message)
|
|
summary.Transitioning = true
|
|
}
|
|
|
|
return summary
|
|
}
|
|
|
|
func checkErrors(_ data.Object, conditions []Condition, summary Summary) Summary {
|
|
for _, c := range conditions {
|
|
if (ErrorFalse[c.Type()] && c.Status() == "False") || c.Reason() == "Error" {
|
|
summary.Error = true
|
|
summary.Message = append(summary.Message, c.Message())
|
|
if summary.State == "active" || summary.State == "" {
|
|
summary.State = "error"
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
if summary.Error {
|
|
return summary
|
|
}
|
|
|
|
for _, c := range conditions {
|
|
if ErrorTrue[c.Type()] && c.Status() == "True" {
|
|
summary.Error = true
|
|
summary.Message = append(summary.Message, c.Message())
|
|
}
|
|
}
|
|
return summary
|
|
}
|
|
|
|
func checkTransitioning(_ data.Object, conditions []Condition, summary Summary) Summary {
|
|
for _, c := range conditions {
|
|
newState, ok := TransitioningUnknown[c.Type()]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if c.Status() == "False" {
|
|
summary.Error = true
|
|
summary.State = newState
|
|
summary.Message = append(summary.Message, c.Message())
|
|
} else if c.Status() == "Unknown" && summary.State == "" {
|
|
summary.Transitioning = true
|
|
summary.State = newState
|
|
summary.Message = append(summary.Message, c.Message())
|
|
}
|
|
}
|
|
|
|
for _, c := range conditions {
|
|
if summary.State != "" {
|
|
break
|
|
}
|
|
newState, ok := TransitioningFalse[c.Type()]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if c.Status() == "False" {
|
|
summary.Transitioning = true
|
|
summary.State = newState
|
|
summary.Message = append(summary.Message, c.Message())
|
|
} else if c.Status() == "Unknown" {
|
|
summary.Error = true
|
|
summary.State = newState
|
|
summary.Message = append(summary.Message, c.Message())
|
|
}
|
|
}
|
|
|
|
return summary
|
|
}
|
|
|
|
func checkActive(obj data.Object, _ []Condition, summary Summary) Summary {
|
|
if summary.State != "" {
|
|
return summary
|
|
}
|
|
|
|
switch obj.String("spec", "active") {
|
|
case "true":
|
|
summary.State = "active"
|
|
case "false":
|
|
summary.State = "inactive"
|
|
}
|
|
|
|
return summary
|
|
}
|
|
|
|
func checkPhase(obj data.Object, _ []Condition, summary Summary) Summary {
|
|
phase := obj.String("status", "phase")
|
|
if phase == "Succeeded" {
|
|
summary.State = "succeeded"
|
|
summary.Transitioning = false
|
|
} else if phase != "" && summary.State == "" {
|
|
summary.State = phase
|
|
}
|
|
return summary
|
|
}
|
|
|
|
func checkInitializing(obj data.Object, conditions []Condition, summary Summary) Summary {
|
|
apiVersion := obj.String("apiVersion")
|
|
_, hasConditions := obj.Map("status")["conditions"]
|
|
if summary.State == "" && hasConditions && len(conditions) == 0 && strings.Contains(apiVersion, "cattle.io") {
|
|
val := obj.String("metadata", "created")
|
|
if i, err := convert.ToTimestamp(val); err == nil {
|
|
if time.Unix(i/1000, 0).Add(5 * time.Second).After(time.Now()) {
|
|
summary.State = "initializing"
|
|
summary.Transitioning = true
|
|
}
|
|
}
|
|
}
|
|
return summary
|
|
}
|
|
|
|
func checkRemoving(obj data.Object, conditions []Condition, summary Summary) Summary {
|
|
removed := obj.String("metadata", "removed")
|
|
if removed == "" {
|
|
return summary
|
|
}
|
|
|
|
summary.State = "removing"
|
|
summary.Transitioning = true
|
|
|
|
finalizers := obj.StringSlice("metadata", "finalizers")
|
|
if len(finalizers) == 0 {
|
|
finalizers = obj.StringSlice("spec", "finalizers")
|
|
}
|
|
|
|
for _, cond := range conditions {
|
|
if cond.Type() == "Removed" && (cond.Status() == "Unknown" || cond.Status() == "False") && cond.Message() != "" {
|
|
summary.Message = append(summary.Message, cond.Message())
|
|
}
|
|
}
|
|
|
|
if len(finalizers) == 0 {
|
|
return summary
|
|
}
|
|
|
|
_, f := kv.RSplit(finalizers[0], "controller.cattle.io/")
|
|
if f == "foregroundDeletion" {
|
|
f = "object cleanup"
|
|
}
|
|
|
|
summary.Message = append(summary.Message, "waiting on "+f)
|
|
if i, err := convert.ToTimestamp(removed); err == nil {
|
|
if time.Unix(i/1000, 0).Add(5 * time.Minute).Before(time.Now()) {
|
|
summary.Error = true
|
|
}
|
|
}
|
|
|
|
return summary
|
|
}
|
|
|
|
func checkLoadBalancer(obj data.Object, _ []Condition, summary Summary) Summary {
|
|
if (summary.State == "active" || summary.State == "") &&
|
|
obj.String("kind") == "Service" &&
|
|
(obj.String("spec", "serviceKind") == "LoadBalancer" ||
|
|
obj.String("spec", "type") == "LoadBalancer") {
|
|
addresses := obj.Slice("status", "loadBalancer", "ingress")
|
|
if len(addresses) == 0 {
|
|
summary.State = "pending"
|
|
summary.Transitioning = true
|
|
summary.Message = append(summary.Message, "Load balancer is being provisioned")
|
|
}
|
|
}
|
|
|
|
return summary
|
|
}
|
|
|
|
func isKind(obj data.Object, kind string, apiGroups ...string) bool {
|
|
if obj.String("kind") != kind {
|
|
return false
|
|
}
|
|
|
|
if len(apiGroups) == 0 {
|
|
return obj.String("apiVersion") == "v1"
|
|
}
|
|
|
|
if len(apiGroups) == 0 {
|
|
apiGroups = []string{""}
|
|
}
|
|
|
|
for _, group := range apiGroups {
|
|
switch {
|
|
case group == "":
|
|
if obj.String("apiVersion") == "v1" {
|
|
return true
|
|
}
|
|
case group[len(group)-1] == '/':
|
|
if strings.HasPrefix(obj.String("apiVersion"), group) {
|
|
return true
|
|
}
|
|
default:
|
|
if obj.String("apiVersion") != group {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func checkApplyOwned(obj data.Object, conditions []Condition, summary Summary) Summary {
|
|
if len(obj.Slice("metadata", "ownerReferences")) > 0 {
|
|
return summary
|
|
}
|
|
|
|
annotations := obj.Map("metadata", "annotations")
|
|
gvkString := convert.ToString(annotations["objectset.rio.cattle.io/owner-gvk"])
|
|
i := strings.Index(gvkString, kindSep)
|
|
if i <= 0 {
|
|
return summary
|
|
}
|
|
|
|
name := convert.ToString(annotations["objectset.rio.cattle.io/owner-name"])
|
|
namespace := convert.ToString(annotations["objectset.rio.cattle.io/owner-namespace"])
|
|
|
|
apiVersion := gvkString[:i]
|
|
kind := gvkString[i+len(kindSep):]
|
|
|
|
rel := Relationship{
|
|
Name: name,
|
|
Namespace: namespace,
|
|
Kind: kind,
|
|
APIVersion: apiVersion,
|
|
Type: "applies",
|
|
Inbound: true,
|
|
}
|
|
|
|
summary.Relationships = append(summary.Relationships, rel)
|
|
|
|
return summary
|
|
}
|