From 308d0bce2d32ad7b88c00e40ba3a2f9bfdda11eb Mon Sep 17 00:00:00 2001
From: Darren Shepherd <darren@rancher.com>
Date: Fri, 14 Feb 2020 16:12:47 -0700
Subject: [PATCH] Move status logic to wrangler

---
 status/status.go | 310 ++---------------------------------------------
 1 file changed, 8 insertions(+), 302 deletions(-)

diff --git a/status/status.go b/status/status.go
index 1823722c..f10f1e66 100644
--- a/status/status.go
+++ b/status/status.go
@@ -3,315 +3,21 @@ package status
 import (
 	"strings"
 
-	"time"
-
-	"encoding/json"
-
-	"github.com/rancher/norman/types/convert"
-	"github.com/rancher/norman/types/values"
-	"github.com/sirupsen/logrus"
+	"github.com/rancher/wrangler/pkg/summary"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
-type status struct {
-	Conditions []condition `json:"conditions"`
-}
-
-type condition struct {
-	Reason  string
-	Type    string
-	Status  string
-	Message string
-}
-
-// True ==
-// False == error
-// Unknown == transitioning
-var transitioningMap = 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",
-	"Waiting":                     "waiting",
-	"InitialRolesPopulated":       "activating",
-	"ScalingActive":               "pending",
-	"AbleToScale":                 "pending",
-	"RunCompleted":                "running",
-}
-
-// True == error
-// False ==
-// Unknown ==
-var reverseErrorMap = map[string]bool{
-	"OutOfDisk":           true,
-	"MemoryPressure":      true,
-	"DiskPressure":        true,
-	"NetworkUnavailable":  true,
-	"KernelHasNoDeadlock": true,
-	"Unschedulable":       true,
-	"ReplicaFailure":      true,
-}
-
-// True ==
-// False == error
-// Unknown ==
-var errorMapping = map[string]bool{
-	"Failed":      true,
-	"Progressing": true,
-}
-
-// True ==
-// False == transitioning
-// Unknown == error
-var doneMap = map[string]string{
-	"Completed":   "activating",
-	"Ready":       "unavailable",
-	"Available":   "updating",
-	"Progressing": "inactive",
-}
-
-// True == transitioning
-// False ==
-// Unknown ==
-var progressMap = map[string]string{}
-
-func concat(str, next string) string {
-	if str == "" {
-		return next
-	}
-	if next == "" {
-		return str
-	}
-	if strings.EqualFold(str, next) {
-		return str
-	}
-	return str + "; " + next
-}
-
 func Set(data map[string]interface{}) {
-	genericStatus(data)
-	loadBalancerStatus(data)
-}
-
-func loadBalancerStatus(data map[string]interface{}) {
-	if data["state"] == "active" && data["kind"] == "Service" && values.GetValueN(data, "spec", "serviceKind") == "LoadBalancer" {
-		addresses, ok := values.GetSlice(data, "status", "loadBalancer", "ingress")
-		if !ok || len(addresses) == 0 {
-			data["state"] = "pending"
-			data["transitioning"] = "yes"
-			data["transitioningMessage"] = "Load balancer is being provisioned"
-		}
-	}
-}
-
-func genericStatus(data map[string]interface{}) {
 	if data == nil {
 		return
 	}
-
-	val, conditionsOk := values.GetValue(data, "status", "conditions")
-	var conditions []condition
-	convert.ToObj(val, &conditions)
-
-	statusAnn, annOK := values.GetValue(data, "metadata", "annotations", "cattle.io/status")
-	if annOK {
-		status := &status{}
-		s, ok := statusAnn.(string)
-		if ok {
-			err := json.Unmarshal([]byte(s), status)
-			if err != nil {
-				logrus.Warnf("Unable to unmarshal cattle status %v. Error: %v", s, err)
-			}
-		}
-		if len(status.Conditions) > 0 {
-			conditions = append(conditions, status.Conditions...)
-		}
-	}
-
-	state := ""
-	error := false
-	transitioning := false
-	message := ""
-
-	for _, c := range conditions {
-		if (errorMapping[c.Type] && c.Status == "False") || c.Reason == "Error" {
-			error = true
-			message = c.Message
-			break
-		}
-	}
-
-	if !error {
-		for _, c := range conditions {
-			if reverseErrorMap[c.Type] && c.Status == "True" {
-				error = true
-				message = concat(message, c.Message)
-			}
-		}
-	}
-
-	for _, c := range conditions {
-		newState, ok := transitioningMap[c.Type]
-		if !ok {
-			continue
-		}
-
-		if c.Status == "False" {
-			error = true
-			state = newState
-			message = concat(message, c.Message)
-		} else if c.Status == "Unknown" && state == "" {
-			transitioning = true
-			state = newState
-			message = concat(message, c.Message)
-		}
-	}
-
-	for _, c := range conditions {
-		if state != "" {
-			break
-		}
-		newState, ok := doneMap[c.Type]
-		if !ok {
-			continue
-		}
-		if c.Status == "False" {
-			transitioning = true
-			state = newState
-			message = concat(message, c.Message)
-		} else if c.Status == "Unknown" {
-			error = true
-			state = newState
-			message = concat(message, c.Message)
-		}
-	}
-
-	for _, c := range conditions {
-		if state != "" {
-			break
-		}
-		newState, ok := progressMap[c.Type]
-		if !ok {
-			continue
-		}
-		if c.Status == "True" {
-			transitioning = true
-			state = newState
-			message = concat(message, c.Message)
-		}
-	}
-
-	if state == "" {
-		val, ok := values.GetValue(data, "spec", "active")
-		if ok {
-			if convert.ToBool(val) {
-				state = "active"
-			} else {
-				state = "inactive"
-			}
-		}
-	}
-
-	phase, ok := values.GetValueN(data, "status", "phase").(string)
-	if phase != "" && ok {
-		if phase == "Succeeded" {
-			state = "succeeded"
-			transitioning = false
-		} else if state == "" {
-			state = phase
-		}
-	}
-
-	apiVersion, _ := values.GetValueN(data, "apiVersion").(string)
-	if state == "" && conditionsOk && len(conditions) == 0 && strings.Contains(apiVersion, "cattle.io") {
-		if val, ok := values.GetValue(data, "metadata", "created"); ok {
-			if i, err := convert.ToTimestamp(val); err == nil {
-				if time.Unix(i/1000, 0).Add(5 * time.Second).After(time.Now()) {
-					state = "initializing"
-					transitioning = true
-				}
-			}
-		}
-	}
-
-	if state == "" {
-		state = "active"
-	}
-
-	if error {
+	summary := summary.Summarize(&unstructured.Unstructured{Object: data})
+	data["state"] = summary.State
+	data["transitioning"] = "no"
+	if summary.Error {
 		data["transitioning"] = "error"
-	} else if transitioning {
+	} else if summary.Transitioning {
 		data["transitioning"] = "yes"
-	} else {
-		data["transitioning"] = "no"
-	}
-
-	data["state"] = strings.ToLower(state)
-	data["transitioningMessage"] = message
-
-	val, ok = values.GetValue(data, "metadata", "removed")
-	if ok && val != "" && val != nil {
-		data["state"] = "removing"
-		data["transitioning"] = "yes"
-
-		finalizers, ok := values.GetStringSlice(data, "metadata", "finalizers")
-		if !ok {
-			finalizers, ok = values.GetStringSlice(data, "spec", "finalizers")
-		}
-
-		msg := message
-		for _, cond := range conditions {
-			if cond.Type == "Removed" && (cond.Status == "Unknown" || cond.Status == "False") && cond.Message != "" {
-				msg = cond.Message
-			}
-		}
-
-		if ok && len(finalizers) > 0 {
-			parts := strings.Split(finalizers[0], "controller.cattle.io/")
-			f := parts[len(parts)-1]
-
-			if f == "foregroundDeletion" {
-				f = "object cleanup"
-			}
-
-			if len(msg) > 0 {
-				msg = msg + "; waiting on " + f
-			} else {
-				msg = "waiting on " + f
-			}
-			data["transitioningMessage"] = msg
-			if i, err := convert.ToTimestamp(val); err == nil {
-				if time.Unix(i/1000, 0).Add(5 * time.Minute).Before(time.Now()) {
-					data["transitioning"] = "error"
-				}
-			}
-		}
-
-		return
 	}
+	data["transitioningMessage"] = strings.Join(summary.Message, "; ")
 }