2017-11-15 23:59:47 +00:00
|
|
|
package status
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
2017-12-04 23:42:18 +00:00
|
|
|
"time"
|
|
|
|
|
2018-03-05 05:02:09 +00:00
|
|
|
"encoding/json"
|
|
|
|
|
2017-11-15 23:59:47 +00:00
|
|
|
"github.com/rancher/norman/types/convert"
|
2017-11-29 21:38:39 +00:00
|
|
|
"github.com/rancher/norman/types/values"
|
2018-03-05 05:02:09 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-11-15 23:59:47 +00:00
|
|
|
)
|
|
|
|
|
2018-03-05 05:02:09 +00:00
|
|
|
type status struct {
|
|
|
|
Conditions []condition `json:"conditions"`
|
|
|
|
}
|
|
|
|
|
2017-11-15 23:59:47 +00:00
|
|
|
type condition struct {
|
2018-06-05 04:44:54 +00:00
|
|
|
Reason string
|
2017-11-15 23:59:47 +00:00
|
|
|
Type string
|
|
|
|
Status string
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
2018-01-12 10:01:35 +00:00
|
|
|
// True ==
|
|
|
|
// False == error
|
|
|
|
// Unknown == transitioning
|
|
|
|
var transitioningMap = map[string]string{
|
2018-02-24 06:14:06 +00:00
|
|
|
"Active": "activating",
|
2018-03-31 10:15:08 +00:00
|
|
|
"AddonDeploy": "provisioning",
|
|
|
|
"AgentDeployed": "provisioning",
|
2018-02-24 06:14:06 +00:00
|
|
|
"BackingNamespaceCreated": "configuring",
|
2018-07-03 08:30:15 +00:00
|
|
|
"Built": "building",
|
2018-03-31 10:15:08 +00:00
|
|
|
"CertsGenerated": "provisioning",
|
2018-02-24 06:14:06 +00:00
|
|
|
"ConfigOK": "configuring",
|
|
|
|
"Created": "creating",
|
2018-02-26 23:15:12 +00:00
|
|
|
"CreatorMadeOwner": "configuring",
|
2018-02-24 06:14:06 +00:00
|
|
|
"DefaultNamespaceAssigned": "configuring",
|
|
|
|
"DefaultNetworkPolicyCreated": "configuring",
|
2018-02-26 23:15:12 +00:00
|
|
|
"DefaultProjectCreated": "configuring",
|
|
|
|
"DockerProvisioned": "provisioning",
|
2019-01-02 18:42:59 +00:00
|
|
|
"Deployed": "deploying",
|
2018-07-11 20:55:08 +00:00
|
|
|
"Drained": "draining",
|
2018-02-24 06:14:06 +00:00
|
|
|
"Downloaded": "downloading",
|
2018-03-31 10:15:08 +00:00
|
|
|
"etcd": "provisioning",
|
2018-02-24 06:14:06 +00:00
|
|
|
"Inactive": "deactivating",
|
|
|
|
"Initialized": "initializing",
|
2018-03-27 08:19:30 +00:00
|
|
|
"Installed": "installing",
|
2018-02-24 06:14:06 +00:00
|
|
|
"NodesCreated": "provisioning",
|
|
|
|
"Pending": "pending",
|
|
|
|
"PodScheduled": "scheduling",
|
|
|
|
"Provisioned": "provisioning",
|
2018-04-19 18:50:37 +00:00
|
|
|
"Refreshed": "refreshed",
|
2018-02-24 06:14:06 +00:00
|
|
|
"Registered": "registering",
|
|
|
|
"Removed": "removing",
|
|
|
|
"Saved": "saving",
|
|
|
|
"Updated": "updating",
|
|
|
|
"Updating": "updating",
|
2018-03-22 23:02:15 +00:00
|
|
|
"Waiting": "waiting",
|
2018-03-05 05:02:09 +00:00
|
|
|
"InitialRolesPopulated": "activating",
|
2019-03-28 13:16:41 +00:00
|
|
|
"ScalingActive": "pending",
|
|
|
|
"AbleToScale": "pending",
|
2019-12-12 22:14:43 +00:00
|
|
|
"RunCompleted": "running",
|
2018-01-12 10:01:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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{
|
2018-03-24 06:02:31 +00:00
|
|
|
"Failed": true,
|
|
|
|
"Progressing": true,
|
2018-01-12 10:01:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// True ==
|
|
|
|
// False == transitioning
|
2018-02-20 19:42:31 +00:00
|
|
|
// Unknown == error
|
2018-01-12 10:01:35 +00:00
|
|
|
var doneMap = map[string]string{
|
2018-06-19 19:50:22 +00:00
|
|
|
"Completed": "activating",
|
|
|
|
"Ready": "unavailable",
|
|
|
|
"Available": "updating",
|
|
|
|
"Progressing": "inactive",
|
2018-03-23 23:22:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// True == transitioning
|
|
|
|
// False ==
|
|
|
|
// Unknown ==
|
2018-03-24 06:02:31 +00:00
|
|
|
var progressMap = map[string]string{}
|
2018-01-12 10:01:35 +00:00
|
|
|
|
|
|
|
func concat(str, next string) string {
|
|
|
|
if str == "" {
|
|
|
|
return next
|
|
|
|
}
|
|
|
|
if next == "" {
|
|
|
|
return str
|
|
|
|
}
|
2018-07-25 08:43:08 +00:00
|
|
|
if strings.EqualFold(str, next) {
|
|
|
|
return str
|
|
|
|
}
|
2018-03-31 10:15:08 +00:00
|
|
|
return str + "; " + next
|
2017-11-15 23:59:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func Set(data map[string]interface{}) {
|
2018-04-17 21:48:46 +00:00
|
|
|
genericStatus(data)
|
2018-06-05 04:44:54 +00:00
|
|
|
loadBalancerStatus(data)
|
2018-04-17 21:48:46 +00:00
|
|
|
}
|
|
|
|
|
2018-06-05 04:44:54 +00:00
|
|
|
func loadBalancerStatus(data map[string]interface{}) {
|
2018-04-17 21:48:46 +00:00
|
|
|
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"
|
2018-06-19 19:50:22 +00:00
|
|
|
data["transitioningMessage"] = "Load balancer is being provisioned"
|
2018-04-17 21:48:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func genericStatus(data map[string]interface{}) {
|
2017-12-20 04:47:20 +00:00
|
|
|
if data == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-02-20 19:42:31 +00:00
|
|
|
val, conditionsOk := values.GetValue(data, "status", "conditions")
|
|
|
|
var conditions []condition
|
|
|
|
convert.ToObj(val, &conditions)
|
|
|
|
|
2018-03-05 05:02:09 +00:00
|
|
|
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...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-15 23:59:47 +00:00
|
|
|
state := ""
|
|
|
|
error := false
|
|
|
|
transitioning := false
|
|
|
|
message := ""
|
|
|
|
|
2018-01-12 10:01:35 +00:00
|
|
|
for _, c := range conditions {
|
2018-06-05 04:44:54 +00:00
|
|
|
if (errorMapping[c.Type] && c.Status == "False") || c.Reason == "Error" {
|
2018-01-12 10:01:35 +00:00
|
|
|
error = true
|
|
|
|
message = c.Message
|
|
|
|
break
|
2017-11-15 23:59:47 +00:00
|
|
|
}
|
2018-01-12 10:01:35 +00:00
|
|
|
}
|
2017-11-15 23:59:47 +00:00
|
|
|
|
2018-01-12 10:01:35 +00:00
|
|
|
if !error {
|
|
|
|
for _, c := range conditions {
|
|
|
|
if reverseErrorMap[c.Type] && c.Status == "True" {
|
|
|
|
error = true
|
|
|
|
message = concat(message, c.Message)
|
2017-12-27 16:48:28 +00:00
|
|
|
}
|
2017-11-15 23:59:47 +00:00
|
|
|
}
|
2018-01-12 10:01:35 +00:00
|
|
|
}
|
2017-11-15 23:59:47 +00:00
|
|
|
|
2018-01-12 10:01:35 +00:00
|
|
|
for _, c := range conditions {
|
|
|
|
newState, ok := transitioningMap[c.Type]
|
|
|
|
if !ok {
|
|
|
|
continue
|
2017-11-15 23:59:47 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 10:01:35 +00:00
|
|
|
if c.Status == "False" {
|
2017-11-15 23:59:47 +00:00
|
|
|
error = true
|
2018-01-12 10:01:35 +00:00
|
|
|
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)
|
2018-02-20 19:42:31 +00:00
|
|
|
} else if c.Status == "Unknown" {
|
|
|
|
error = true
|
|
|
|
state = newState
|
|
|
|
message = concat(message, c.Message)
|
2017-11-15 23:59:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-23 23:22:50 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-15 23:59:47 +00:00
|
|
|
if state == "" {
|
2018-01-08 17:01:46 +00:00
|
|
|
val, ok := values.GetValue(data, "spec", "active")
|
|
|
|
if ok {
|
|
|
|
if convert.ToBool(val) {
|
|
|
|
state = "active"
|
|
|
|
} else {
|
|
|
|
state = "inactive"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-26 23:56:50 +00:00
|
|
|
phase, ok := values.GetValueN(data, "status", "phase").(string)
|
|
|
|
if phase != "" && ok {
|
|
|
|
if phase == "Succeeded" {
|
|
|
|
state = "succeeded"
|
2018-04-27 20:03:19 +00:00
|
|
|
transitioning = false
|
2018-04-26 23:56:50 +00:00
|
|
|
} else if state == "" {
|
|
|
|
state = phase
|
2018-01-08 17:01:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-30 15:52:55 +00:00
|
|
|
apiVersion, _ := values.GetValueN(data, "apiVersion").(string)
|
2018-02-01 22:15:48 +00:00
|
|
|
if state == "" && conditionsOk && len(conditions) == 0 && strings.Contains(apiVersion, "cattle.io") {
|
2018-01-04 23:39:06 +00:00
|
|
|
if val, ok := values.GetValue(data, "metadata", "created"); ok {
|
|
|
|
if i, err := convert.ToTimestamp(val); err == nil {
|
2018-01-12 10:01:35 +00:00
|
|
|
if time.Unix(i/1000, 0).Add(5 * time.Second).After(time.Now()) {
|
2018-01-08 17:01:46 +00:00
|
|
|
state = "initializing"
|
|
|
|
transitioning = true
|
2018-01-04 23:39:06 +00:00
|
|
|
}
|
2017-12-14 20:49:44 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-08 17:01:46 +00:00
|
|
|
}
|
2017-12-14 20:49:44 +00:00
|
|
|
|
2018-01-08 17:01:46 +00:00
|
|
|
if state == "" {
|
|
|
|
state = "active"
|
2017-11-15 23:59:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if error {
|
|
|
|
data["transitioning"] = "error"
|
|
|
|
} else if transitioning {
|
|
|
|
data["transitioning"] = "yes"
|
|
|
|
} else {
|
|
|
|
data["transitioning"] = "no"
|
|
|
|
}
|
|
|
|
|
2018-01-17 01:06:45 +00:00
|
|
|
data["state"] = strings.ToLower(state)
|
2017-11-15 23:59:47 +00:00
|
|
|
data["transitioningMessage"] = message
|
2018-03-23 23:22:50 +00:00
|
|
|
|
2018-04-26 23:56:50 +00:00
|
|
|
val, ok = values.GetValue(data, "metadata", "removed")
|
2018-03-23 23:22:50 +00:00
|
|
|
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]
|
|
|
|
|
2018-06-19 19:50:22 +00:00
|
|
|
if f == "foregroundDeletion" {
|
|
|
|
f = "object cleanup"
|
|
|
|
}
|
|
|
|
|
2018-03-23 23:22:50 +00:00
|
|
|
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
|
|
|
|
}
|
2017-11-15 23:59:47 +00:00
|
|
|
}
|