@@ -41,17 +41,22 @@ import (
4141 appsv1 "k8s.io/api/apps/v1"
4242 batchv1 "k8s.io/api/batch/v1"
4343 corev1 "k8s.io/api/core/v1"
44+ apierrors "k8s.io/apimachinery/pkg/api/errors"
4445 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4546 "k8s.io/client-go/kubernetes"
4647 typedappsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
4748 typedbatchv1 "k8s.io/client-go/kubernetes/typed/batch/v1"
4849 typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
50+ "k8s.io/client-go/tools/clientcmd"
4951 "sigs.k8s.io/cluster-api-provider-azure/azure"
52+ "sigs.k8s.io/cluster-api-provider-azure/util/tele"
5053 clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
54+ "sigs.k8s.io/cluster-api/controllers/noderefutil"
5155 clusterv1exp "sigs.k8s.io/cluster-api/exp/api/v1alpha4"
5256 "sigs.k8s.io/cluster-api/test/framework"
5357 "sigs.k8s.io/cluster-api/test/framework/kubernetesversions"
5458 "sigs.k8s.io/cluster-api/util"
59+ utilkubeconfig "sigs.k8s.io/cluster-api/util/kubeconfig"
5560 "sigs.k8s.io/controller-runtime/pkg/client"
5661)
5762
@@ -311,18 +316,17 @@ type nodeSSHInfo struct {
311316// getClusterSSHInfo returns the information needed to establish a SSH connection through a
312317// control plane endpoint to each node in the cluster.
313318func getClusterSSHInfo (ctx context.Context , c client.Client , namespace , name string ) ([]nodeSSHInfo , error ) {
314- sshInfo := []nodeSSHInfo {}
315-
319+ var sshInfo []nodeSSHInfo
316320 // Collect the info for each VM / Machine.
317321 machines , err := getMachinesInCluster (ctx , c , namespace , name )
318322 if err != nil {
319- return nil , err
323+ return sshInfo , errors . Wrap ( err , "failed to get machines in the cluster" )
320324 }
321325 for i := range machines .Items {
322326 m := & machines .Items [i ]
323327 cluster , err := util .GetClusterFromMetadata (ctx , c , m .ObjectMeta )
324328 if err != nil {
325- return nil , err
329+ return nil , errors . Wrap ( err , "failed to get cluster from metadata" )
326330 }
327331 sshInfo = append (sshInfo , nodeSSHInfo {
328332 Endpoint : cluster .Spec .ControlPlaneEndpoint .Host ,
@@ -334,28 +338,98 @@ func getClusterSSHInfo(ctx context.Context, c client.Client, namespace, name str
334338 // Collect the info for each instance in a VMSS / MachinePool.
335339 machinePools , err := getMachinePoolsInCluster (ctx , c , namespace , name )
336340 if err != nil {
337- return nil , err
341+ return sshInfo , errors .Wrap (err , "failed to find machine pools in cluster" )
342+ }
343+
344+ // make a workload client to access the workload cluster
345+ workloadClient , err := getWorkloadClient (ctx , c , namespace , name )
346+ if err != nil {
347+ return sshInfo , errors .Wrap (err , "failed to get workload client" )
338348 }
349+
339350 for i := range machinePools .Items {
340351 p := & machinePools .Items [i ]
341352 cluster , err := util .GetClusterFromMetadata (ctx , c , p .ObjectMeta )
342353 if err != nil {
343- return nil , err
354+ return sshInfo , errors .Wrap (err , "failed to get cluster from metadata" )
355+ }
356+
357+ nodes , err := getReadyNodes (ctx , workloadClient , p .Status .NodeRefs )
358+ if err != nil {
359+ return sshInfo , errors .Wrap (err , "failed to get ready nodes" )
344360 }
345- for j := range p .Status .NodeRefs {
346- n := p .Status .NodeRefs [j ]
361+
362+ if p .Spec .Replicas != nil && len (nodes ) < int (* p .Spec .Replicas ) {
363+ message := fmt .Sprintf ("machine pool %s/%s expected replicas %d, but only found %d ready nodes" , p .Namespace , p .Name , * p .Spec .Replicas , len (nodes ))
364+ Log (message )
365+ return sshInfo , errors .New (message )
366+ }
367+
368+ for _ , node := range nodes {
347369 sshInfo = append (sshInfo , nodeSSHInfo {
348370 Endpoint : cluster .Spec .ControlPlaneEndpoint .Host ,
349- Hostname : n .Name ,
371+ Hostname : node .Name ,
350372 Port : sshPort ,
351373 })
352374 }
353-
354375 }
355376
356377 return sshInfo , nil
357378}
358379
380+ func getReadyNodes (ctx context.Context , c client.Client , refs []corev1.ObjectReference ) ([]corev1.Node , error ) {
381+ var nodes []corev1.Node
382+ for _ , ref := range refs {
383+ var node corev1.Node
384+ if err := c .Get (ctx , client.ObjectKey {
385+ Namespace : ref .Namespace ,
386+ Name : ref .Name ,
387+ }, & node ); err != nil {
388+ if apierrors .IsNotFound (err ) {
389+ // If 404, continue. Likely the node refs have not caught up to infra providers
390+ continue
391+ }
392+
393+ return nodes , err
394+ }
395+
396+ if ! noderefutil .IsNodeReady (& node ) {
397+ Logf ("node is not ready and won't be counted for ssh info %s/%s" , node .Namespace , node .Name )
398+ continue
399+ }
400+
401+ nodes = append (nodes , node )
402+ }
403+
404+ return nodes , nil
405+ }
406+
407+ func getWorkloadClient (ctx context.Context , c client.Client , namespace , clusterName string ) (client.Client , error ) {
408+ ctx , span := tele .Tracer ().Start (ctx , "scope.MachinePoolMachineScope.getWorkloadClient" )
409+ defer span .End ()
410+
411+ obj := client.ObjectKey {
412+ Namespace : namespace ,
413+ Name : clusterName ,
414+ }
415+ dataBytes , err := utilkubeconfig .FromSecret (ctx , c , obj )
416+ if err != nil {
417+ return nil , errors .Wrapf (err , "\" %s-kubeconfig\" not found in namespace %q" , obj .Name , obj .Namespace )
418+ }
419+
420+ cfg , err := clientcmd .Load (dataBytes )
421+ if err != nil {
422+ return nil , errors .Wrapf (err , "failed to load \" %s-kubeconfig\" in namespace %q" , obj .Name , obj .Namespace )
423+ }
424+
425+ restConfig , err := clientcmd .NewDefaultClientConfig (* cfg , & clientcmd.ConfigOverrides {}).ClientConfig ()
426+ if err != nil {
427+ return nil , errors .Wrapf (err , "failed transform config \" %s-kubeconfig\" in namespace %q" , obj .Name , obj .Namespace )
428+ }
429+
430+ return client .New (restConfig , client.Options {})
431+ }
432+
359433// getMachinesInCluster returns a list of all machines in the given cluster.
360434// This is adapted from CAPI's test/framework/cluster_proxy.go.
361435func getMachinesInCluster (ctx context.Context , c framework.Lister , namespace , name string ) (* clusterv1.MachineList , error ) {
0 commit comments