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, "; ") }