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

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package api_test
     5  
     6  import (
     7  	"net"
     8  	"sync/atomic"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils/parallel"
    15  	"golang.org/x/net/websocket"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	"github.com/juju/juju/api"
    19  	"github.com/juju/juju/apiserver/params"
    20  	jujutesting "github.com/juju/juju/juju/testing"
    21  	"github.com/juju/juju/rpc"
    22  	jujuversion "github.com/juju/juju/version"
    23  )
    24  
    25  type apiclientSuite struct {
    26  	jujutesting.JujuConnSuite
    27  }
    28  
    29  var _ = gc.Suite(&apiclientSuite{})
    30  
    31  func (s *apiclientSuite) TestConnectWebsocketToEnv(c *gc.C) {
    32  	info := s.APIInfo(c)
    33  	conn, _, err := api.ConnectWebsocket(info, api.DialOpts{})
    34  	c.Assert(err, jc.ErrorIsNil)
    35  	defer conn.Close()
    36  	assertConnAddrForEnv(c, conn, info.Addrs[0], s.State.ModelUUID(), "/api")
    37  }
    38  
    39  func (s *apiclientSuite) TestConnectWebsocketToRoot(c *gc.C) {
    40  	info := s.APIInfo(c)
    41  	info.ModelTag = names.NewModelTag("")
    42  	conn, _, err := api.ConnectWebsocket(info, api.DialOpts{})
    43  	c.Assert(err, jc.ErrorIsNil)
    44  	defer conn.Close()
    45  	assertConnAddrForRoot(c, conn, info.Addrs[0])
    46  }
    47  
    48  func (s *apiclientSuite) TestConnectWebsocketMultiple(c *gc.C) {
    49  	// Create a socket that proxies to the API server.
    50  	info := s.APIInfo(c)
    51  	serverAddr := info.Addrs[0]
    52  	proxy := testing.NewTCPProxy(c, serverAddr)
    53  	defer proxy.Close()
    54  
    55  	// Check that we can use the proxy to connect.
    56  	info.Addrs = []string{proxy.Addr()}
    57  	conn, _, err := api.ConnectWebsocket(info, api.DialOpts{})
    58  	c.Assert(err, jc.ErrorIsNil)
    59  	conn.Close()
    60  	assertConnAddrForEnv(c, conn, proxy.Addr(), s.State.ModelUUID(), "/api")
    61  
    62  	// Now break Addrs[0], and ensure that Addrs[1]
    63  	// is successfully connected to.
    64  	proxy.Close()
    65  	info.Addrs = []string{proxy.Addr(), serverAddr}
    66  	conn, _, err = api.ConnectWebsocket(info, api.DialOpts{})
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	conn.Close()
    69  	assertConnAddrForEnv(c, conn, serverAddr, s.State.ModelUUID(), "/api")
    70  }
    71  
    72  func (s *apiclientSuite) TestConnectWebsocketMultipleError(c *gc.C) {
    73  	listener, err := net.Listen("tcp", "127.0.0.1:0")
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	defer listener.Close()
    76  	// count holds the number of times we've accepted a connection.
    77  	var count int32
    78  	go func() {
    79  		for {
    80  			client, err := listener.Accept()
    81  			if err != nil {
    82  				return
    83  			}
    84  			atomic.AddInt32(&count, 1)
    85  			client.Close()
    86  		}
    87  	}()
    88  	info := s.APIInfo(c)
    89  	addr := listener.Addr().String()
    90  	info.Addrs = []string{addr, addr, addr}
    91  	_, _, err = api.ConnectWebsocket(info, api.DialOpts{})
    92  	c.Assert(err, gc.ErrorMatches, `unable to connect to API: websocket.Dial wss://.*/model/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/api: .*`)
    93  	c.Assert(atomic.LoadInt32(&count), gc.Equals, int32(3))
    94  }
    95  
    96  func (s *apiclientSuite) TestOpen(c *gc.C) {
    97  	info := s.APIInfo(c)
    98  	st, err := api.Open(info, api.DialOpts{})
    99  	c.Assert(err, jc.ErrorIsNil)
   100  	defer st.Close()
   101  
   102  	c.Assert(st.Addr(), gc.Equals, info.Addrs[0])
   103  	modelTag, err := st.ModelTag()
   104  	c.Assert(err, jc.ErrorIsNil)
   105  	c.Assert(modelTag, gc.Equals, s.State.ModelTag())
   106  
   107  	remoteVersion, versionSet := st.ServerVersion()
   108  	c.Assert(versionSet, jc.IsTrue)
   109  	c.Assert(remoteVersion, gc.Equals, jujuversion.Current)
   110  }
   111  
   112  func (s *apiclientSuite) TestOpenHonorsModelTag(c *gc.C) {
   113  	info := s.APIInfo(c)
   114  
   115  	// TODO(jam): 2014-06-05 http://pad.lv/1326802
   116  	// we want to test this eventually, but for now s.APIInfo uses
   117  	// conn.StateInfo() which doesn't know about ModelTag.
   118  	// c.Check(info.ModelTag, gc.Equals, env.Tag())
   119  	// c.Assert(info.ModelTag, gc.Not(gc.Equals), "")
   120  
   121  	// We start by ensuring we have an invalid tag, and Open should fail.
   122  	info.ModelTag = names.NewModelTag("bad-tag")
   123  	_, err := api.Open(info, api.DialOpts{})
   124  	c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{
   125  		Message: `unknown model: "bad-tag"`,
   126  		Code:    "not found",
   127  	})
   128  	c.Check(params.ErrCode(err), gc.Equals, params.CodeNotFound)
   129  
   130  	// Now set it to the right tag, and we should succeed.
   131  	info.ModelTag = s.State.ModelTag()
   132  	st, err := api.Open(info, api.DialOpts{})
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	st.Close()
   135  
   136  	// Backwards compatibility, we should succeed if we do not set an
   137  	// model tag
   138  	info.ModelTag = names.NewModelTag("")
   139  	st, err = api.Open(info, api.DialOpts{})
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	st.Close()
   142  }
   143  
   144  func (s *apiclientSuite) TestServerRoot(c *gc.C) {
   145  	url := api.ServerRoot(s.APIState.Client())
   146  	c.Assert(url, gc.Matches, "https://localhost:[0-9]+")
   147  }
   148  
   149  func (s *apiclientSuite) TestDialWebsocketStopped(c *gc.C) {
   150  	stopped := make(chan struct{})
   151  	f := api.NewWebsocketDialer(nil, api.DialOpts{})
   152  	close(stopped)
   153  	result, err := f(stopped)
   154  	c.Assert(err, gc.Equals, parallel.ErrStopped)
   155  	c.Assert(result, gc.IsNil)
   156  }
   157  
   158  func assertConnAddrForEnv(c *gc.C, conn *websocket.Conn, addr, modelUUID, tail string) {
   159  	c.Assert(conn.RemoteAddr(), gc.Matches, "^wss://"+addr+"/model/"+modelUUID+tail+"$")
   160  }
   161  
   162  func assertConnAddrForRoot(c *gc.C, conn *websocket.Conn, addr string) {
   163  	c.Assert(conn.RemoteAddr(), gc.Matches, "^wss://"+addr+"/$")
   164  }