@@ -51,6 +51,14 @@ type target struct {
5151 assets []asset.WritableAsset
5252}
5353
54+ type customDNSInfo struct {
55+ platform string
56+ privateInstall bool
57+ lbIPAddr string
58+ }
59+
60+ var savedConfig * rest.Config
61+
5462// each target is a variable to preserve the order when creating subcommands and still
5563// allow other functions to directly access each target individually.
5664var (
@@ -152,8 +160,15 @@ func clusterCreatePostRun(ctx context.Context) (int, error) {
152160 return 0 , errors .Wrap (err , "loading kubeconfig" )
153161 }
154162
163+ // Kubeconfig could get modified with the Dialer context. Save a copy of the original config
164+ // in case we need to revert back to it (e.g., when a proxy is configured for private installs).
165+ savedConfig = rest .CopyConfig (config )
166+ customDNSData := customDNSInfo {}
167+ if err := getCustomDNSInfo (ctx , & customDNSData ); err != nil {
168+ logrus .Fatal (fmt .Errorf ("unable to retrieve configuration for UserProvisionedDNS: %w" , err ))
169+ }
155170 // Handle the case when the API server is not reachable.
156- if err := handleUnreachableAPIServer (ctx , config ); err != nil {
171+ if err := handleUnreachableAPIServer (config , & customDNSData ); err != nil {
157172 logrus .Fatal (fmt .Errorf ("unable to handle api server override: %w" , err ))
158173 }
159174
@@ -222,6 +237,8 @@ func clusterCreatePostRun(ctx context.Context) (int, error) {
222237 }
223238 timer .StopTimer (timer .TotalTimeElapsed )
224239 timer .LogSummary ()
240+
241+ postInstallSteps (& customDNSData )
225242 return 0 , nil
226243}
227244
@@ -336,13 +353,11 @@ func runTargetCmd(ctx context.Context, targets ...asset.WritableAsset) func(cmd
336353}
337354
338355func waitForBootstrapComplete (ctx context.Context , config * rest.Config ) * clusterCreateError {
339- client , err := kubernetes .NewForConfig (config )
356+ customDNSData := customDNSInfo {}
357+ err := getCustomDNSInfo (ctx , & customDNSData )
340358 if err != nil {
341- return newClientError (errors .Wrap (err , "creating a Kubernetes client " ))
359+ return newClientError (errors .Wrap (err , "unable to retrieve configuration for UserProvisionedDNS " ))
342360 }
343-
344- discovery := client .Discovery ()
345-
346361 apiTimeout := 20 * time .Minute
347362
348363 untilTime := time .Now ().Add (apiTimeout )
@@ -366,6 +381,16 @@ func waitForBootstrapComplete(ctx context.Context, config *rest.Config) *cluster
366381
367382 var lastErr error
368383 err = wait .PollUntilContextCancel (apiContext , 2 * time .Second , true , func (_ context.Context ) (done bool , err error ) {
384+
385+ // Determine which config to use: the modified one (with custom dialer) or the original.
386+ // When UserProvisionedDNS is enabled and this is a private install, a proxy server
387+ // could be configured. In that case, kubeconfig does not need the custom dialer.
388+ activeConfig := getKubeconfig (& customDNSData , config )
389+ client , err := kubernetes .NewForConfig (activeConfig )
390+ if err != nil {
391+ return false , err
392+ }
393+ discovery := client .Discovery ()
369394 version , err := discovery .ServerVersion ()
370395 if err == nil {
371396 logrus .Infof ("API %s up" , version )
@@ -425,11 +450,16 @@ func waitForBootstrapComplete(ctx context.Context, config *rest.Config) *cluster
425450 logrus .Infof (" Baremetal control plane finished provisioning." )
426451 }
427452
453+ activeConfig := getKubeconfig (& customDNSData , config )
454+ client , err := kubernetes .NewForConfig (activeConfig )
455+ if err != nil {
456+ return newClientError (errors .Wrap (err , "creating a Kubernetes client" ))
457+ }
428458 if err := waitForBootstrapConfigMap (waitCtx , client ); err != nil {
429459 return err
430460 }
431461
432- if err := waitForEtcdBootstrapMemberRemoval (ctx , config ); err != nil {
462+ if err := waitForEtcdBootstrapMemberRemoval (ctx , activeConfig ); err != nil {
433463 return newBootstrapError (err )
434464 }
435465
@@ -501,12 +531,15 @@ func waitForEtcdBootstrapMemberRemoval(ctx context.Context, config *rest.Config)
501531 return nil
502532}
503533
504- func handleUnreachableAPIServer (ctx context.Context , config * rest.Config ) error {
534+ // getCustomDNSInfo gets LB IP address and other values needed specifically when UserProvisionedDNS
535+ // is configured from various assets in the asset store and populates customDNSInfo. That information
536+ // is useful to make updates to kubeconfig before and after install.
537+ func getCustomDNSInfo (ctx context.Context , customDNSData * customDNSInfo ) error {
505538 assetStore , err := assetstore .NewStore (command .RootOpts .Dir )
506539 if err != nil {
507540 return fmt .Errorf ("failed to create asset store: %w" , err )
508541 }
509-
542+ customDNSData . platform = ""
510543 // Ensure that the install is expecting the user to provision their own DNS solution.
511544 installConfig := & installconfig.InstallConfig {}
512545 if err := assetStore .Fetch (ctx , installConfig ); err != nil {
@@ -528,43 +561,106 @@ func handleUnreachableAPIServer(ctx context.Context, config *rest.Config) error
528561 default :
529562 return nil
530563 }
564+ customDNSData .platform = installConfig .Config .Platform .Name ()
531565
532566 lbConfig := & lbconfig.Config {}
533567 if err := assetStore .Fetch (ctx , lbConfig ); err != nil {
534568 return fmt .Errorf ("failed to fetch %s: %w" , lbConfig .Name (), err )
535569 }
536570
537571 lbType := lbconfig .PublicLoadBalancer
572+ customDNSData .privateInstall = false
538573 if ! installConfig .Config .PublicAPI () {
539574 lbType = lbconfig .PrivateLoadBalancer
575+ customDNSData .privateInstall = true
540576 }
541577
542578 _ , ipAddrs , err := lbConfig .ParseDNSDataFromConfig (lbType )
543579 if err != nil {
544580 return fmt .Errorf ("failed to parse lbconfig: %w" , err )
545581 }
546-
547- // The kubeconfig handles one ip address
582+ // Grabbing just the 1st load balancer IP address
548583 ipAddr := ""
549584 if len (ipAddrs ) > 0 {
550585 ipAddr = ipAddrs [0 ].String ()
551586 }
552587 if ipAddr == "" {
553588 return fmt .Errorf ("no ip address found in lbconfig" )
554589 }
555-
556- dialer := & net.Dialer {
557- Timeout : 1 * time .Minute ,
558- KeepAlive : 1 * time .Minute ,
559- }
560- config .Dial = kubeconfig .CreateDialContext (dialer , ipAddr )
590+ customDNSData .lbIPAddr = ipAddr
561591
562592 // The asset is currently saved in <install-dir>/openshift. This directory
563593 // was consumed during install but this file is generated after that action. This
564594 // artifact will hang around unless it is purged here.
565595 if err := asset .DeleteAssetFromDisk (lbConfig , command .RootOpts .Dir ); err != nil {
566596 return fmt .Errorf ("failed to delete %s from disk" , lbConfig .Name ())
567597 }
598+ return nil
599+ }
600+
601+ // handleUnreachableAPIServer updates kubeconfig with Dial context if needed.
602+ func handleUnreachableAPIServer (config * rest.Config , customDNSData * customDNSInfo ) error {
603+ if customDNSData .platform == "" {
604+ // Platform doesn't support custom-dns or UserProvisioedDNS was not enabled in a
605+ // supported platform. Nothing to do.
606+ return nil
607+ }
608+ if customDNSData .lbIPAddr == "" {
609+ return fmt .Errorf ("no load balancer ip address found to update kubeconfig" )
610+ }
568611
612+ dialer := & net.Dialer {
613+ Timeout : 1 * time .Minute ,
614+ KeepAlive : 1 * time .Minute ,
615+ }
616+ config .Dial = kubeconfig .CreateDialContext (dialer , customDNSData .lbIPAddr )
569617 return nil
570618}
619+
620+ // checkPrivateInstallReachability checks if install host is within the same network
621+ // as the private cluster.
622+ func checkPrivateInstallReachability (customDNSData * customDNSInfo ) (bool , error ) {
623+ reachableAPIServer := true
624+ if customDNSData .platform == "" {
625+ // Platform doesn't support custom-dns or UserProvisionedDNS was not enabled in a
626+ // supported platform. Nothing to do.
627+ return true , nil
628+ }
629+ if customDNSData .privateInstall {
630+ if customDNSData .lbIPAddr == "" {
631+ return true , fmt .Errorf ("no load balancer ip address found to check connectivity" )
632+ }
633+ logrus .Debugf ("checking if API server reachable with updated kubeconfig." )
634+ // Check if private LB IP and port are reachable from the Install host.
635+ address := net .JoinHostPort (customDNSData .lbIPAddr , "6443" )
636+ conn , err := net .DialTimeout ("tcp" , address , time .Second * 10 )
637+ if err != nil {
638+ // Not reachable. So, the install host is not in the same network.
639+ // Use kubeconfig without dialer context because some reachability
640+ // mechanism should be put in place at this time.
641+ logrus .Debugf ("unable to reach API server with kubeconfig. Reverting to using original kubeconfig." )
642+ reachableAPIServer = false
643+ } else {
644+ conn .Close ()
645+ }
646+ }
647+ return reachableAPIServer , nil
648+ }
649+
650+ func postInstallSteps (customDNSData * customDNSInfo ) {
651+ if customDNSData .platform != "" {
652+ // Platform with ProvisionedDNS enabled. Display post-install message.
653+ logrus .Infof ("This cluster is configured with UserProvisionedDNS. To access the cluster, please configure your External DNS solution with entries for the cluster's API and *.apps service endpoints. Please refer to documentation for further details" )
654+ }
655+ }
656+
657+ func getKubeconfig (customDNSData * customDNSInfo , config * rest.Config ) * rest.Config {
658+ activeConfig := config
659+ reachable , reachErr := checkPrivateInstallReachability (customDNSData )
660+ if reachErr == nil && ! reachable && savedConfig != nil {
661+ // Revert to saved kubeconfig without Dialer context.
662+ logrus .Debug ("Private install not directly reachable, using original kubeconfig configuration" )
663+ activeConfig = savedConfig
664+ }
665+ return activeConfig
666+ }
0 commit comments