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