in tools/draincluster/drain/drain.go [40:126]
func (h *Helper) Drain(ctx context.Context) (bool, error) {
if err := h.cordon(ctx); err != nil {
return false, fmt.Errorf("failed to cordon member cluster %s: %w", h.ClusterName, err)
}
log.Printf("Successfully cordoned member cluster %s by adding cordon taint", h.ClusterName)
crpNameMap, err := h.fetchClusterResourcePlacementNamesToEvict(ctx)
if err != nil {
return false, err
}
if len(crpNameMap) == 0 {
log.Printf("There are currently no resources propagated to %s from fleet using ClusterResourcePlacement resources", h.ClusterName)
return true, nil
}
isDrainSuccessful := true
// create eviction objects for all <crpName, targetCluster>.
for crpName := range crpNameMap {
evictionName, err := generateDrainEvictionName(crpName, h.ClusterName)
if err != nil {
return false, err
}
err = retry.OnError(retry.DefaultBackoff, func(err error) bool {
return k8errors.IsAlreadyExists(err)
}, func() error {
eviction := placementv1beta1.ClusterResourcePlacementEviction{
ObjectMeta: metav1.ObjectMeta{
Name: evictionName,
},
Spec: placementv1beta1.PlacementEvictionSpec{
PlacementName: crpName,
ClusterName: h.ClusterName,
},
}
return h.HubClient.Create(ctx, &eviction)
})
if err != nil {
return false, fmt.Errorf("failed to create eviction for CRP %s: %w", crpName, err)
}
// wait until evictions reach a terminal state.
var eviction placementv1beta1.ClusterResourcePlacementEviction
err = wait.ExponentialBackoffWithContext(ctx, retry.DefaultBackoff, func(ctx context.Context) (bool, error) {
if err := h.HubClient.Get(ctx, types.NamespacedName{Name: evictionName}, &eviction); err != nil {
return false, fmt.Errorf("failed to get eviction %s: %w", evictionName, err)
}
return evictionutils.IsEvictionInTerminalState(&eviction), nil
})
if err != nil {
return false, fmt.Errorf("failed to wait for evictions to reach terminal state: %w", err)
}
// TODO: add safeguards to check if eviction conditions are set to unknown.
validCondition := eviction.GetCondition(string(placementv1beta1.PlacementEvictionConditionTypeValid))
if validCondition != nil && validCondition.Status == metav1.ConditionFalse {
// check to see if CRP is missing or CRP is being deleted or CRB is missing.
if validCondition.Reason == condition.EvictionInvalidMissingCRPMessage ||
validCondition.Reason == condition.EvictionInvalidDeletingCRPMessage ||
validCondition.Reason == condition.EvictionInvalidMissingCRBMessage {
log.Printf("eviction %s is invalid with reason %s for CRP %s, but drain will succeed", evictionName, validCondition.Reason, crpName)
continue
}
}
executedCondition := eviction.GetCondition(string(placementv1beta1.PlacementEvictionConditionTypeExecuted))
if executedCondition == nil || executedCondition.Status == metav1.ConditionFalse {
isDrainSuccessful = false
log.Printf("eviction %s was not executed successfully for CRP %s", evictionName, crpName)
continue
}
log.Printf("eviction %s was executed successfully for CRP %s", evictionName, crpName)
// log each cluster scoped resource evicted for CRP.
clusterScopedResourceIdentifiers, err := h.collectClusterScopedResourcesSelectedByCRP(ctx, crpName)
if err != nil {
log.Printf("failed to collect cluster scoped resources selected by CRP %s: %v", crpName, err)
continue
}
for _, resourceIdentifier := range clusterScopedResourceIdentifiers {
log.Printf("evicted resource %s propagated by CRP %s", generateResourceIdentifierKey(resourceIdentifier), crpName)
}
}
return isDrainSuccessful, nil
}