github.com/jenkins-x/jx/v2@v2.1.155/pkg/cmd/opts/domain.go (about)

     1  package opts
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/jenkins-x/jx-logging/pkg/log"
    11  	"github.com/jenkins-x/jx/v2/pkg/cloud"
    12  	"github.com/jenkins-x/jx/v2/pkg/cloud/amazon"
    13  	"github.com/jenkins-x/jx/v2/pkg/cloud/iks"
    14  	"github.com/jenkins-x/jx/v2/pkg/surveyutils"
    15  	"github.com/jenkins-x/jx/v2/pkg/util"
    16  	"gopkg.in/AlecAivazis/survey.v1"
    17  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    18  	"k8s.io/client-go/kubernetes"
    19  )
    20  
    21  // GetDomain returns the domain name, trying to infer it either from various Kubernetes resources or cloud provider. If no domain
    22  // can be determined, it will prompt to the user for a value.
    23  func (o *CommonOptions) GetDomain(client kubernetes.Interface, domain string, provider string, ingressNamespace string, ingressService string, externalIP string) (string, error) {
    24  	surveyOpts := survey.WithStdio(o.In, o.Out, o.Err)
    25  	address := externalIP
    26  	if address == "" {
    27  		info := util.ColorInfo
    28  		log.Logger().Infof("Waiting to find the external host name of the ingress controller Service in namespace %s with name %s",
    29  			info(ingressNamespace), info(ingressService))
    30  		if provider == cloud.KUBERNETES {
    31  			log.Logger().Infof("If you are installing Jenkins X on premise you may want to use the '--on-premise' flag or specify the '--external-ip' flags. See: %s",
    32  				info("https://jenkins-x.io/getting-started/install-on-cluster/#installing-jenkins-x-on-premise"))
    33  		}
    34  		svc, err := client.CoreV1().Services(ingressNamespace).Get(ingressService, metav1.GetOptions{})
    35  		if err != nil {
    36  			return "", err
    37  		}
    38  		if svc != nil {
    39  			for _, v := range svc.Status.LoadBalancer.Ingress {
    40  				if v.IP != "" {
    41  					address = v.IP
    42  				} else if v.Hostname != "" {
    43  					address = v.Hostname
    44  				}
    45  			}
    46  		}
    47  	}
    48  	defaultDomain := address
    49  
    50  	if provider == cloud.AWS || provider == cloud.EKS {
    51  		if domain != "" {
    52  			err := amazon.RegisterAwsCustomDomain(domain, address)
    53  			return domain, err
    54  		}
    55  
    56  		// if we are booting, we want to use nip.io directly if a domain is not provided
    57  		// if one is provided, we'll expose it through external-dns
    58  		// we also check that we are not in cluster to do this, as the domain gets wiped if we are using .nip/xip.io
    59  		// and we need to create a new one again
    60  		if !o.InCluster() && os.Getenv("JX_INTERPRET_PIPELINE") != "true" {
    61  			log.Logger().Infof("\nOn AWS we recommend using a custom DNS name to access services in your Kubernetes cluster to ensure you can use all of your Availability Zones")
    62  			log.Logger().Infof("If you do not have a custom DNS name you can use yet, then you can register a new one here: %s\n",
    63  				util.ColorInfo("https://console.aws.amazon.com/route53/home?#DomainRegistration:"))
    64  
    65  			if o.BatchMode {
    66  				return "", fmt.Errorf("Please specify a custom DNS name via --domain when installing on AWS in batch mode")
    67  			}
    68  			for {
    69  				if answer, err := util.Confirm("Would you like to register a wildcard DNS ALIAS to point at this ELB address? ", true,
    70  					"When using AWS we need to use a wildcard DNS alias to point at the ELB host name so you can access services inside Jenkins X and in your Environments.", o.GetIOFileHandles()); err != nil {
    71  					return "", err
    72  				} else if answer {
    73  					customDomain := ""
    74  					prompt := &survey.Input{
    75  						Message: "Your custom DNS name: ",
    76  						Help:    "Enter your custom domain that we can use to setup a Route 53 ALIAS record to point at the ELB host: " + address,
    77  					}
    78  					err = survey.AskOne(prompt, &customDomain, nil, surveyOpts)
    79  					if err != nil {
    80  						return "", err
    81  					}
    82  					if customDomain != "" {
    83  						err := amazon.RegisterAwsCustomDomain(customDomain, address)
    84  						return customDomain, err
    85  					}
    86  				} else {
    87  					break
    88  				}
    89  			}
    90  		}
    91  	}
    92  
    93  	if provider == cloud.IKS {
    94  		if domain != "" {
    95  			log.Logger().Infof("\nIBM Kubernetes Service will use provided domain. Ensure name is registered with DNS (ex. CIS) and pointing the cluster ingress IP: %s",
    96  				util.ColorInfo(address))
    97  			return domain, nil
    98  		}
    99  		clusterName, err := iks.GetClusterName()
   100  		clusterRegion, err := iks.GetKubeClusterRegion(client)
   101  		if err == nil && clusterName != "" && clusterRegion != "" {
   102  			customDomain := clusterName + "." + clusterRegion + ".containers.appdomain.cloud"
   103  			log.Logger().Infof("\nIBM Kubernetes Service will use the default cluster domain: ")
   104  			log.Logger().Infof("%s", util.ColorInfo(customDomain))
   105  			return customDomain, nil
   106  		}
   107  		log.Logger().Infof("ERROR getting IBM Kubernetes Service will use the default cluster domain:")
   108  		log.Logger().Infof(err.Error())
   109  	}
   110  
   111  	if address != "" {
   112  		addNip := true
   113  		aip := net.ParseIP(address)
   114  		if aip == nil {
   115  			log.Logger().Infof("The Ingress address %s is not an IP address. We recommend we try resolve it to a public IP address and use that for the domain to access services externally.",
   116  				util.ColorInfo(address))
   117  
   118  			addressIP := ""
   119  			resolve := true
   120  			if !o.BatchMode {
   121  				answer, err := util.Confirm("Would you like wait and resolve this address to an IP address and use it for the domain?", true,
   122  					"Should we convert "+address+" to an IP address so we can access resources externally", o.GetIOFileHandles())
   123  				if err != nil {
   124  					return "", err
   125  				}
   126  				resolve = answer
   127  			}
   128  			if resolve {
   129  				log.Logger().Infof("Waiting for %s to be resolvable to an IP address...", util.ColorInfo(address))
   130  				f := func() error {
   131  					ips, err := net.LookupIP(address)
   132  					if err == nil {
   133  						for _, ip := range ips {
   134  							t := ip.String()
   135  							if t != "" && !ip.IsLoopback() {
   136  								addressIP = t
   137  								return nil
   138  							}
   139  						}
   140  					}
   141  					return fmt.Errorf("Address cannot be resolved yet %s", address)
   142  				}
   143  				err := o.RetryQuiet(5*6, time.Second*10, f)
   144  				if err != nil {
   145  					return "", err
   146  				}
   147  			}
   148  			if addressIP == "" {
   149  				addNip = false
   150  				log.Logger().Infof("Still not managed to resolve address %s into an IP address. Please try figure out the domain by hand", address)
   151  			} else {
   152  				log.Logger().Infof("%s resolved to IP %s", util.ColorInfo(address), util.ColorInfo(addressIP))
   153  				address = addressIP
   154  			}
   155  		}
   156  		if addNip && !strings.HasSuffix(address, ".amazonaws.com") {
   157  			defaultDomain = fmt.Sprintf("%s.nip.io", address)
   158  		}
   159  	}
   160  
   161  	if domain == "" {
   162  		if o.BatchMode {
   163  			log.Logger().Infof("No domain flag provided so using default %s to generate Ingress rules", defaultDomain)
   164  			return defaultDomain, nil
   165  		}
   166  		log.Logger().Infof("You can now configure a wildcard DNS pointing to the new Load Balancer address %s", util.ColorInfo(address))
   167  		log.Logger().Infof("If you don't have a wildcard DNS setup then create a DNS (A) record and point it at: %s, then use the DNS domain in the next input...", util.ColorInfo(address))
   168  
   169  		log.Logger().Info("\nIf you do not have a custom domain setup yet, Ingress rules will be set for magic DNS nip.io.")
   170  		// Todo: Evaluate mention of this deprecated command
   171  		log.Logger().Infof("Once you have a custom domain ready, you can update with the command %s", util.ColorInfo("jx upgrade ingress --cluster"))
   172  
   173  		if domain == "" {
   174  			prompt := &survey.Input{
   175  				Message: "Domain",
   176  				Default: defaultDomain,
   177  				Help:    "Enter your custom domain that is used to generate Ingress rules, defaults to the magic DNS nip.io",
   178  			}
   179  			err := survey.AskOne(prompt, &domain,
   180  				survey.ComposeValidators(survey.Required, surveyutils.NoWhiteSpaceValidator()), surveyOpts)
   181  			if err != nil {
   182  				return "", err
   183  			}
   184  		}
   185  		if domain == "" {
   186  			domain = defaultDomain
   187  		}
   188  	} else {
   189  		if domain != defaultDomain {
   190  			log.Logger().Infof("You can now configure your wildcard DNS %s to point to %s", domain, address)
   191  		}
   192  	}
   193  
   194  	return domain, nil
   195  }