launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/provider/common/state.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "errors" 8 "fmt" 9 10 "launchpad.net/juju-core/environs" 11 "launchpad.net/juju-core/environs/bootstrap" 12 "launchpad.net/juju-core/environs/config" 13 "launchpad.net/juju-core/instance" 14 "launchpad.net/juju-core/log" 15 "launchpad.net/juju-core/state" 16 "launchpad.net/juju-core/state/api" 17 ) 18 19 // getDNSNames queries and returns the DNS names for the given instances, 20 // ignoring nil instances or ones without DNS names. 21 func getDNSNames(instances []instance.Instance) []string { 22 names := make([]string, 0) 23 for _, inst := range instances { 24 if inst != nil { 25 name, err := inst.DNSName() 26 // If that fails, just keep looking. 27 if err == nil && name != "" { 28 names = append(names, name) 29 } 30 } 31 } 32 return names 33 } 34 35 // composeAddresses suffixes each of a slice of hostnames with a given port 36 // number. 37 func composeAddresses(hostnames []string, port int) []string { 38 addresses := make([]string, len(hostnames)) 39 for index, hostname := range hostnames { 40 addresses[index] = fmt.Sprintf("%s:%d", hostname, port) 41 } 42 return addresses 43 } 44 45 // composeStateInfo puts together the state.Info and api.Info for the given 46 // config, with the given state-server host names. 47 // The given config absolutely must have a CACert. 48 func getStateInfo(config *config.Config, hostnames []string) (*state.Info, *api.Info) { 49 cert, hasCert := config.CACert() 50 if !hasCert { 51 panic(errors.New("getStateInfo: config has no CACert")) 52 } 53 return &state.Info{ 54 Addrs: composeAddresses(hostnames, config.StatePort()), 55 CACert: cert, 56 }, &api.Info{ 57 Addrs: composeAddresses(hostnames, config.APIPort()), 58 CACert: cert, 59 } 60 } 61 62 // StateInfo is a reusable implementation of Environ.StateInfo, available to 63 // providers that also use the other functionality from this file. 64 func StateInfo(env environs.Environ) (*state.Info, *api.Info, error) { 65 st, err := bootstrap.LoadState(env.Storage()) 66 if err != nil { 67 return nil, nil, err 68 } 69 config := env.Config() 70 if _, hasCert := config.CACert(); !hasCert { 71 return nil, nil, fmt.Errorf("no CA certificate in environment configuration") 72 } 73 // Wait for the DNS names of any of the instances 74 // to become available. 75 log.Debugf("waiting for DNS name(s) of state server instances %v", st.StateInstances) 76 var hostnames []string 77 for a := LongAttempt.Start(); len(hostnames) == 0 && a.Next(); { 78 insts, err := env.Instances(st.StateInstances) 79 if err != nil && err != environs.ErrPartialInstances { 80 log.Debugf("error getting state instances: %v", err.Error()) 81 return nil, nil, err 82 } 83 hostnames = getDNSNames(insts) 84 } 85 86 if len(hostnames) == 0 { 87 return nil, nil, fmt.Errorf("timed out waiting for mgo address from %v", st.StateInstances) 88 } 89 90 stateInfo, apiInfo := getStateInfo(config, hostnames) 91 return stateInfo, apiInfo, nil 92 }