Skip to content

Commit

Permalink
Add summary
Browse files Browse the repository at this point in the history
  • Loading branch information
ibuildthecloud committed Feb 14, 2020
1 parent 5eddba9 commit 8321517
Show file tree
Hide file tree
Showing 3 changed files with 338 additions and 0 deletions.
45 changes: 45 additions & 0 deletions pkg/summary/condition.go
@@ -0,0 +1,45 @@
package summary

import (
"encoding/json"

"github.com/rancher/wrangler/pkg/data"
)

func getRawConditions(obj data.Object) []data.Object {
statusAnn := obj.String("metadata", "annotations", "cattle.io/status")
if statusAnn != "" {
status := data.Object{}
if err := json.Unmarshal([]byte(statusAnn), &status); err == nil {
return append(obj.Slice("status", "conditions"), status.Slice("conditions")...)
}
}
return obj.Slice("status", "conditions")
}

func getConditions(obj data.Object) (result []Condition) {
for _, condition := range getRawConditions(obj) {
result = append(result, Condition{d: condition})
}
return
}

type Condition struct {
d data.Object
}

func (c Condition) Type() string {
return c.d.String("type")
}

func (c Condition) Status() string {
return c.d.String("status")
}

func (c Condition) Reason() string {
return c.d.String("reason")
}

func (c Condition) Message() string {
return c.d.String("message")
}
257 changes: 257 additions & 0 deletions pkg/summary/summarizers.go
@@ -0,0 +1,257 @@
package summary

import (
"strings"
"time"

"github.com/rancher/wrangler/pkg/data"
"github.com/rancher/wrangler/pkg/data/convert"
"github.com/rancher/wrangler/pkg/kv"
)

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",
"Waiting": "waiting",
"InitialRolesPopulated": "activating",
"ScalingActive": "pending",
"AbleToScale": "pending",
"RunCompleted": "running",
}

// 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{
checkErrors,
checkTransitioning,
checkActive,
checkPhase,
checkInitializing,
checkRemoving,
checkLoadBalancer,
}
}

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())
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" {
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
}
36 changes: 36 additions & 0 deletions pkg/summary/summary.go
@@ -0,0 +1,36 @@
package summary

import (
"github.com/rancher/wrangler/pkg/data"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

type Summary struct {
State string
Error bool
Transitioning bool
Message []string
}

func Summarize(unstr *unstructured.Unstructured) Summary {
var (
obj data.Object
summary Summary
)

if unstr != nil {
obj = unstr.Object
}

conditions := getConditions(obj)

for _, summarizer := range Summarizers {
summary = summarizer(obj, conditions, summary)
}

if summary.State == "" {
summary.State = "active"
}

return summary
}

0 comments on commit 8321517

Please sign in to comment.