func dialLogsinkAPI(apiInfo *api.Info) (*websocket.Conn, error) { // TODO(mjs) Most of this should be extracted to be shared for // connections to both /log (debuglog) and /logsink. header := utils.BasicAuthHeader(apiInfo.Tag.String(), apiInfo.Password) header.Set("X-Juju-Nonce", apiInfo.Nonce) conn, err := api.Connect(apiInfo, "/logsink", header, api.DialOpts{}) if err != nil { return nil, errors.Annotate(err, "failed to connect to logsink API") } // Read the initial error and translate to a real error. // Read up to the first new line character. We can't use bufio here as it // reads too much from the reader. line := make([]byte, 4096) n, err := conn.Read(line) if err != nil { return nil, errors.Annotate(err, "unable to read initial response") } line = line[0:n] var errResult params.ErrorResult err = json.Unmarshal(line, &errResult) if err != nil { return nil, errors.Annotate(err, "unable to unmarshal initial response") } if errResult.Error != nil { return nil, errors.Annotatef(err, "initial server error") } return conn, nil }
func (s *apiclientSuite) TestConnectToEnvWithPathTail(c *gc.C) { info := s.APIInfo(c) conn, err := api.Connect(info, "/log", nil, api.DialOpts{}) c.Assert(err, jc.ErrorIsNil) defer conn.Close() assertConnAddrForEnv(c, conn, info.Addrs[0], s.State.EnvironUUID(), "/log") }
func (s *apiclientSuite) TestConnectToRoot(c *gc.C) { info := s.APIInfo(c) info.EnvironTag = names.NewEnvironTag("") conn, err := api.Connect(info, "", nil, api.DialOpts{}) c.Assert(err, jc.ErrorIsNil) defer conn.Close() assertConnAddrForRoot(c, conn, info.Addrs[0]) }
func (s *apiclientSuite) TestConnectMultiple(c *gc.C) { // Create a socket that proxies to the API server. info := s.APIInfo(c) serverAddr := info.Addrs[0] server, err := net.Dial("tcp", serverAddr) c.Assert(err, jc.ErrorIsNil) defer server.Close() listener, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, jc.ErrorIsNil) defer listener.Close() go func() { for { client, err := listener.Accept() if err != nil { return } go io.Copy(client, server) go io.Copy(server, client) } }() // Check that we can use the proxy to connect. proxyAddr := listener.Addr().String() info.Addrs = []string{proxyAddr} conn, err := api.Connect(info, "/api", nil, api.DialOpts{}) c.Assert(err, jc.ErrorIsNil) conn.Close() assertConnAddrForEnv(c, conn, proxyAddr, s.State.EnvironUUID(), "/api") // Now break Addrs[0], and ensure that Addrs[1] // is successfully connected to. info.Addrs = []string{proxyAddr, serverAddr} listener.Close() conn, err = api.Connect(info, "/api", nil, api.DialOpts{}) c.Assert(err, jc.ErrorIsNil) conn.Close() assertConnAddrForEnv(c, conn, serverAddr, s.State.EnvironUUID(), "/api") }
func (s *apiclientSuite) TestConnectWithHeader(c *gc.C) { var seenCfg *websocket.Config fakeNewDialer := func(cfg *websocket.Config, _ api.DialOpts) func(<-chan struct{}) (io.Closer, error) { seenCfg = cfg return func(<-chan struct{}) (io.Closer, error) { return nil, errors.New("fake") } } s.PatchValue(api.NewWebsocketDialerPtr, fakeNewDialer) header := utils.BasicAuthHeader("foo", "bar") api.Connect(s.APIInfo(c), "", header, api.DialOpts{}) // Return values not important here c.Assert(seenCfg, gc.NotNil) c.Assert(seenCfg.Header, gc.DeepEquals, header) }
func (s *apiclientSuite) TestConnectMultipleError(c *gc.C) { listener, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, jc.ErrorIsNil) defer listener.Close() go func() { for { client, err := listener.Accept() if err != nil { return } client.Close() } }() info := s.APIInfo(c) addr := listener.Addr().String() info.Addrs = []string{addr, addr, addr} _, err = api.Connect(info, "/api", nil, api.DialOpts{}) c.Assert(err, gc.ErrorMatches, `unable to connect to "wss://.*/environment/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/api"`) }
func (s *apiclientSuite) TestConnectPrefersLocalhostIfPresent(c *gc.C) { // Create a socket that proxies to the API server though our localhost address. info := s.APIInfo(c) serverAddr := info.Addrs[0] server, err := net.Dial("tcp", serverAddr) c.Assert(err, jc.ErrorIsNil) defer server.Close() listener, err := net.Listen("tcp", "localhost:0") c.Assert(err, jc.ErrorIsNil) defer listener.Close() go func() { for { client, err := listener.Accept() if err != nil { return } go io.Copy(client, server) go io.Copy(server, client) } }() // Check that we are using our working address to connect listenerAddress := listener.Addr().String() // listenAddress contains the actual IP address, but APIHostPorts // is going to report localhost, so just find the port _, port, err := net.SplitHostPort(listenerAddress) c.Check(err, jc.ErrorIsNil) portNum, err := strconv.Atoi(port) c.Check(err, jc.ErrorIsNil) expectedHostPort := fmt.Sprintf("localhost:%d", portNum) info.Addrs = []string{"fakeAddress:1", "fakeAddress:1", expectedHostPort} conn, err := api.Connect(info, "/api", nil, api.DialOpts{}) c.Assert(err, jc.ErrorIsNil) defer conn.Close() assertConnAddrForEnv(c, conn, expectedHostPort, s.State.EnvironUUID(), "/api") }
func (s *apiclientSuite) TestConnectRequiresTailStartsWithSlash(c *gc.C) { _, err := api.Connect(s.APIInfo(c), "foo", nil, api.DialOpts{}) c.Assert(err, gc.ErrorMatches, `path tail must start with "/"`) }