github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/commands/bootstrap_interactive.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package commands 5 6 import ( 7 "bufio" 8 "fmt" 9 "io" 10 "sort" 11 "strings" 12 13 "github.com/juju/errors" 14 "gopkg.in/juju/names.v2" 15 16 jujucloud "github.com/juju/juju/cloud" 17 "github.com/juju/juju/cmd/juju/common" 18 "github.com/juju/juju/cmd/juju/interact" 19 ) 20 21 // assembleClouds 22 func assembleClouds() ([]string, error) { 23 public, _, err := jujucloud.PublicCloudMetadata(jujucloud.JujuPublicCloudsPath()) 24 if err != nil { 25 return nil, errors.Trace(err) 26 } 27 28 personal, err := jujucloud.PersonalCloudMetadata() 29 if err != nil { 30 return nil, errors.Trace(err) 31 } 32 33 builtin, err := common.BuiltInClouds() 34 if err != nil { 35 return nil, errors.Trace(err) 36 } 37 38 return sortClouds(public, builtin, personal), nil 39 } 40 41 // queryCloud asks the user to choose a cloud. 42 func queryCloud(clouds []string, defCloud string, scanner *bufio.Scanner, w io.Writer) (string, error) { 43 list := strings.Join(clouds, "\n") 44 if _, err := fmt.Fprint(w, "Clouds\n", list, "\n\n"); err != nil { 45 return "", errors.Trace(err) 46 } 47 48 // add support for a default (empty) selection. 49 clouds = append(clouds, "") 50 51 verify := interact.MatchOptions(clouds, "Invalid cloud.") 52 53 query := fmt.Sprintf("Select a cloud [%s]: ", defCloud) 54 cloud, err := interact.QueryVerify(query, scanner, w, w, verify) 55 if err != nil { 56 return "", errors.Trace(err) 57 } 58 if cloud == "" { 59 return defCloud, nil 60 } 61 if ok := names.IsValidCloud(cloud); !ok { 62 return "", errors.NotValidf("cloud name %q", cloud) 63 } 64 65 cloudName, ok := interact.FindMatch(cloud, clouds) 66 if !ok { 67 // should be impossible 68 return "", errors.Errorf("invalid cloud name chosen: %s", cloud) 69 } 70 71 return cloudName, nil 72 } 73 74 // queryRegion asks the user to pick a region of the ones passed in. The first 75 // region in the list will be the default. 76 func queryRegion(cloud string, regions []jujucloud.Region, scanner *bufio.Scanner, w io.Writer) (string, error) { 77 fmt.Fprintf(w, "Regions in %s:\n", cloud) 78 names := jujucloud.RegionNames(regions) 79 // add an empty string to allow for a default value. Also gives us an extra 80 // line return after the list of names. 81 names = append(names, "") 82 if _, err := fmt.Fprintln(w, strings.Join(names, "\n")); err != nil { 83 return "", errors.Trace(err) 84 } 85 verify := interact.MatchOptions(names, "Invalid region.") 86 defaultRegion := regions[0].Name 87 query := fmt.Sprintf("Select a region in %s [%s]: ", cloud, defaultRegion) 88 region, err := interact.QueryVerify(query, scanner, w, w, verify) 89 if err != nil { 90 return "", errors.Trace(err) 91 } 92 if region == "" { 93 return defaultRegion, nil 94 } 95 regionName, ok := interact.FindMatch(region, names) 96 if !ok { 97 // should be impossible 98 return "", errors.Errorf("invalid region name chosen: %s", region) 99 } 100 101 return regionName, nil 102 } 103 104 func defaultControllerName(cloudname, region string) string { 105 if region == "" { 106 return cloudname 107 } 108 return cloudname + "-" + region 109 } 110 111 func queryName(defName string, scanner *bufio.Scanner, w io.Writer) (string, error) { 112 query := fmt.Sprintf("Enter a name for the Controller [%s]: ", defName) 113 name, err := interact.QueryVerify(query, scanner, w, w, nil) 114 if err != nil { 115 return "", errors.Trace(err) 116 } 117 if name == "" { 118 return defName, nil 119 } 120 return name, nil 121 } 122 123 func sortClouds(maps ...map[string]jujucloud.Cloud) []string { 124 var clouds []string 125 for _, m := range maps { 126 for name := range m { 127 clouds = append(clouds, name) 128 } 129 } 130 sort.Strings(clouds) 131 return clouds 132 }