Skip to content

Commit 5e39b39

Browse files
Merge pull request #903 from typeid/fix/transfer-owner-manifestwork-race
Fix race condition in transfer-owner ManifestWork update for HCP
2 parents f22b6f1 + bb0a099 commit 5e39b39

2 files changed

Lines changed: 295 additions & 55 deletions

File tree

cmd/cluster/transferowner.go

Lines changed: 63 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1919
"k8s.io/apimachinery/pkg/types"
2020
"k8s.io/client-go/kubernetes"
21+
"k8s.io/client-go/util/retry"
2122
workv1 "open-cluster-management.io/api/work/v1"
2223
"sigs.k8s.io/controller-runtime/pkg/client"
2324

@@ -321,12 +322,9 @@ func updateManifestWork(conn *sdk.Connection, kubeCli client.Client, clusterID,
321322
return fmt.Errorf("failed to get cluster: %w", err)
322323
}
323324

324-
// Use domain prefix here instead of hostedcluster.Name, since the pull secret will follow the domain prefix
325325
secretNamePrefix := hostedCluster.DomainPrefix() + "-pull"
326326

327-
// Generate a random new secret name based on the existing pull secret name
328327
randomSuffix := func(chars string, length int) string {
329-
rand.Seed(time.Now().UnixNano())
330328
result := make([]byte, length)
331329
for i := range result {
332330
result[i] = chars[rand.Intn(len(chars))]
@@ -335,74 +333,84 @@ func updateManifestWork(conn *sdk.Connection, kubeCli client.Client, clusterID,
335333
}
336334
newSecretName := secretNamePrefix + "-" + randomSuffix("0123456789abcdef", 6)
337335

338-
manifestWork := &workv1.ManifestWork{}
339-
err = kubeCli.Get(context.TODO(), types.NamespacedName{Name: manifestWorkName, Namespace: manifestWorkNamespace}, manifestWork)
336+
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
337+
defer cancel()
338+
339+
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
340+
manifestWork := &workv1.ManifestWork{}
341+
if err := kubeCli.Get(ctx, types.NamespacedName{Name: manifestWorkName, Namespace: manifestWorkNamespace}, manifestWork); err != nil {
342+
return fmt.Errorf("failed to get the target manifestwork for given cluster %v: %w", clusterID, err)
343+
}
344+
345+
if err := updateManifestWorkPayloads(manifestWork, secretNamePrefix, newSecretName, pullsecret); err != nil {
346+
return err
347+
}
348+
349+
return kubeCli.Update(ctx, manifestWork, &client.UpdateOptions{})
350+
})
340351
if err != nil {
341-
return fmt.Errorf("failed to get the target manifestwork for given cluster %v: %w", clusterID, err)
352+
return fmt.Errorf("cannot update the pull-secret within manifestwork: %w", err)
342353
}
343354

344-
for i, manifest := range manifestWork.Spec.Workload.Manifests {
345-
if manifest.Raw != nil {
346-
var manifestData map[string]interface{}
347-
err := json.Unmarshal(manifest.Raw, &manifestData)
348-
if err != nil {
349-
return err
350-
}
355+
fmt.Println("waiting 60 seconds for secret to sync on guest cluster")
356+
select {
357+
case <-ctx.Done():
358+
return fmt.Errorf("context cancelled while waiting for secret sync: %w", ctx.Err())
359+
case <-time.After(60 * time.Second):
360+
}
351361

352-
jsonData, err := json.Marshal(manifestData)
353-
if err != nil {
362+
return nil
363+
}
364+
365+
func updateManifestWorkPayloads(mw *workv1.ManifestWork, secretNamePrefix, newSecretName string, pullsecret []byte) error {
366+
for i, manifest := range mw.Spec.Workload.Manifests {
367+
if manifest.Raw == nil {
368+
continue
369+
}
370+
371+
var meta struct {
372+
Kind string `json:"kind"`
373+
}
374+
if err := json.Unmarshal(manifest.Raw, &meta); err != nil {
375+
return err
376+
}
377+
378+
switch meta.Kind {
379+
case "Secret":
380+
secret := &corev1.Secret{}
381+
if err := json.Unmarshal(manifest.Raw, secret); err != nil {
354382
return err
355383
}
356-
357-
if manifestData["kind"] == "Secret" {
358-
secret := &corev1.Secret{}
359-
err = json.Unmarshal(jsonData, secret)
360-
if err != nil {
361-
return err
384+
if strings.Contains(secret.Name, secretNamePrefix) {
385+
if secret.Data == nil {
386+
secret.Data = map[string][]byte{}
362387
}
363-
if strings.Contains(secret.Name, secretNamePrefix) {
364-
// Firstly, get the ecr auth from the existing pull secret and append it to new pull secret
365-
oldPullSecret := secret.Data[".dockerconfigjson"]
366-
newPullSecret, err := buildNewSecret(oldPullSecret, pullsecret)
367-
if err != nil {
368-
return fmt.Errorf("cannot build the new pull secret: %w", err)
369-
}
370-
// Then, update the secret with new name and new value
371-
secret.Name = newSecretName
372-
secret.Data[".dockerconfigjson"] = newPullSecret
388+
oldPullSecret := secret.Data[".dockerconfigjson"]
389+
newPullSecret, err := buildNewSecret(oldPullSecret, pullsecret)
390+
if err != nil {
391+
return fmt.Errorf("cannot build the new pull secret: %w", err)
373392
}
393+
secret.Name = newSecretName
394+
secret.Data[".dockerconfigjson"] = newPullSecret
374395
secretJson, err := json.Marshal(secret)
375396
if err != nil {
376397
return err
377398
}
378-
manifestWork.Spec.Workload.Manifests[i].Raw = secretJson
399+
mw.Spec.Workload.Manifests[i].Raw = secretJson
379400
}
380-
if manifestData["kind"] == "HostedCluster" {
381-
hc := &hypershiftv1beta1.HostedCluster{}
382-
err = json.Unmarshal(jsonData, hc)
383-
if err != nil {
384-
return err
385-
}
386-
// Update the hosted cluster reference secret name to the new name
387-
hc.Spec.PullSecret.Name = newSecretName
388-
hcJson, err := json.Marshal(hc)
389-
if err != nil {
390-
return err
391-
}
392-
manifestWork.Spec.Workload.Manifests[i].Raw = hcJson
401+
case "HostedCluster":
402+
hc := &hypershiftv1beta1.HostedCluster{}
403+
if err := json.Unmarshal(manifest.Raw, hc); err != nil {
404+
return err
393405
}
406+
hc.Spec.PullSecret.Name = newSecretName
407+
hcJson, err := json.Marshal(hc)
408+
if err != nil {
409+
return err
410+
}
411+
mw.Spec.Workload.Manifests[i].Raw = hcJson
394412
}
395413
}
396-
397-
err = kubeCli.Update(context.TODO(), manifestWork, &client.UpdateOptions{})
398-
if err != nil {
399-
return fmt.Errorf("cannot update the pull-secret within manifestwork: %w", err)
400-
}
401-
402-
// The secret will be synced to the management cluster and guest cluster in a few seconds, wait here
403-
fmt.Println("sleep 60 seconds here to make sure secret gets synced on guest cluster")
404-
time.Sleep(time.Second * 60)
405-
406414
return nil
407415
}
408416

0 commit comments

Comments
 (0)