github.com/openshift/installer@v1.4.17/pkg/asset/installconfig/gcp/gcp.go (about)

     1  package gcp
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/AlecAivazis/survey/v2"
    11  	"github.com/AlecAivazis/survey/v2/core"
    12  	"github.com/pkg/errors"
    13  
    14  	"github.com/openshift/installer/pkg/types/gcp"
    15  	gcpValidation "github.com/openshift/installer/pkg/types/gcp/validation"
    16  )
    17  
    18  // Platform collects GCP-specific configuration.
    19  func Platform() (*gcp.Platform, error) {
    20  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
    21  	defer cancel()
    22  	project, err := selectProject(ctx)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	region, err := selectRegion(ctx, project)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	return &gcp.Platform{
    33  		ProjectID: project,
    34  		Region:    region,
    35  	}, nil
    36  }
    37  
    38  func selectProject(ctx context.Context) (string, error) {
    39  	ssn, err := GetSession(ctx)
    40  	if err != nil {
    41  		return "", errors.Wrap(err, "failed to get session")
    42  	}
    43  	defaultProject := ssn.Credentials.ProjectID
    44  
    45  	client := &Client{
    46  		ssn: ssn,
    47  	}
    48  
    49  	projects, err := client.GetProjects(ctx)
    50  	if err != nil {
    51  		return "", errors.Wrap(err, "failed to get projects")
    52  	} else if len(projects) == 0 {
    53  		return "", fmt.Errorf("failed to get projects for the given service principal, please check your permissions")
    54  	}
    55  
    56  	var options []string
    57  	ids := make(map[string]string)
    58  
    59  	var defaultValue string
    60  	for id, name := range projects {
    61  		option := fmt.Sprintf("%s (%s)", name, id)
    62  		ids[option] = id
    63  		if id == defaultProject {
    64  			defaultValue = option
    65  		}
    66  		options = append(options, option)
    67  	}
    68  	sort.Strings(options)
    69  
    70  	var selectedProject string
    71  	err = survey.Ask([]*survey.Question{
    72  		{
    73  			Prompt: &survey.Select{
    74  				Message: "Project ID",
    75  				Help:    "The project id where the cluster will be provisioned. The default is taken from the provided service account.",
    76  				Default: defaultValue,
    77  				Options: options,
    78  			},
    79  		},
    80  	}, &selectedProject)
    81  
    82  	selectedProject = ids[selectedProject]
    83  	return selectedProject, err
    84  }
    85  
    86  func getValidatedRegions(computeRegions []string) map[string]string {
    87  	validatedRegions := make(map[string]string)
    88  	for _, region := range computeRegions {
    89  		// Only add validated regions
    90  		if value, ok := gcpValidation.Regions[region]; ok {
    91  			validatedRegions[region] = value
    92  		}
    93  	}
    94  
    95  	return validatedRegions
    96  }
    97  
    98  func selectRegion(ctx context.Context, project string) (string, error) {
    99  	ssn, err := GetSession(ctx)
   100  	if err != nil {
   101  		return "", errors.Wrap(err, "failed to get session")
   102  	}
   103  
   104  	client := &Client{
   105  		ssn: ssn,
   106  	}
   107  
   108  	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
   109  	defer cancel()
   110  	computeRegions, err := client.GetRegions(ctx, project)
   111  	if err != nil || len(computeRegions) == 0 {
   112  		return "", errors.Wrap(err, "failed to get regions")
   113  	}
   114  
   115  	validRegions := getValidatedRegions(computeRegions)
   116  
   117  	defaultRegion := "us-central1"
   118  	defaultRegionName := ""
   119  	longRegions := make([]string, 0, len(validRegions))
   120  	shortRegions := make([]string, 0, len(validRegions))
   121  	for key, value := range validRegions {
   122  		shortRegions = append(shortRegions, key)
   123  		regionDesc := fmt.Sprintf("%s (%s)", key, value)
   124  		longRegions = append(longRegions, regionDesc)
   125  
   126  		if defaultRegionName == "" && key == defaultRegion {
   127  			defaultRegionName = regionDesc
   128  		}
   129  	}
   130  
   131  	var regionTransform survey.Transformer = func(ans interface{}) interface{} {
   132  		switch v := ans.(type) {
   133  		case core.OptionAnswer:
   134  			return core.OptionAnswer{Value: strings.SplitN(v.Value, " ", 2)[0], Index: v.Index}
   135  		case string:
   136  			return strings.SplitN(v, " ", 2)[0]
   137  		}
   138  		return ""
   139  	}
   140  
   141  	sort.Strings(longRegions)
   142  	sort.Strings(shortRegions)
   143  
   144  	if defaultRegionName == "" && len(longRegions) > 0 {
   145  		defaultRegionName = longRegions[0]
   146  	}
   147  
   148  	var selectedRegion string
   149  	err = survey.Ask([]*survey.Question{
   150  		{
   151  			Prompt: &survey.Select{
   152  				Message: "Region",
   153  				Help:    "The GCP region to be used for installation.",
   154  				Default: defaultRegionName,
   155  				Options: longRegions,
   156  			},
   157  			Validate: survey.ComposeValidators(survey.Required, func(ans interface{}) error {
   158  				choice := regionTransform(ans).(core.OptionAnswer).Value
   159  				i := sort.SearchStrings(shortRegions, choice)
   160  				if i == len(shortRegions) || shortRegions[i] != choice {
   161  					return errors.Errorf("invalid region %q", choice)
   162  				}
   163  				return nil
   164  			}),
   165  			Transform: regionTransform,
   166  		},
   167  	}, &selectedRegion)
   168  	if err != nil {
   169  		return "", err
   170  	}
   171  
   172  	return selectedRegion, nil
   173  }