github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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  // address represents the location of a machine, including metadata
   154  // about what kind of location the address describes.
   155  //
   156  // TODO(dimitern) Make sure we integrate this with other networking
   157  // stuff at some point. We want to use juju-specific network names
   158  // that point to existing documents in the networks collection.
   159  type address struct {
   160  	Value       string `bson:"value"`
   161  	AddressType string `bson:"addresstype"`
   162  	Scope       string `bson:"networkscope,omitempty"`
   163  	Origin      string `bson:"origin,omitempty"`
   164  	SpaceName   string `bson:"spacename,omitempty"`
   165  }
   166  
   167  // Origin specifies where an address comes from, whether it was reported by a
   168  // provider or by a machine.
   169  type Origin string
   170  
   171  const (
   172  	// Address origin unknown.
   173  	OriginUnknown Origin = ""
   174  	// Address comes from a provider.
   175  	OriginProvider Origin = "provider"
   176  	// Address comes from a machine.
   177  	OriginMachine Origin = "machine"
   178  )
   179  
   180  // fromNetworkAddress is a convenience helper to create a state type
   181  // out of the network type, here for Address with a given Origin.
   182  func fromNetworkAddress(netAddr network.Address, origin Origin) address {
   183  	return address{
   184  		Value:       netAddr.Value,
   185  		AddressType: string(netAddr.Type),
   186  		Scope:       string(netAddr.Scope),
   187  		Origin:      string(origin),
   188  		SpaceName:   string(netAddr.SpaceName),
   189  	}
   190  }
   191  
   192  // networkAddress is a convenience helper to return the state type
   193  // as network type, here for Address.
   194  func (addr *address) networkAddress() network.Address {
   195  	return network.Address{
   196  		Value:     addr.Value,
   197  		Type:      network.AddressType(addr.AddressType),
   198  		Scope:     network.Scope(addr.Scope),
   199  		SpaceName: network.SpaceName(addr.SpaceName),
   200  	}
   201  }
   202  
   203  // fromNetworkAddresses is a convenience helper to create a state type
   204  // out of the network type, here for a slice of Address with a given origin.
   205  func fromNetworkAddresses(netAddrs []network.Address, origin Origin) []address {
   206  	addrs := make([]address, len(netAddrs))
   207  	for i, netAddr := range netAddrs {
   208  		addrs[i] = fromNetworkAddress(netAddr, origin)
   209  	}
   210  	return addrs
   211  }
   212  
   213  // networkAddresses is a convenience helper to return the state type
   214  // as network type, here for a slice of Address.
   215  func networkAddresses(addrs []address) []network.Address {
   216  	netAddrs := make([]network.Address, len(addrs))
   217  	for i, addr := range addrs {
   218  		netAddrs[i] = addr.networkAddress()
   219  	}
   220  	return netAddrs
   221  }
   222  
   223  // hostPort associates an address with a port. See also network.HostPort,
   224  // from/to which this is transformed.
   225  //
   226  // TODO(dimitern) Make sure we integrate this with other networking
   227  // stuff at some point. We want to use juju-specific network names
   228  // that point to existing documents in the networks collection.
   229  type hostPort struct {
   230  	Value       string `bson:"value"`
   231  	AddressType string `bson:"addresstype"`
   232  	Scope       string `bson:"networkscope,omitempty"`
   233  	Port        int    `bson:"port"`
   234  	SpaceName   string `bson:"spacename,omitempty"`
   235  }
   236  
   237  // fromNetworkHostPort is a convenience helper to create a state type
   238  // out of the network type, here for HostPort.
   239  func fromNetworkHostPort(netHostPort network.HostPort) hostPort {
   240  	return hostPort{
   241  		Value:       netHostPort.Value,
   242  		AddressType: string(netHostPort.Type),
   243  		Scope:       string(netHostPort.Scope),
   244  		Port:        netHostPort.Port,
   245  		SpaceName:   string(netHostPort.SpaceName),
   246  	}
   247  }
   248  
   249  // networkHostPort is a convenience helper to return the state type
   250  // as network type, here for HostPort.
   251  func (hp *hostPort) networkHostPort() network.HostPort {
   252  	return network.HostPort{
   253  		Address: network.Address{
   254  			Value:     hp.Value,
   255  			Type:      network.AddressType(hp.AddressType),
   256  			Scope:     network.Scope(hp.Scope),
   257  			SpaceName: network.SpaceName(hp.SpaceName),
   258  		},
   259  		Port: hp.Port,
   260  	}
   261  }
   262  
   263  // fromNetworkHostsPorts is a helper to create a state type
   264  // out of the network type, here for a nested slice of HostPort.
   265  func fromNetworkHostsPorts(netHostsPorts [][]network.HostPort) [][]hostPort {
   266  	hsps := make([][]hostPort, len(netHostsPorts))
   267  	for i, netHostPorts := range netHostsPorts {
   268  		hsps[i] = make([]hostPort, len(netHostPorts))
   269  		for j, netHostPort := range netHostPorts {
   270  			hsps[i][j] = fromNetworkHostPort(netHostPort)
   271  		}
   272  	}
   273  	return hsps
   274  }
   275  
   276  // networkHostsPorts is a convenience helper to return the state type
   277  // as network type, here for a nested slice of HostPort.
   278  func networkHostsPorts(hsps [][]hostPort) [][]network.HostPort {
   279  	netHostsPorts := make([][]network.HostPort, len(hsps))
   280  	for i, hps := range hsps {
   281  		netHostsPorts[i] = make([]network.HostPort, len(hps))
   282  		for j, hp := range hps {
   283  			netHostsPorts[i][j] = hp.networkHostPort()
   284  		}
   285  	}
   286  	return netHostsPorts
   287  }
   288  
   289  // addressEqual checks that two slices of network addresses are equal.
   290  func addressesEqual(a, b []network.Address) bool {
   291  	return reflect.DeepEqual(a, b)
   292  }
   293  
   294  // hostsPortsEqual checks that two arrays of network hostports are equal.
   295  func hostsPortsEqual(a, b [][]network.HostPort) bool {
   296  	return reflect.DeepEqual(a, b)
   297  }