|
6 | 6 | "context" |
7 | 7 | "crypto/x509" |
8 | 8 | "crypto/x509/pkix" |
| 9 | + "encoding/json" |
9 | 10 | "fmt" |
10 | 11 | "io" |
11 | 12 | "net" |
@@ -1986,6 +1987,108 @@ func EnsureGlobalPullSecret(t *testing.T, ctx context.Context, mgmtClient crclie |
1986 | 1987 | nodeCount := *np.Spec.Replicas |
1987 | 1988 | t.Logf("NodePool replicas: %d", nodeCount) |
1988 | 1989 |
|
| 1990 | + // Extract the DaemonSet image early so it is available for all subtests. |
| 1991 | + g.Eventually(func() error { |
| 1992 | + daemonSet := hccomanifests.GlobalPullSecretDaemonSet() |
| 1993 | + if err := guestClient.Get(ctx, crclient.ObjectKey{Name: daemonSet.Name, Namespace: daemonSet.Namespace}, daemonSet); err != nil { |
| 1994 | + return err |
| 1995 | + } |
| 1996 | + dsImage = daemonSet.Spec.Template.Spec.Containers[0].Image |
| 1997 | + return nil |
| 1998 | + }, 30*time.Second, 5*time.Second).Should(Succeed(), "DaemonSet is not present") |
| 1999 | + |
| 2000 | + // Verify that in-place management-cluster pull secret updates propagate to the guest cluster |
| 2001 | + // without triggering a NodePool rollout. |
| 2002 | + t.Run("When management-cluster hostedCluster.Spec.PullSecret is updated in-place it should propagate to guest without rollout", func(t *testing.T) { |
| 2003 | + CPOAtLeast(t, Version423, entryHostedCluster) |
| 2004 | + g := NewWithT(t) |
| 2005 | + t.Logf("Reading management-cluster pull secret %s/%s", entryHostedCluster.Namespace, entryHostedCluster.Spec.PullSecret.Name) |
| 2006 | + mgmtSecret := &corev1.Secret{} |
| 2007 | + g.Expect(mgmtClient.Get(ctx, crclient.ObjectKey{ |
| 2008 | + Namespace: entryHostedCluster.Namespace, |
| 2009 | + Name: entryHostedCluster.Spec.PullSecret.Name, |
| 2010 | + }, mgmtSecret)).To(Succeed(), "failed to get management-cluster pull secret") |
| 2011 | + |
| 2012 | + originalData := make([]byte, len(mgmtSecret.Data[corev1.DockerConfigJsonKey])) |
| 2013 | + copy(originalData, mgmtSecret.Data[corev1.DockerConfigJsonKey]) |
| 2014 | + |
| 2015 | + t.Cleanup(func() { |
| 2016 | + t.Log("Restoring original management-cluster pull secret data") |
| 2017 | + fresh := &corev1.Secret{} |
| 2018 | + if err := mgmtClient.Get(ctx, crclient.ObjectKey{ |
| 2019 | + Namespace: entryHostedCluster.Namespace, |
| 2020 | + Name: entryHostedCluster.Spec.PullSecret.Name, |
| 2021 | + }, fresh); err != nil { |
| 2022 | + t.Logf("Warning: failed to re-read pull secret for cleanup: %v", err) |
| 2023 | + return |
| 2024 | + } |
| 2025 | + fresh.Data[corev1.DockerConfigJsonKey] = originalData |
| 2026 | + if err := mgmtClient.Update(ctx, fresh); err != nil { |
| 2027 | + t.Logf("Warning: failed to restore pull secret: %v", err) |
| 2028 | + } |
| 2029 | + }) |
| 2030 | + |
| 2031 | + // Merge a dummy auth entry into the pull secret without removing existing auths |
| 2032 | + type dockerConfigJSON struct { |
| 2033 | + Auths map[string]json.RawMessage `json:"auths"` |
| 2034 | + } |
| 2035 | + var cfg dockerConfigJSON |
| 2036 | + g.Expect(json.Unmarshal(originalData, &cfg)).To(Succeed(), "failed to parse pull secret") |
| 2037 | + cfg.Auths["e2e-dummy.example.com"] = json.RawMessage(`{"auth":"dGVzdDp0ZXN0"}`) |
| 2038 | + modifiedData, err := json.Marshal(cfg) |
| 2039 | + g.Expect(err).NotTo(HaveOccurred(), "failed to marshal modified pull secret") |
| 2040 | + |
| 2041 | + t.Log("Patching management-cluster pull secret with dummy auth entry") |
| 2042 | + mgmtSecret.Data[corev1.DockerConfigJsonKey] = modifiedData |
| 2043 | + g.Expect(mgmtClient.Update(ctx, mgmtSecret)).To(Succeed(), "failed to update management-cluster pull secret") |
| 2044 | + |
| 2045 | + // Wait for openshift-config/pull-secret in the guest cluster to pick up the change |
| 2046 | + t.Log("Waiting for openshift-config/pull-secret to update in guest cluster") |
| 2047 | + g.Eventually(func() bool { |
| 2048 | + secret := &corev1.Secret{} |
| 2049 | + if err := guestClient.Get(ctx, crclient.ObjectKey{Name: "pull-secret", Namespace: "openshift-config"}, secret); err != nil { |
| 2050 | + return false |
| 2051 | + } |
| 2052 | + return bytes.Contains(secret.Data[corev1.DockerConfigJsonKey], []byte("e2e-dummy.example.com")) |
| 2053 | + }, 150*time.Second, 5*time.Second).Should(BeTrue(), "openshift-config/pull-secret did not propagate dummy entry") |
| 2054 | + |
| 2055 | + // Wait for kube-system/original-pull-secret to pick up the change (globalps controller path) |
| 2056 | + t.Log("Waiting for kube-system/original-pull-secret to update in guest cluster") |
| 2057 | + g.Eventually(func() bool { |
| 2058 | + secret := hccomanifests.OriginalPullSecret() |
| 2059 | + if err := guestClient.Get(ctx, crclient.ObjectKey{Name: secret.Name, Namespace: secret.Namespace}, secret); err != nil { |
| 2060 | + return false |
| 2061 | + } |
| 2062 | + return bytes.Contains(secret.Data[corev1.DockerConfigJsonKey], []byte("e2e-dummy.example.com")) |
| 2063 | + }, 150*time.Second, 5*time.Second).Should(BeTrue(), "kube-system/original-pull-secret did not propagate dummy entry") |
| 2064 | + |
| 2065 | + // Verify no NodePool rollout was triggered |
| 2066 | + t.Log("Verifying no NodePool rollout was triggered") |
| 2067 | + nodePool := &hyperv1.NodePool{} |
| 2068 | + g.Expect(mgmtClient.Get(ctx, crclient.ObjectKeyFromObject(np), nodePool)).To(Succeed(), "failed to get NodePool") |
| 2069 | + for _, cond := range nodePool.Status.Conditions { |
| 2070 | + if cond.Type == hyperv1.NodePoolUpdatingConfigConditionType { |
| 2071 | + g.Expect(string(cond.Status)).To(Equal(string(metav1.ConditionFalse)), |
| 2072 | + "UpdatingConfig should be False — in-place pull secret update must not trigger a rollout") |
| 2073 | + break |
| 2074 | + } |
| 2075 | + } |
| 2076 | + |
| 2077 | + nodeList := &corev1.NodeList{} |
| 2078 | + g.Expect(guestClient.List(ctx, nodeList, crclient.MatchingLabels{ |
| 2079 | + hyperv1.NodePoolLabel: np.Name, |
| 2080 | + })).To(Succeed(), "failed to list nodes") |
| 2081 | + g.Expect(len(nodeList.Items)).To(Equal(int(nodeCount)), "node count changed — unexpected rollout") |
| 2082 | + |
| 2083 | + // Verify the on-disk kubelet config.json reflects the updated pull secret. |
| 2084 | + // The global-pull-secret-syncer DaemonSet polls every 30s, so wait for it |
| 2085 | + // to sync original-pull-secret → /var/lib/kubelet/config.json, then deploy |
| 2086 | + // a verifier DaemonSet that compares the on-disk file against the cluster pull secret. |
| 2087 | + VerifyKubeletConfigWithDaemonSet(t, ctx, guestClient, dsImage, nodeCount) |
| 2088 | + |
| 2089 | + t.Log("Pull secret propagated to guest cluster and on-disk kubelet config.json without triggering rollout") |
| 2090 | + }) |
| 2091 | + |
1989 | 2092 | // Verify that nodes from Replace NodePools have the globalPS label applied via CAPI propagation. |
1990 | 2093 | // This label is set on the MachineDeployment template so it flows to Nodes at creation time. |
1991 | 2094 | t.Run("Check Replace nodes have globalPS label from CAPI propagation", func(t *testing.T) { |
@@ -2029,18 +2132,6 @@ func EnsureGlobalPullSecret(t *testing.T, ctx context.Context, mgmtClient crclie |
2029 | 2132 | }, 30*time.Second, 5*time.Second).Should(Succeed(), "global-pull-secret secret is not present") |
2030 | 2133 | }) |
2031 | 2134 |
|
2032 | | - // Check if the DaemonSet is present in the DataPlane |
2033 | | - t.Run("Check if the DaemonSet is present in the DataPlane", func(t *testing.T) { |
2034 | | - g.Eventually(func() error { |
2035 | | - daemonSet := hccomanifests.GlobalPullSecretDaemonSet() |
2036 | | - if err := guestClient.Get(ctx, crclient.ObjectKey{Name: daemonSet.Name, Namespace: daemonSet.Namespace}, daemonSet); err != nil { |
2037 | | - return err |
2038 | | - } |
2039 | | - dsImage = daemonSet.Spec.Template.Spec.Containers[0].Image |
2040 | | - return nil |
2041 | | - }, 30*time.Second, 5*time.Second).Should(Succeed(), "DaemonSet is not present") |
2042 | | - }) |
2043 | | - |
2044 | 2135 | t.Run("Wait for critical DaemonSets to be ready - first check", func(t *testing.T) { |
2045 | 2136 | g.Expect(waitForDaemonSetReady(t, ctx, guestClient, "ovnkube-node", "openshift-ovn-kubernetes", nodeCount)).To(Succeed()) |
2046 | 2137 | g.Expect(waitForDaemonSetReady(t, ctx, guestClient, hccomanifests.GlobalPullSecretDSName, hccomanifests.GlobalPullSecretNamespace, nodeCount)).To(Succeed()) |
|
0 commit comments