github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/address.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"net"
     8  	"reflect"
     9  	"strconv"
    10  
    11  	"github.com/juju/errors"
    12  	"gopkg.in/mgo.v2/bson"
    13  	"gopkg.in/mgo.v2/txn"
    14  
    15  	"github.com/juju/juju/network"
    16  )
    17  
    18  // controllerAddresses returns the list of internal addresses of the state
    19  // server machines.
    20  func (st *State) controllerAddresses() ([]string, error) {
    21  	ssState := st
    22  	model, err := st.ControllerModel()
    23  	if err != nil {
    24  		return nil, errors.Trace(err)
    25  	}
    26  	if st.ModelTag() != model.ModelTag() {
    27  		// We are not using the controller model, so get one.
    28  		logger.Debugf("getting a controller state connection, current env: %s", st.ModelTag())
    29  		ssState, err = st.ForModel(model.ModelTag())
    30  		if err != nil {
    31  			return nil, errors.Trace(err)
    32  		}
    33  		defer ssState.Close()
    34  		logger.Debugf("ssState env: %s", ssState.ModelTag())
    35  	}
    36  
    37  	type addressMachine struct {
    38  		Addresses []address
    39  	}
    40  	var allAddresses []addressMachine
    41  	// TODO(rog) 2013/10/14 index machines on jobs.
    42  	machines, closer := ssState.getCollection(machinesC)
    43  	defer closer()
    44  	err = machines.Find(bson.D{{"jobs", JobManageModel}}).All(&allAddresses)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	if len(allAddresses) == 0 {
    49  		return nil, errors.New("no controller machines found")
    50  	}
    51  	apiAddrs := make([]string, 0, len(allAddresses))
    52  	for _, addrs := range allAddresses {
    53  		naddrs := networkAddresses(addrs.Addresses)
    54  		addr, ok := network.SelectControllerAddress(naddrs, false)
    55  		if ok {
    56  			apiAddrs = append(apiAddrs, addr.Value)
    57  		}
    58  	}
    59  	if len(apiAddrs) == 0 {
    60  		return nil, errors.New("no controller machines with addresses found")
    61  	}
    62  	return apiAddrs, nil
    63  }
    64  
    65  func appendPort(addrs []string, port int) []string {
    66  	newAddrs := make([]string, len(addrs))
    67  	for i, addr := range addrs {
    68  		newAddrs[i] = net.JoinHostPort(addr, strconv.Itoa(port))
    69  	}
    70  	return newAddrs
    71  }
    72  
    73  // Addresses returns the list of cloud-internal addresses that
    74  // can be used to connect to the state.
    75  func (st *State) Addresses() ([]string, error) {
    76  	addrs, err := st.controllerAddresses()
    77  	if err != nil {
    78  		return nil, errors.Trace(err)
    79  	}
    80  	config, err := st.ModelConfig()
    81  	if err != nil {
    82  		return nil, errors.Trace(err)
    83  	}
    84  	return appendPort(addrs, config.StatePort()), nil
    85  }
    86  
    87  // APIAddressesFromMachines returns the list of cloud-internal addresses that
    88  // can be used to connect to the state API server.
    89  // This method will be deprecated when API addresses are
    90  // stored independently in their own document.
    91  func (st *State) APIAddressesFromMachines() ([]string, error) {
    92  	addrs, err := st.controllerAddresses()
    93  	if err != nil {
    94  		return nil, errors.Trace(err)
    95  	}
    96  	config, err := st.ModelConfig()
    97  	if err != nil {
    98  		return nil, errors.Trace(err)
    99  	}
   100  	return appendPort(addrs, config.APIPort()), nil
   101  }
   102  
   103  const apiHostPortsKey = "apiHostPorts"
   104  
   105  type apiHostPortsDoc struct {
   106  	APIHostPorts [][]hostPort `bson:"apihostports"`
   107  }
   108  
   109  // SetAPIHostPorts sets the addresses of the API server instances.
   110  // Each server is represented by one element in the top level slice.
   111  func (st *State) SetAPIHostPorts(netHostsPorts [][]network.HostPort) error {
   112  	doc := apiHostPortsDoc{
   113  		APIHostPorts: fromNetworkHostsPorts(netHostsPorts),
   114  	}
   115  	buildTxn := func(attempt int) ([]txn.Op, error) {
   116  		existing, err := st.APIHostPorts()
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  		op := txn.Op{
   121  			C:  controllersC,
   122  			Id: apiHostPortsKey,
   123  			Assert: bson.D{{
   124  				"apihostports", fromNetworkHostsPorts(existing),
   125  			}},
   126  		}
   127  		if !hostsPortsEqual(netHostsPorts, existing) {
   128  			op.Update = bson.D{{
   129  				"$set", bson.D{{"apihostports", doc.APIHostPorts}},
   130  			}}
   131  		}
   132  		return []txn.Op{op}, nil
   133  	}
   134  	if err := st.run(buildTxn); err != nil {
   135  		return errors.Annotate(err, "cannot set API addresses")
   136  	}
   137  	logger.Debugf("setting API hostPorts: %v", netHostsPorts)
   138  	return nil
   139  }
   140  
   141  // APIHostPorts returns the API addresses as set by SetAPIHostPorts.
   142  func (st *State) APIHostPorts() ([][]network.HostPort, error) {
   143  	var doc apiHostPortsDoc
   144  	controllers, closer := st.getCollection(controllersC)
   145  	defer closer()
   146  	err := controllers.Find(bson.D{{"_id", apiHostPortsKey}}).One(&doc)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	return networkHostsPorts(doc.APIHostPorts), nil
   151  }
   152  
   153  type DeployerConnectionValues struct {
   154  	StateAddresses []string
   155  	APIAddresses   []string
   156  }
   157  
   158  // DeployerConnectionInfo returns the address information necessary for the deployer.
   159  // The function does the expensive operations (getting stuff from mongo) just once.
   160  func (st *State) DeployerConnectionInfo() (*DeployerConnectionValues, error) {
   161  	addrs, err := st.controllerAddresses()
   162  	if err != nil {
   163  		return nil, errors.Trace(err)
   164  	}
   165  	config, err := st.ModelConfig()
   166  	if err != nil {
   167  		return nil, errors.Trace(err)
   168  	}
   169  	return &DeployerConnectionValues{
   170  		StateAddresses: appendPort(addrs, config.StatePort()),
   171  		APIAddresses:   appendPort(addrs, config.APIPort()),
   172  	}, nil
   173  }
   174  
   175  // address represents the location of a machine, including metadata
   176  // about what kind of location the address describes.
   177  //
   178  // TODO(dimitern) Make sure we integrate this with other networking
   179  // stuff at some point. We want to use juju-specific network names
   180  // that point to existing documents in the networks collection.
   181  type address struct {
   182  	Value       string `bson:"value"`
   183  	AddressType string `bson:"addresstype"`
   184  	Scope       string `bson:"networkscope,omitempty"`
   185  	Origin      string `bson:"origin,omitempty"`
   186  	SpaceName   string `bson:"spacename,omitempty"`
   187  }
   188  
   189  // Origin specifies where an address comes from, whether it was reported by a
   190  // provider or by a machine.
   191  type Origin string
   192  
   193  const (
   194  	// Address origin unknown.
   195  	OriginUnknown Origin = ""
   196  	// Address comes from a provider.
   197  	OriginProvider Origin = "provider"
   198  	// Address comes from a machine.
   199  	OriginMachine Origin = "machine"
   200  )
   201  
   202  // fromNetworkAddress is a convenience helper to create a state type
   203  // out of the network type, here for Address with a given Origin.
   204  func fromNetworkAddress(netAddr network.Address, origin Origin) address {
   205  	return address{
   206  		Value:       netAddr.Value,
   207  		AddressType: string(netAddr.Type),
   208  		Scope:       string(netAddr.Scope),
   209  		Origin:      string(origin),
   210  		SpaceName:   string(netAddr.SpaceName),
   211  	}
   212  }
   213  
   214  // networkAddress is a convenience helper to return the state type
   215  // as network type, here for Address.
   216  func (addr *address) networkAddress() network.Address {
   217  	return network.Address{
   218  		Value:     addr.Value,
   219  		Type:      network.AddressType(addr.AddressType),
   220  		Scope:     network.Scope(addr.Scope),
   221  		SpaceName: network.SpaceName(addr.SpaceName),
   222  	}
   223  }
   224  
   225  // fromNetworkAddresses is a convenience helper to create a state type
   226  // out of the network type, here for a slice of Address with a given origin.
   227  func fromNetworkAddresses(netAddrs []network.Address, origin Origin) []address {
   228  	addrs := make([]address, len(netAddrs))
   229  	for i, netAddr := range netAddrs {
   230  		addrs[i] = fromNetworkAddress(netAddr, origin)
   231  	}
   232  	return addrs
   233  }
   234  
   235  // networkAddresses is a convenience helper to return the state type
   236  // as network type, here for a slice of Address.
   237  func networkAddresses(addrs []address) []network.Address {
   238  	netAddrs := make([]network.Address, len(addrs))
   239  	for i, addr := range addrs {
   240  		netAddrs[i] = addr.networkAddress()
   241  	}
   242  	return netAddrs
   243  }
   244  
   245  // hostPort associates an address with a port. See also network.HostPort,
   246  // from/to which this is transformed.
   247  //
   248  // TODO(dimitern) Make sure we integrate this with other networking
   249  // stuff at some point. We want to use juju-specific network names
   250  // that point to existing documents in the networks collection.
   251  type hostPort struct {
   252  	Value       string `bson:"value"`
   253  	AddressType string `bson:"addresstype"`
   254  	Scope       string `bson:"networkscope,omitempty"`
   255  	Port        int    `bson:"port"`
   256  	SpaceName   string `bson:"spacename,omitempty"`
   257  }
   258  
   259  // fromNetworkHostPort is a convenience helper to create a state type
   260  // out of the network type, here for HostPort.
   261  func fromNetworkHostPort(netHostPort network.HostPort) hostPort {
   262  	return hostPort{
   263  		Value:       netHostPort.Value,
   264  		AddressType: string(netHostPort.Type),
   265  		Scope:       string(netHostPort.Scope),
   266  		Port:        netHostPort.Port,
   267  		SpaceName:   string(netHostPort.SpaceName),
   268  	}
   269  }
   270  
   271  // networkHostPort is a convenience helper to return the state type
   272  // as network type, here for HostPort.
   273  func (hp *hostPort) networkHostPort() network.HostPort {
   274  	return network.HostPort{
   275  		Address: network.Address{
   276  			Value:     hp.Value,
   277  			Type:      network.AddressType(hp.AddressType),
   278  			Scope:     network.Scope(hp.Scope),
   279  			SpaceName: network.SpaceName(hp.SpaceName),
   280  		},
   281  		Port: hp.Port,
   282  	}
   283  }
   284  
   285  // fromNetworkHostsPorts is a helper to create a state type
   286  // out of the network type, here for a nested slice of HostPort.
   287  func fromNetworkHostsPorts(netHostsPorts [][]network.HostPort) [][]hostPort {
   288  	hsps := make([][]hostPort, len(netHostsPorts))
   289  	for i, netHostPorts := range netHostsPorts {
   290  		hsps[i] = make([]hostPort, len(netHostPorts))
   291  		for j, netHostPort := range netHostPorts {
   292  			hsps[i][j] = fromNetworkHostPort(netHostPort)
   293  		}
   294  	}
   295  	return hsps
   296  }
   297  
   298  // networkHostsPorts is a convenience helper to return the state type
   299  // as network type, here for a nested slice of HostPort.
   300  func networkHostsPorts(hsps [][]hostPort) [][]network.HostPort {
   301  	netHostsPorts := make([][]network.HostPort, len(hsps))
   302  	for i, hps := range hsps {
   303  		netHostsPorts[i] = make([]network.HostPort, len(hps))
   304  		for j, hp := range hps {
   305  			netHostsPorts[i][j] = hp.networkHostPort()
   306  		}
   307  	}
   308  	return netHostsPorts
   309  }
   310  
   311  // addressEqual checks that two slices of network addresses are equal.
   312  func addressesEqual(a, b []network.Address) bool {
   313  	return reflect.DeepEqual(a, b)
   314  }
   315  
   316  // hostsPortsEqual checks that two arrays of network hostports are equal.
   317  func hostsPortsEqual(a, b [][]network.HostPort) bool {
   318  	return reflect.DeepEqual(a, b)
   319  }