github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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  	"github.com/juju/juju/environs"
    11  	"github.com/juju/juju/environs/bootstrap"
    12  	"github.com/juju/juju/environs/config"
    13  	"github.com/juju/juju/instance"
    14  	"github.com/juju/juju/mongo"
    15  	"github.com/juju/juju/state"
    16  	"github.com/juju/juju/state/api"
    17  )
    18  
    19  // getAddresses queries and returns the Addresses for the given instances,
    20  // ignoring nil instances or ones without addresses.
    21  func getAddresses(instances []instance.Instance) []string {
    22  	names := make([]string, 0)
    23  	for _, inst := range instances {
    24  		if inst == nil {
    25  			continue
    26  		}
    27  		addrs, err := inst.Addresses()
    28  		if err != nil {
    29  			logger.Debugf(
    30  				"failed to get addresses for %v: %v (ignoring)",
    31  				inst.Id(), err,
    32  			)
    33  			continue
    34  		}
    35  		for _, addr := range addrs {
    36  			names = append(names, addr.Value)
    37  		}
    38  	}
    39  	return names
    40  }
    41  
    42  // composeAddresses suffixes each of a slice of hostnames with a given port
    43  // number.
    44  func composeAddresses(hostnames []string, port int) []string {
    45  	addresses := make([]string, len(hostnames))
    46  	for index, hostname := range hostnames {
    47  		addresses[index] = fmt.Sprintf("%s:%d", hostname, port)
    48  	}
    49  	return addresses
    50  }
    51  
    52  // getStateInfo puts together the state.Info and api.Info for the given
    53  // config, with the given state-server host names.
    54  // The given config absolutely must have a CACert.
    55  func getStateInfo(config *config.Config, hostnames []string) (*state.Info, *api.Info) {
    56  	cert, hasCert := config.CACert()
    57  	if !hasCert {
    58  		panic(errors.New("getStateInfo: config has no CACert"))
    59  	}
    60  	return &state.Info{
    61  			Info: mongo.Info{
    62  				Addrs:  composeAddresses(hostnames, config.StatePort()),
    63  				CACert: cert,
    64  			},
    65  		}, &api.Info{
    66  			Addrs:  composeAddresses(hostnames, config.APIPort()),
    67  			CACert: cert,
    68  		}
    69  }
    70  
    71  // StateInfo is a reusable implementation of Environ.StateInfo, available to
    72  // providers that also use the other functionality from this file.
    73  func StateInfo(env environs.Environ) (*state.Info, *api.Info, error) {
    74  	st, err := bootstrap.LoadState(env.Storage())
    75  	if err != nil {
    76  		return nil, nil, err
    77  	}
    78  	config := env.Config()
    79  	if _, hasCert := config.CACert(); !hasCert {
    80  		return nil, nil, fmt.Errorf("no CA certificate in environment configuration")
    81  	}
    82  	// Wait for the addresses of at least one of the instances to become available.
    83  	logger.Debugf("waiting for addresses of state server instances %v", st.StateInstances)
    84  	var addresses []string
    85  	for a := LongAttempt.Start(); len(addresses) == 0 && a.Next(); {
    86  		insts, err := env.Instances(st.StateInstances)
    87  		if err != nil && err != environs.ErrPartialInstances {
    88  			logger.Debugf("error getting state instances: %v", err.Error())
    89  			return nil, nil, err
    90  		}
    91  		addresses = getAddresses(insts)
    92  	}
    93  
    94  	if len(addresses) == 0 {
    95  		return nil, nil, fmt.Errorf("timed out waiting for addresses from %v", st.StateInstances)
    96  	}
    97  
    98  	stateInfo, apiInfo := getStateInfo(config, addresses)
    99  	return stateInfo, apiInfo, nil
   100  }