Skip to content

Commit 8931bee

Browse files
author
Arvind Thirumurugan
committed
refactor
1 parent 45a869c commit 8931bee

File tree

1 file changed

+107
-46
lines changed

1 file changed

+107
-46
lines changed

tools/cordon-cluster/main/main.go

Lines changed: 107 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"flag"
66
"fmt"
77
"log"
8+
"os"
89

910
k8errors "k8s.io/apimachinery/pkg/api/errors"
1011
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -26,9 +27,16 @@ var (
2627
)
2728

2829
const (
29-
testEvictionNameFormat = "test-eviction-%s-%s"
30+
testEvictionNameFormat = "test-eviction-%s-%s"
31+
resourceIdentifierKeyFormat = "%s/%s/%s/%s/%s"
3032
)
3133

34+
type DrainCluster struct {
35+
hubClient client.Client
36+
ClusterName string
37+
ClusterResourcePlacementResourcesMap map[string][]placementv1beta1.ResourceIdentifier
38+
}
39+
3240
func main() {
3341
hubClusterContext := flag.String("hubClusterContext", "", "the kubectl context for the hub cluster")
3442
clusterName := flag.String("clusterName", "", "name of the cluster to cordon")
@@ -53,65 +61,43 @@ func main() {
5361
log.Fatalf("failed to create hub cluster client: %v", err)
5462
}
5563

56-
var mc clusterv1beta1.MemberCluster
57-
if err = hubClient.Get(ctx, types.NamespacedName{Name: *clusterName}, &mc); err != nil {
58-
log.Fatalf("failed to get member cluster %s: %v", *clusterName, err)
64+
drainCluster := DrainCluster{
65+
hubClient: hubClient,
66+
ClusterName: *clusterName,
67+
ClusterResourcePlacementResourcesMap: make(map[string][]placementv1beta1.ResourceIdentifier),
68+
}
69+
70+
if err = drainCluster.cordonCluster(); err != nil {
71+
log.Fatalf("failed to cordon member cluster %s: %v", drainCluster.ClusterName, err)
5972
}
6073

6174
var crpList placementv1beta1.ClusterResourcePlacementList
62-
if err = hubClient.List(ctx, &crpList); err != nil {
75+
if err = drainCluster.hubClient.List(ctx, &crpList); err != nil {
6376
log.Fatalf("failed to list cluster resource placements: %v", err)
6477
}
6578

6679
// find all unique CRP names for which eviction needs to occur.
67-
crpNameMap := make(map[string]bool)
6880
for i := range crpList.Items {
69-
for j := range crpList.Items[i].Status.PlacementStatuses {
70-
if crpList.Items[i].Status.PlacementStatuses[j].ClusterName == *clusterName {
71-
crpNameMap[crpList.Items[i].Name] = true
81+
crpStatus := crpList.Items[i].Status
82+
for j := range crpStatus.PlacementStatuses {
83+
placementStatus := crpStatus.PlacementStatuses[j]
84+
if placementStatus.ClusterName == *clusterName {
85+
drainCluster.ClusterResourcePlacementResourcesMap[crpList.Items[i].Name] = resourcePropagatedByCRP(crpStatus.SelectedResources, placementStatus.FailedPlacements)
7286
break
7387
}
7488
}
7589
}
7690

77-
if len(crpNameMap) == 0 {
78-
log.Fatalf("failed to find any CRP which has propagated resource to cluster %s", *clusterName)
79-
}
80-
81-
// add taint to member cluster to ensure resources aren't scheduled on it.
82-
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
83-
var mc clusterv1beta1.MemberCluster
84-
if err := hubClient.Get(ctx, types.NamespacedName{Name: *clusterName}, &mc); err != nil {
85-
return err
86-
}
87-
cordonTaint := clusterv1beta1.Taint{
88-
Key: "cordon-key",
89-
Value: "cordon-value",
90-
Effect: "NoSchedule",
91-
}
92-
93-
// search to see cordonTaint already exists on the cluster.
94-
for i := range mc.Spec.Taints {
95-
if mc.Spec.Taints[i].Key == cordonTaint.Key {
96-
return nil
97-
}
98-
}
99-
100-
// add taint to member cluster to cordon.
101-
mc.Spec.Taints = append(mc.Spec.Taints, cordonTaint)
102-
103-
return hubClient.Update(ctx, &mc)
104-
})
105-
106-
if err != nil {
107-
log.Fatalf("failed to update member cluster with taint: %v", err)
91+
if len(drainCluster.ClusterResourcePlacementResourcesMap) == 0 {
92+
log.Printf("There are no resources propagated to %s from fleet using ClusterResourcePlacement resources", *clusterName)
93+
os.Exit(0)
10894
}
10995

11096
// create eviction objects for all <crpName, targetCluster>.
111-
for crpName := range crpNameMap {
97+
for crpName := range drainCluster.ClusterResourcePlacementResourcesMap {
11298
evictionName := fmt.Sprintf(testEvictionNameFormat, crpName, *clusterName)
11399

114-
if err = removeExistingEviction(ctx, hubClient, evictionName); err != nil {
100+
if err = removeExistingEviction(ctx, drainCluster.hubClient, evictionName); err != nil {
115101
log.Fatalf("failed to remove existing eviction for CRP %s", crpName)
116102
}
117103

@@ -127,7 +113,7 @@ func main() {
127113
ClusterName: *clusterName,
128114
},
129115
}
130-
return hubClient.Create(ctx, &eviction)
116+
return drainCluster.hubClient.Create(ctx, &eviction)
131117
})
132118

133119
if err != nil {
@@ -137,11 +123,11 @@ func main() {
137123

138124
// TODO: move isEvictionInTerminalState to a function in the pkg/utils package.
139125
// wait until all evictions reach a terminal state.
140-
for crpName := range crpNameMap {
126+
for crpName := range drainCluster.ClusterResourcePlacementResourcesMap {
141127
err = wait.ExponentialBackoffWithContext(ctx, retry.DefaultBackoff, func(ctx context.Context) (bool, error) {
142128
evictionName := fmt.Sprintf(testEvictionNameFormat, crpName, *clusterName)
143129
eviction := placementv1beta1.ClusterResourcePlacementEviction{}
144-
if err := hubClient.Get(ctx, types.NamespacedName{Name: evictionName}, &eviction); err != nil {
130+
if err := drainCluster.hubClient.Get(ctx, types.NamespacedName{Name: evictionName}, &eviction); err != nil {
145131
return false, err
146132
}
147133
validCondition := eviction.GetCondition(string(placementv1beta1.PlacementEvictionConditionTypeValid))
@@ -160,7 +146,82 @@ func main() {
160146
}
161147
}
162148

163-
log.Printf("Issued evictions to cordon member cluster %s, please verify to ensure evictions were successfully executed", *clusterName)
149+
isDrainSuccessful := true
150+
// check if all evictions have been executed.
151+
for crpName := range drainCluster.ClusterResourcePlacementResourcesMap {
152+
evictionName := fmt.Sprintf(testEvictionNameFormat, crpName, *clusterName)
153+
eviction := placementv1beta1.ClusterResourcePlacementEviction{}
154+
if err := drainCluster.hubClient.Get(ctx, types.NamespacedName{Name: evictionName}, &eviction); err != nil {
155+
log.Fatalf("failed to get eviction %s: %v", evictionName, err)
156+
}
157+
executedCondition := eviction.GetCondition(string(placementv1beta1.PlacementEvictionConditionTypeExecuted))
158+
if executedCondition == nil || executedCondition.Status == metav1.ConditionFalse {
159+
isDrainSuccessful = false
160+
log.Fatalf("eviction %s was not executed successfully for CRP %s", evictionName, crpName)
161+
}
162+
log.Printf("eviction %s was executed successfully for CRP %s", evictionName, crpName)
163+
// log each resource evicted by CRP.
164+
for i := range drainCluster.ClusterResourcePlacementResourcesMap[crpName] {
165+
resourceIdentifier := drainCluster.ClusterResourcePlacementResourcesMap[crpName][i]
166+
log.Printf("evicted resource %s propagated by CRP %s", fmt.Sprintf(resourceIdentifierKeyFormat, resourceIdentifier.Group, resourceIdentifier.Version, resourceIdentifier.Kind, resourceIdentifier.Name, resourceIdentifier.Namespace), crpName)
167+
}
168+
}
169+
170+
if isDrainSuccessful {
171+
log.Printf("drain was successful for cluster %s", *clusterName)
172+
} else {
173+
log.Printf("drain was not successful for cluster %s, some eviction were not successfully executed", *clusterName)
174+
}
175+
}
176+
177+
func (d *DrainCluster) cordonCluster() error {
178+
// add taint to member cluster to ensure resources aren't scheduled on it.
179+
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
180+
var mc clusterv1beta1.MemberCluster
181+
if err := d.hubClient.Get(ctx, types.NamespacedName{Name: d.ClusterName}, &mc); err != nil {
182+
return err
183+
}
184+
cordonTaint := clusterv1beta1.Taint{
185+
Key: "cordon-key",
186+
Value: "cordon-value",
187+
Effect: "NoSchedule",
188+
}
189+
190+
// search to see cordonTaint already exists on the cluster.
191+
for i := range mc.Spec.Taints {
192+
if mc.Spec.Taints[i].Key == cordonTaint.Key {
193+
return nil
194+
}
195+
}
196+
197+
// add taint to member cluster to cordon.
198+
mc.Spec.Taints = append(mc.Spec.Taints, cordonTaint)
199+
200+
return d.hubClient.Update(ctx, &mc)
201+
})
202+
}
203+
204+
func resourcePropagatedByCRP(selectedResources []placementv1beta1.ResourceIdentifier, failedPlacements []placementv1beta1.FailedResourcePlacement) []placementv1beta1.ResourceIdentifier {
205+
selectedResourcesMap := make(map[string]placementv1beta1.ResourceIdentifier, len(selectedResources))
206+
for i := range selectedResources {
207+
selectedResourcesMap[generateResourceIdentifierKey(selectedResources[i])] = selectedResources[i]
208+
}
209+
for i := range failedPlacements {
210+
if failedPlacements[i].Condition.Type == placementv1beta1.WorkConditionTypeApplied {
211+
delete(selectedResourcesMap, generateResourceIdentifierKey(failedPlacements[i].ResourceIdentifier))
212+
}
213+
}
214+
resourcesPropagated := make([]placementv1beta1.ResourceIdentifier, len(selectedResourcesMap))
215+
i := 0
216+
for key := range selectedResourcesMap {
217+
resourcesPropagated[i] = selectedResourcesMap[key]
218+
i++
219+
}
220+
return resourcesPropagated
221+
}
222+
223+
func generateResourceIdentifierKey(resourceIdentifier placementv1beta1.ResourceIdentifier) string {
224+
return fmt.Sprintf(resourceIdentifierKeyFormat, resourceIdentifier.Group, resourceIdentifier.Version, resourceIdentifier.Kind, resourceIdentifier.Name, resourceIdentifier.Namespace)
164225
}
165226

166227
func removeExistingEviction(ctx context.Context, client client.Client, evictionName string) error {

0 commit comments

Comments
 (0)