...

Source file src/sigs.k8s.io/cli-utils/pkg/kstatus/polling/event/event.go

Documentation: sigs.k8s.io/cli-utils/pkg/kstatus/polling/event

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package event
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    10  	"sigs.k8s.io/cli-utils/pkg/kstatus/status"
    11  	"sigs.k8s.io/cli-utils/pkg/object"
    12  )
    13  
    14  // Type is the type that describes the type of an Event that is passed back to the caller
    15  // as resources in the cluster are being polled.
    16  //
    17  //go:generate stringer -type=Type -linecomment
    18  type Type int
    19  
    20  const (
    21  	// ResourceUpdateEvent describes events related to a change in the status of one of the polled resources.
    22  	ResourceUpdateEvent Type = iota // Update
    23  	// ErrorEvent signals that the engine has encountered an error that it can not recover from. The engine
    24  	// is shutting down and the event channel will be closed after this event.
    25  	ErrorEvent // Error
    26  	// SyncEvent signals that the engine has completed its initial
    27  	// synchronization, and the cache is primed. After this point, it's safe to
    28  	// assume that you won't miss events caused by your own subsequent actions.
    29  	SyncEvent // Sync
    30  )
    31  
    32  // Event defines that type that is passed back through the event channel to notify the caller of changes
    33  // as resources are being polled.
    34  type Event struct {
    35  	// Type defines the type of event.
    36  	Type Type
    37  
    38  	// Resource is only available for ResourceUpdateEvents. It includes information about the resource,
    39  	// including the resource status, any errors and the resource itself (as an unstructured).
    40  	Resource *ResourceStatus
    41  
    42  	// Error is only available for ErrorEvents. It contains the error that caused the engine to
    43  	// give up.
    44  	Error error
    45  }
    46  
    47  // String returns a string suitable for logging
    48  func (e Event) String() string {
    49  	if e.Error != nil {
    50  		return fmt.Sprintf("Event{ Type: %q, Resource: %v, Error: %q }",
    51  			e.Type, e.Resource, e.Error)
    52  	}
    53  	return fmt.Sprintf("Event{ Type: %q, Resource: %v }",
    54  		e.Type, e.Resource)
    55  }
    56  
    57  // ResourceStatus contains information about a resource after we have
    58  // fetched it from the cluster and computed status.
    59  type ResourceStatus struct {
    60  	// Identifier contains the information necessary to locate the
    61  	// resource within a cluster.
    62  	Identifier object.ObjMetadata
    63  
    64  	// Status is the computed status for this resource.
    65  	Status status.Status
    66  
    67  	// Resource contains the actual manifest for the resource that
    68  	// was fetched from the cluster and used to compute status.
    69  	Resource *unstructured.Unstructured
    70  
    71  	// Errors contains the error if something went wrong during the
    72  	// process of fetching the resource and computing the status.
    73  	Error error
    74  
    75  	// Message is text describing the status of the resource.
    76  	Message string
    77  
    78  	// GeneratedResources is a slice of ResourceStatus that
    79  	// contains information and status for any generated resources
    80  	// of the current resource.
    81  	GeneratedResources ResourceStatuses
    82  }
    83  
    84  // String returns a string suitable for logging
    85  func (rs ResourceStatus) String() string {
    86  	if rs.Error != nil {
    87  		return fmt.Sprintf("ResourceStatus{ Identifier: %q, Status: %q, Message: %q, Resource: %v, GeneratedResources: %v, Error: %q }",
    88  			rs.Identifier, rs.Status, rs.Message, rs.Resource, rs.GeneratedResources, rs.Error)
    89  	}
    90  	return fmt.Sprintf("ResourceStatus{ Identifier: %q, Status: %q, Message: %q, Resource: %v, GeneratedResources: %v }",
    91  		rs.Identifier, rs.Status, rs.Message, rs.Resource, rs.GeneratedResources)
    92  }
    93  
    94  type ResourceStatuses []*ResourceStatus
    95  
    96  func (g ResourceStatuses) Len() int {
    97  	return len(g)
    98  }
    99  
   100  func (g ResourceStatuses) Less(i, j int) bool {
   101  	idI := g[i].Identifier
   102  	idJ := g[j].Identifier
   103  
   104  	if idI.Namespace != idJ.Namespace {
   105  		return idI.Namespace < idJ.Namespace
   106  	}
   107  	if idI.GroupKind.Group != idJ.GroupKind.Group {
   108  		return idI.GroupKind.Group < idJ.GroupKind.Group
   109  	}
   110  	if idI.GroupKind.Kind != idJ.GroupKind.Kind {
   111  		return idI.GroupKind.Kind < idJ.GroupKind.Kind
   112  	}
   113  	return idI.Name < idJ.Name
   114  }
   115  
   116  func (g ResourceStatuses) Swap(i, j int) {
   117  	g[i], g[j] = g[j], g[i]
   118  }
   119  
   120  // ResourceStatusEqual checks if two instances of ResourceStatus are the same.
   121  // This is used to determine whether status has changed for a particular resource.
   122  // Important to note that this does not check all fields, but only the ones
   123  // that are considered part of the status for a resource. So if the status
   124  // or the message of an ResourceStatus (or any of its generated ResourceStatuses)
   125  // have changed, this will return true. Changes to the state of the resource
   126  // itself that doesn't impact status are not considered.
   127  func ResourceStatusEqual(or1, or2 *ResourceStatus) bool {
   128  	if or1.Identifier != or2.Identifier ||
   129  		or1.Status != or2.Status ||
   130  		or1.Message != or2.Message {
   131  		return false
   132  	}
   133  
   134  	// Check if generation has changed to make sure that even if
   135  	// an update to a resource doesn't affect the status, a status event
   136  	// will still be sent.
   137  	if getGeneration(or1) != getGeneration(or2) {
   138  		return false
   139  	}
   140  
   141  	if or1.Error != nil && or2.Error != nil && or1.Error.Error() != or2.Error.Error() {
   142  		return false
   143  	}
   144  	if (or1.Error == nil && or2.Error != nil) || (or1.Error != nil && or2.Error == nil) {
   145  		return false
   146  	}
   147  
   148  	if len(or1.GeneratedResources) != len(or2.GeneratedResources) {
   149  		return false
   150  	}
   151  
   152  	for i := range or1.GeneratedResources {
   153  		if !ResourceStatusEqual(or1.GeneratedResources[i], or2.GeneratedResources[i]) {
   154  			return false
   155  		}
   156  	}
   157  	return true
   158  }
   159  
   160  func getGeneration(r *ResourceStatus) int64 {
   161  	if r.Resource == nil {
   162  		return 0
   163  	}
   164  	return r.Resource.GetGeneration()
   165  }
   166  

View as plain text