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  }