cli for status

This commit is contained in:
Morten Torkildsen
2019-12-05 08:57:40 -08:00
parent 54b1549586
commit 1b3b8522f9
15 changed files with 1490 additions and 54 deletions

View File

@@ -36,29 +36,9 @@ func checkGenericProperties(u *unstructured.Unstructured) (*Result, error) {
}, nil
}
// ensure that the meta generation is observed
generation, found, err := unstructured.NestedInt64(u.Object, "metadata", "generation")
if err != nil {
return nil, errors.Wrap(err, "looking up metadata.generation from resource")
}
if !found {
return nil, errors.New("unable to find metadata.generation from resource")
}
observedGeneration, found, err := unstructured.NestedInt64(u.Object, "status", "observedGeneration")
if err != nil {
return nil, errors.Wrap(err, "looking up status.observedGeneration from resource")
}
if found {
// Resource does not have this field, so we can't do this check.
// TODO(mortent): Verify behavior of not set vs does not exist.
if observedGeneration != generation {
message := fmt.Sprintf("%s generation is %d, but latest observed generation is %d", u.GetKind(), generation, observedGeneration)
return &Result{
Status: InProgressStatus,
Message: message,
Conditions: []Condition{newInProgressCondition("LatestGenerationNotObserved", message)},
}, nil
}
res, err := checkGeneration(u)
if res != nil || err != nil {
return res, err
}
// Check if the resource has any of the standard conditions. If so, we just use them
@@ -90,3 +70,31 @@ func checkGenericProperties(u *unstructured.Unstructured) (*Result, error) {
return nil, nil
}
func checkGeneration(u *unstructured.Unstructured) (*Result, error) {
// ensure that the meta generation is observed
generation, found, err := unstructured.NestedInt64(u.Object, "metadata", "generation")
if err != nil {
return nil, errors.Wrap(err, "looking up metadata.generation from resource")
}
if !found {
return nil, nil
}
observedGeneration, found, err := unstructured.NestedInt64(u.Object, "status", "observedGeneration")
if err != nil {
return nil, errors.Wrap(err, "looking up status.observedGeneration from resource")
}
if found {
// Resource does not have this field, so we can't do this check.
// TODO(mortent): Verify behavior of not set vs does not exist.
if observedGeneration != generation {
message := fmt.Sprintf("%s generation is %d, but latest observed generation is %d", u.GetKind(), generation, observedGeneration)
return &Result{
Status: InProgressStatus,
Message: message,
Conditions: []Condition{newInProgressCondition("LatestGenerationNotObserved", message)},
}, nil
}
}
return nil, nil
}

View File

@@ -184,6 +184,12 @@ func (r *Resolver) WaitForStatus(ctx context.Context, resources []ResourceIdenti
// resources while polling the state.
waitState := newWaitState(resources, r.statusComputeFunc)
// Check all resources immediately. If the aggregate status is already
// Current, we can exit immediately.
if r.checkAllResources(ctx, waitState, eventChan) {
return
}
// Loop until either all resources have reached the Current status
// or until the wait is cancelled through the context. In both cases
// we will break out of the loop by returning from the function.
@@ -199,37 +205,11 @@ func (r *Resolver) WaitForStatus(ctx context.Context, resources []ResourceIdenti
}
return
case <-ticker.C:
// Every time the ticker fires, fetch all resources from the cluster,
// check if their status has changed and send an event for each resource
// with a new status. In each event, we also include the latest aggregate
// status. Finally, if the aggregate status becomes Current, send a final
// Completed type event and then return.
for id := range waitState.ResourceWaitStates {
// Make sure we have a local copy since we are passing
// pointers to this variable as parameters to functions
identifier := id
u, err := r.fetchResource(ctx, &identifier)
eventResource, updateObserved := waitState.ResourceObserved(&identifier, u, err)
// Find the aggregate status based on the new state for this resource.
aggStatus := waitState.AggregateStatus()
// We want events for changes in status for each resource, so send
// an event for this resource before checking if the aggregate status
// has become Current.
if updateObserved {
eventChan <- Event{
Type: ResourceUpdate,
AggregateStatus: aggStatus,
EventResource: &eventResource,
}
}
// If aggregate status is Current, we are done!
if aggStatus == status.CurrentStatus {
eventChan <- Event{
Type: Completed,
AggregateStatus: status.CurrentStatus,
}
return
}
// Every time the ticker fires, we check the status of all
// resources. If the aggregate status has reached Current, checkAllResources
// will return true. If so, we just return.
if r.checkAllResources(ctx, waitState, eventChan) {
return
}
}
}
@@ -238,6 +218,43 @@ func (r *Resolver) WaitForStatus(ctx context.Context, resources []ResourceIdenti
return eventChan
}
// checkAllResources fetches all resources from the cluster,
// checks if their status has changed and send an event for each resource
// with a new status. In each event, we also include the latest aggregate
// status. Finally, if the aggregate status becomes Current, send a final
// Completed type event. If the aggregate status has become Current, this function
// will return true to signal that it is done.
func (r *Resolver) checkAllResources(ctx context.Context, waitState *waitState, eventChan chan Event) bool {
for id := range waitState.ResourceWaitStates {
// Make sure we have a local copy since we are passing
// pointers to this variable as parameters to functions
identifier := id
u, err := r.fetchResource(ctx, &identifier)
eventResource, updateObserved := waitState.ResourceObserved(&identifier, u, err)
// Find the aggregate status based on the new state for this resource.
aggStatus := waitState.AggregateStatus()
// We want events for changes in status for each resource, so send
// an event for this resource before checking if the aggregate status
// has become Current.
if updateObserved {
eventChan <- Event{
Type: ResourceUpdate,
AggregateStatus: aggStatus,
EventResource: &eventResource,
}
}
// If aggregate status is Current, we are done!
if aggStatus == status.CurrentStatus {
eventChan <- Event{
Type: Completed,
AggregateStatus: status.CurrentStatus,
}
return true
}
}
return false
}
// fetchResource gets the resource given by the identifier from the cluster
// through the client available in the Resolver. It returns the resource
// as an Unstructured.