[lxc-devel] [lxd/master] Lxd simple client nomerge

tych0 on Github lxc-bot at linuxcontainers.org
Wed Feb 24 14:53:09 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 353 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160224/4f6da509/attachment.bin>
-------------- next part --------------
From 57d5b5c3b36175425037c13ec123a64099acce8e Mon Sep 17 00:00:00 2001
From: John Arbash Meinel <john at arbash-meinel.com>
Date: Tue, 23 Feb 2016 18:25:13 +0400
Subject: [PATCH 1/6] Rework lxd.NewClient so we don't need a disk cache.

This adds a new interface NewClientFromInfo which lets API clients
decide how they want to track the information that they need, and
just supply that information when they go to connect.
NewClient still uses the disk cache, but shares all of the actual
setting up of connections via NewClientFromInfo.

Signed-off-by: John Arbash Meinel <john at arbash-meinel.com>
---
 client.go         | 231 +++++++++++++++++++++++++++++++++++++-----------------
 shared/network.go |  63 ++++++++++++---
 2 files changed, 210 insertions(+), 84 deletions(-)

diff --git a/client.go b/client.go
index d63c75d..97e2099 100644
--- a/client.go
+++ b/client.go
@@ -142,7 +142,7 @@ func HoistResponse(r *http.Response, rtype ResponseType) (*Response, error) {
 	return resp, nil
 }
 
-func readMyCert(configDir string) (string, string, error) {
+func ensureMyCert(configDir string) (string, string, error) {
 	certf := path.Join(configDir, "client.crt")
 	keyf := path.Join(configDir, "client.key")
 
@@ -153,94 +153,181 @@ func readMyCert(configDir string) (string, string, error) {
 
 // NewClient returns a new LXD client.
 func NewClient(config *Config, remote string) (*Client, error) {
-	c := Client{
-		Config: *config,
-		Http:   http.Client{},
-	}
-
-	c.Name = remote
-
 	if remote == "" {
 		return nil, fmt.Errorf("A remote name must be provided.")
 	}
 
-	if r, ok := config.Remotes[remote]; ok {
-		if r.Addr[0:5] == "unix:" {
-			if r.Addr == "unix://" {
-				r.Addr = fmt.Sprintf("unix:%s", shared.VarPath("unix.socket"))
-			}
-
-			c.BaseURL = "http://unix.socket"
-			c.BaseWSURL = "ws://unix.socket"
-			c.Transport = "unix"
-			uDial := func(networ, addr string) (net.Conn, error) {
-				var err error
-				var raddr *net.UnixAddr
-				if r.Addr[7:] == "unix://" {
-					raddr, err = net.ResolveUnixAddr("unix", r.Addr[7:])
-				} else {
-					raddr, err = net.ResolveUnixAddr("unix", r.Addr[5:])
-				}
-				if err != nil {
-					return nil, err
-				}
-				return net.DialUnix("unix", nil, raddr)
-			}
-			c.Http.Transport = &http.Transport{Dial: uDial}
-			c.websocketDialer.NetDial = uDial
-			c.Remote = &r
-
-			st, err := c.ServerStatus()
+	r, ok := config.Remotes[remote]
+	if !ok {
+		return nil, fmt.Errorf("unknown remote name: %q", remote)
+	}
+	info := ConnectInfo{
+		Name: remote,
+		Addr: r.Addr,
+	}
+	if r.Addr[0:5] != "unix:" {
+		certf, keyf, err := ensureMyCert(config.ConfigDir)
+		if err != nil {
+			return nil, err
+		}
+		certBytes, err := ioutil.ReadFile(certf)
+		if err != nil {
+			return nil, err
+		}
+		keyBytes, err := ioutil.ReadFile(keyf)
+		if err != nil {
+			return nil, err
+		}
+		info.ClientPEMCert = string(certBytes)
+		info.ClientPEMKey = string(keyBytes)
+		serverCertPath := config.ServerCertPath(remote)
+		if shared.PathExists(serverCertPath) {
+			cert, err := shared.ReadCert(serverCertPath)
 			if err != nil {
 				return nil, err
 			}
-			c.Certificate = st.Environment.Certificate
+
+			info.ServerPEMCert = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
+		}
+	}
+	c, err := NewClientFromInfo(info)
+	if err != nil {
+		return nil, err
+	}
+	c.Config = *config
+	return c, nil
+}
+
+// ConnectInfo contains the information we need to connect to a specific LXD server
+type ConnectInfo struct {
+	// Name is a simple identifier for the remote server. In 'lxc' it is
+	// the name used to lookup the address and other information in the
+	// config.yml file.
+	Name string
+	// Addr is the host address to connect to. It can be unix: to indicate
+	// we should connect over a unix socket, or it can be an IP Address or
+	// Hostname, or an https:// URL.
+	Addr string
+	// ClientPEMCert is the PEM encoded bytes of the client's certificate.
+	// If Addr indicates a Unix socket, the certificate and key bytes will
+	// not be used.
+	ClientPEMCert string
+	// ClientPEMKey is the PEM encoded private bytes of the client's key associated with its certificate
+	ClientPEMKey string
+	// ServerPEMCert is the PEM encoded server certificate that we are
+	// connecting to. It can be the empty string if we do not know the
+	// server's certificate yet.
+	ServerPEMCert string
+}
+
+// how to handle ServerCerts that we want to cache?
+
+func connectViaUnix(c *Client, addr string) error {
+	if addr == "unix://" {
+		addr = fmt.Sprintf("unix:%s", shared.VarPath("unix.socket"))
+	}
+
+	c.BaseURL = "http://unix.socket"
+	c.BaseWSURL = "ws://unix.socket"
+	c.Transport = "unix"
+	r := &RemoteConfig{
+		Addr: addr,
+		// TODO: (jam) RemoteConfig.Public is not relevant for the purposes of
+		// an active connection. It only seems to be used to display server
+		// information in "list", though if we aren't getting that information
+		// from the service itself, it seems like it can't be guaranteed to be
+		// correct, as it is just the local information about a remote server.
+		// Is there another reason it is here?
+		Public: false,
+	}
+	uDial := func(networ, addr string) (net.Conn, error) {
+		// TODO: (jam) Why are we ignoring the passed in 'addr' and only using our own Addr?
+		// Do we allow other code to mutate Client.Remote.Addr and
+		// change what we connect to, and still ignore the 'adrd'
+		// passed to Dial?
+		var err error
+		var raddr *net.UnixAddr
+		if r.Addr[7:] == "unix://" {
+			raddr, err = net.ResolveUnixAddr("unix", r.Addr[7:])
 		} else {
-			certf, keyf, err := readMyCert(config.ConfigDir)
-			if err != nil {
-				return nil, err
-			}
+			raddr, err = net.ResolveUnixAddr("unix", r.Addr[5:])
+		}
+		if err != nil {
+			return nil, err
+		}
+		return net.DialUnix("unix", nil, raddr)
+	}
+	c.Http.Transport = &http.Transport{Dial: uDial}
+	c.websocketDialer.NetDial = uDial
+	c.Remote = r
 
-			var cert *x509.Certificate
-			if shared.PathExists(c.Config.ServerCertPath(c.Name)) {
-				cert, err = shared.ReadCert(c.Config.ServerCertPath(c.Name))
-				if err != nil {
-					return nil, err
-				}
+	st, err := c.ServerStatus()
+	if err != nil {
+		return err
+	}
+	c.Certificate = st.Environment.Certificate
+	return nil
+}
 
-				c.Certificate = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
-			}
+func connectViaHttp(c *Client, addr, clientCert, clientKey, serverCert string) error {
 
-			tlsconfig, err := shared.GetTLSConfig(certf, keyf, cert)
-			if err != nil {
-				return nil, err
-			}
+	tlsconfig, err := shared.GetTLSConfigMem(clientCert, clientKey, serverCert)
+	if err != nil {
+		return err
+	}
 
-			tr := &http.Transport{
-				TLSClientConfig: tlsconfig,
-				Dial:            shared.RFC3493Dialer,
-				Proxy:           http.ProxyFromEnvironment,
-			}
+	tr := &http.Transport{
+		TLSClientConfig: tlsconfig,
+		Dial:            shared.RFC3493Dialer,
+		Proxy:           http.ProxyFromEnvironment,
+	}
 
-			c.websocketDialer.NetDial = shared.RFC3493Dialer
-			c.websocketDialer.TLSClientConfig = tlsconfig
+	c.websocketDialer.NetDial = shared.RFC3493Dialer
+	c.websocketDialer.TLSClientConfig = tlsconfig
 
-			if r.Addr[0:8] == "https://" {
-				c.BaseURL = "https://" + r.Addr[8:]
-				c.BaseWSURL = "wss://" + r.Addr[8:]
-			} else {
-				c.BaseURL = "https://" + r.Addr
-				c.BaseWSURL = "wss://" + r.Addr
-			}
-			c.Transport = "https"
-			c.Http.Transport = tr
-			c.Remote = &r
-		}
+	if addr[0:8] == "https://" {
+		c.BaseURL = "https://" + addr[8:]
+		c.BaseWSURL = "wss://" + addr[8:]
 	} else {
-		return nil, fmt.Errorf("unknown remote name: %q", remote)
+		c.BaseURL = "https://" + addr
+		c.BaseWSURL = "wss://" + addr
+	}
+	c.Transport = "https"
+	c.Http.Transport = tr
+	c.Remote = &RemoteConfig{
+		Addr: addr,
+		// TODO: (jam) RemoteConfig.Public is not relevant for the purposes of
+		// an active connection. It only seems to be used to display server
+		// information in "list", though if we aren't getting that information
+		// from the service itself, it seems like it can't be guaranteed to be
+		// correct, as it is just the local information about a remote server.
+		// Is there another reason it is here?
+		Public: false,
+	}
+	// TODO: (jam) It is odd to me that the Unix socket path actually
+	// connects to the service, but the HTTP path just sets up a transport
+	// and socket dialer, but doesn't actually try to connect.
+	return nil
+}
+
+// NewClientFromInfo returns a new LXD client.
+func NewClientFromInfo(info ConnectInfo) (*Client, error) {
+	c := &Client{
+		// Config: *config,
+		Http: http.Client{},
+	}
+	c.Name = info.Name
+	var err error
+	if info.Addr[0:5] == "unix:" {
+		err = connectViaUnix(c, info.Addr)
+	} else {
+		err = connectViaHttp(c, info.Addr, info.ClientPEMCert, info.ClientPEMKey, info.ServerPEMCert)
+	}
+	if err != nil {
+		return nil, err
 	}
 
-	return &c, nil
+	return c, nil
 }
 
 func (c *Client) Addresses() ([]string, error) {
diff --git a/shared/network.go b/shared/network.go
index d853a85..cda850d 100644
--- a/shared/network.go
+++ b/shared/network.go
@@ -3,6 +3,7 @@ package shared
 import (
 	"crypto/tls"
 	"crypto/x509"
+	"encoding/pem"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -62,8 +63,8 @@ func GetRemoteCertificate(address string) (*x509.Certificate, error) {
 	return resp.TLS.PeerCertificates[0], nil
 }
 
-func GetTLSConfig(tlsClientCert string, tlsClientKey string, tlsRemoteCert *x509.Certificate) (*tls.Config, error) {
-	tlsConfig := &tls.Config{
+func initTLSConfig() *tls.Config {
+	return &tls.Config{
 		MinVersion: tls.VersionTLS12,
 		MaxVersion: tls.VersionTLS12,
 		CipherSuites: []uint16{
@@ -71,17 +72,9 @@ func GetTLSConfig(tlsClientCert string, tlsClientKey string, tlsRemoteCert *x509
 			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 		PreferServerCipherSuites: true,
 	}
+}
 
-	// Client authentication
-	if tlsClientCert != "" && tlsClientKey != "" {
-		cert, err := tls.LoadX509KeyPair(tlsClientCert, tlsClientKey)
-		if err != nil {
-			return nil, err
-		}
-
-		tlsConfig.Certificates = []tls.Certificate{cert}
-	}
-
+func finalizeTLSConfig(tlsConfig *tls.Config, tlsRemoteCert *x509.Certificate) {
 	// Trusted certificates
 	if tlsRemoteCert != nil {
 		caCertPool := x509.NewCertPool()
@@ -101,6 +94,52 @@ func GetTLSConfig(tlsClientCert string, tlsClientKey string, tlsRemoteCert *x509
 	}
 
 	tlsConfig.BuildNameToCertificate()
+}
+
+func GetTLSConfig(tlsClientCertFile string, tlsClientKeyFile string, tlsRemoteCert *x509.Certificate) (*tls.Config, error) {
+	tlsConfig := initTLSConfig()
+
+	// Client authentication
+	if tlsClientCertFile != "" && tlsClientKeyFile != "" {
+		cert, err := tls.LoadX509KeyPair(tlsClientCertFile, tlsClientKeyFile)
+		if err != nil {
+			return nil, err
+		}
+
+		tlsConfig.Certificates = []tls.Certificate{cert}
+	}
+
+	finalizeTLSConfig(tlsConfig, tlsRemoteCert)
+	return tlsConfig, nil
+}
+
+func GetTLSConfigMem(tlsClientCert string, tlsClientKey string, tlsRemoteCertPEM string) (*tls.Config, error) {
+	tlsConfig := initTLSConfig()
+
+	// Client authentication
+	if tlsClientCert != "" && tlsClientKey != "" {
+		cert, err := tls.X509KeyPair([]byte(tlsClientCert), []byte(tlsClientKey))
+		if err != nil {
+			return nil, err
+		}
+
+		tlsConfig.Certificates = []tls.Certificate{cert}
+	}
+
+	var tlsRemoteCert *x509.Certificate
+	if tlsRemoteCertPEM != "" {
+		/// XXX: jam 2016-02-23 shared.ReadCert ignores any trailing
+		//content. Is that secure? I know there were security holes in
+		//some gpg decrypt usage because it would pass on extra bytes
+		//outside of the signed portion.
+		certBlock, _ := pem.Decode([]byte(tlsRemoteCertPEM))
+		var err error
+		tlsRemoteCert, err = x509.ParseCertificate(certBlock.Bytes)
+		if err != nil {
+			return nil, err
+		}
+	}
+	finalizeTLSConfig(tlsConfig, tlsRemoteCert)
 
 	return tlsConfig, nil
 }

From fc90e7db9374ddf257eddf5b2e970415cbd5402a Mon Sep 17 00:00:00 2001
From: John Arbash Meinel <john at arbash-meinel.com>
Date: Wed, 24 Feb 2016 13:40:48 +0400
Subject: [PATCH 2/6] Review comments indicated the code expected NewClient to
 actually connect, but we can just fix 'lxc finger' instead.

Signed-off-by: John Arbash Meinel <john at arbash-meinel.com>
---
 client.go     | 14 +-------------
 lxc/finger.go | 10 ++++++++--
 2 files changed, 9 insertions(+), 15 deletions(-)

diff --git a/client.go b/client.go
index 97e2099..6b55b0c 100644
--- a/client.go
+++ b/client.go
@@ -220,8 +220,6 @@ type ConnectInfo struct {
 	ServerPEMCert string
 }
 
-// how to handle ServerCerts that we want to cache?
-
 func connectViaUnix(c *Client, addr string) error {
 	if addr == "unix://" {
 		addr = fmt.Sprintf("unix:%s", shared.VarPath("unix.socket"))
@@ -270,7 +268,6 @@ func connectViaUnix(c *Client, addr string) error {
 }
 
 func connectViaHttp(c *Client, addr, clientCert, clientKey, serverCert string) error {
-
 	tlsconfig, err := shared.GetTLSConfigMem(clientCert, clientKey, serverCert)
 	if err != nil {
 		return err
@@ -294,16 +291,7 @@ func connectViaHttp(c *Client, addr, clientCert, clientKey, serverCert string) e
 	}
 	c.Transport = "https"
 	c.Http.Transport = tr
-	c.Remote = &RemoteConfig{
-		Addr: addr,
-		// TODO: (jam) RemoteConfig.Public is not relevant for the purposes of
-		// an active connection. It only seems to be used to display server
-		// information in "list", though if we aren't getting that information
-		// from the service itself, it seems like it can't be guaranteed to be
-		// correct, as it is just the local information about a remote server.
-		// Is there another reason it is here?
-		Public: false,
-	}
+	c.Remote = &RemoteConfig{Addr: addr}
 	// TODO: (jam) It is odd to me that the Unix socket path actually
 	// connects to the service, but the HTTP path just sets up a transport
 	// and socket dialer, but doesn't actually try to connect.
diff --git a/lxc/finger.go b/lxc/finger.go
index 6c07638..3fea385 100644
--- a/lxc/finger.go
+++ b/lxc/finger.go
@@ -34,7 +34,13 @@ func (c *fingerCmd) run(config *lxd.Config, args []string) error {
 		remote = config.DefaultRemote
 	}
 
-	// NewClient will finger the server to test the connection before returning.
-	_, err := lxd.NewClient(config, remote)
+	// New client may or may not need to connect to the remote host, but
+	// client.ServerStatus will at least request the basic information from
+	// the server.
+	client, err := lxd.NewClient(config, remote)
+	if err != nil {
+		return err
+	}
+	_, err = client.ServerStatus()
 	return err
 }

From 3638b2fe0bca12f47d7713add5e0c2b7f57cacf9 Mon Sep 17 00:00:00 2001
From: John Arbash Meinel <john at arbash-meinel.com>
Date: Wed, 24 Feb 2016 13:52:26 +0400
Subject: [PATCH 3/6] review cleanup.

Explain why we aren't making use of network and addr arguments, ignore that we
don't pass in a value for 'Public'.

Signed-off-by: John Arbash Meinel <john at arbash-meinel.com>
---
 client.go | 26 ++++++++------------------
 1 file changed, 8 insertions(+), 18 deletions(-)

diff --git a/client.go b/client.go
index 6b55b0c..31f9ef7 100644
--- a/client.go
+++ b/client.go
@@ -228,21 +228,12 @@ func connectViaUnix(c *Client, addr string) error {
 	c.BaseURL = "http://unix.socket"
 	c.BaseWSURL = "ws://unix.socket"
 	c.Transport = "unix"
-	r := &RemoteConfig{
-		Addr: addr,
-		// TODO: (jam) RemoteConfig.Public is not relevant for the purposes of
-		// an active connection. It only seems to be used to display server
-		// information in "list", though if we aren't getting that information
-		// from the service itself, it seems like it can't be guaranteed to be
-		// correct, as it is just the local information about a remote server.
-		// Is there another reason it is here?
-		Public: false,
-	}
-	uDial := func(networ, addr string) (net.Conn, error) {
-		// TODO: (jam) Why are we ignoring the passed in 'addr' and only using our own Addr?
-		// Do we allow other code to mutate Client.Remote.Addr and
-		// change what we connect to, and still ignore the 'adrd'
-		// passed to Dial?
+	r := &RemoteConfig{Addr: addr}
+	uDial := func(network, addr string) (net.Conn, error) {
+		// The arguments 'network' and 'addr' are ignored because
+		// they are the wrong information.
+		// Addr takes unix://unix.socket and tries to connect to
+		// 'unix.socket:80' which is certainly not what we want.
 		var err error
 		var raddr *net.UnixAddr
 		if r.Addr[7:] == "unix://" {
@@ -292,9 +283,8 @@ func connectViaHttp(c *Client, addr, clientCert, clientKey, serverCert string) e
 	c.Transport = "https"
 	c.Http.Transport = tr
 	c.Remote = &RemoteConfig{Addr: addr}
-	// TODO: (jam) It is odd to me that the Unix socket path actually
-	// connects to the service, but the HTTP path just sets up a transport
-	// and socket dialer, but doesn't actually try to connect.
+	// We don't actually need to connect yet, defer that until someone
+	// needs something from the server.
 	return nil
 }
 

From f5f2030f734e433af12ec22588a49bb31973f6d0 Mon Sep 17 00:00:00 2001
From: John Arbash Meinel <john at arbash-meinel.com>
Date: Wed, 24 Feb 2016 14:16:16 +0400
Subject: [PATCH 4/6] more review feedback

Signed-off-by: John Arbash Meinel <john at arbash-meinel.com>
---
 client.go         | 15 +++++++++------
 shared/network.go |  5 +----
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/client.go b/client.go
index 31f9ef7..24e44db 100644
--- a/client.go
+++ b/client.go
@@ -166,6 +166,10 @@ func NewClient(config *Config, remote string) (*Client, error) {
 		Addr: r.Addr,
 	}
 	if r.Addr[0:5] != "unix:" {
+		if r.Addr == "unix://" {
+			r.Addr = fmt.Sprintf("unix:%s", shared.VarPath("unix.socket"))
+		}
+
 		certf, keyf, err := ensureMyCert(config.ConfigDir)
 		if err != nil {
 			return nil, err
@@ -204,9 +208,12 @@ type ConnectInfo struct {
 	// the name used to lookup the address and other information in the
 	// config.yml file.
 	Name string
-	// Addr is the host address to connect to. It can be unix: to indicate
-	// we should connect over a unix socket, or it can be an IP Address or
+	// Addr is the host address to connect to. It can be
+	// unix:/path/to/socket to indicate we should connect over a unix
+	// socket, or it can be an IP Address or
 	// Hostname, or an https:// URL.
+	// The standard unix socket is located at $LXD_DIR/unix.socket
+	// See also github.com/lxc/lxd/shared.VarPath("unix.socket")
 	Addr string
 	// ClientPEMCert is the PEM encoded bytes of the client's certificate.
 	// If Addr indicates a Unix socket, the certificate and key bytes will
@@ -221,10 +228,6 @@ type ConnectInfo struct {
 }
 
 func connectViaUnix(c *Client, addr string) error {
-	if addr == "unix://" {
-		addr = fmt.Sprintf("unix:%s", shared.VarPath("unix.socket"))
-	}
-
 	c.BaseURL = "http://unix.socket"
 	c.BaseWSURL = "ws://unix.socket"
 	c.Transport = "unix"
diff --git a/shared/network.go b/shared/network.go
index cda850d..20ddb68 100644
--- a/shared/network.go
+++ b/shared/network.go
@@ -128,10 +128,7 @@ func GetTLSConfigMem(tlsClientCert string, tlsClientKey string, tlsRemoteCertPEM
 
 	var tlsRemoteCert *x509.Certificate
 	if tlsRemoteCertPEM != "" {
-		/// XXX: jam 2016-02-23 shared.ReadCert ignores any trailing
-		//content. Is that secure? I know there were security holes in
-		//some gpg decrypt usage because it would pass on extra bytes
-		//outside of the signed portion.
+		// Ignore any content outside of the PEM bytes we care about
 		certBlock, _ := pem.Decode([]byte(tlsRemoteCertPEM))
 		var err error
 		tlsRemoteCert, err = x509.ParseCertificate(certBlock.Bytes)

From 5129553cb7e3128fd51c66a906e7f28ca4859b6c Mon Sep 17 00:00:00 2001
From: John Arbash Meinel <john at arbash-meinel.com>
Date: Wed, 24 Feb 2016 15:23:20 +0400
Subject: [PATCH 5/6] Switch to strings.TrimPrefix

There were a few places we were using string slicing. Using strings package makes it
cleaner and less error prone.

Signed-off-by: John Arbash Meinel <john at arbash-meinel.com>
---
 client.go | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/client.go b/client.go
index 24e44db..d9da65d 100644
--- a/client.go
+++ b/client.go
@@ -165,11 +165,12 @@ func NewClient(config *Config, remote string) (*Client, error) {
 		Name: remote,
 		Addr: r.Addr,
 	}
-	if r.Addr[0:5] != "unix:" {
+	if strings.HasPrefix(r.Addr, "unix:") {
+		// replace "unix://" with the official "unix:/var/lib/lxd/unix.socket"
 		if r.Addr == "unix://" {
 			r.Addr = fmt.Sprintf("unix:%s", shared.VarPath("unix.socket"))
 		}
-
+	} else {
 		certf, keyf, err := ensureMyCert(config.ConfigDir)
 		if err != nil {
 			return nil, err
@@ -235,15 +236,18 @@ func connectViaUnix(c *Client, addr string) error {
 	uDial := func(network, addr string) (net.Conn, error) {
 		// The arguments 'network' and 'addr' are ignored because
 		// they are the wrong information.
-		// Addr takes unix://unix.socket and tries to connect to
+		// addr is generated from BaseURL which becomes
 		// 'unix.socket:80' which is certainly not what we want.
-		var err error
-		var raddr *net.UnixAddr
-		if r.Addr[7:] == "unix://" {
-			raddr, err = net.ResolveUnixAddr("unix", r.Addr[7:])
-		} else {
-			raddr, err = net.ResolveUnixAddr("unix", r.Addr[5:])
-		}
+		// handle:
+		//   unix:///path/to/socket
+		//   unix:/path/to/socket
+		//   unix:path/to/socket
+		path := strings.TrimPrefix(r.Addr, "unix:")
+		if strings.HasPrefix(path, "///") {
+			// translate unix:///path/to, to just "/path/to"
+			path = path[2:]
+		}
+		raddr, err := net.ResolveUnixAddr("unix", path)
 		if err != nil {
 			return nil, err
 		}
@@ -276,13 +280,9 @@ func connectViaHttp(c *Client, addr, clientCert, clientKey, serverCert string) e
 	c.websocketDialer.NetDial = shared.RFC3493Dialer
 	c.websocketDialer.TLSClientConfig = tlsconfig
 
-	if addr[0:8] == "https://" {
-		c.BaseURL = "https://" + addr[8:]
-		c.BaseWSURL = "wss://" + addr[8:]
-	} else {
-		c.BaseURL = "https://" + addr
-		c.BaseWSURL = "wss://" + addr
-	}
+	justAddr := strings.TrimPrefix(addr, "https://")
+	c.BaseURL = "https://" + justAddr
+	c.BaseWSURL = "wss://" + justAddr
 	c.Transport = "https"
 	c.Http.Transport = tr
 	c.Remote = &RemoteConfig{Addr: addr}

From 73776ab6ddcf73436957291fa076b55ce923a568 Mon Sep 17 00:00:00 2001
From: John Arbash Meinel <john at arbash-meinel.com>
Date: Wed, 24 Feb 2016 16:17:09 +0400
Subject: [PATCH 6/6] If lxd fails to spawn, then cleanup would fail with
 missing variable.

Signed-off-by: John Arbash Meinel <john at arbash-meinel.com>
---
 test/main.sh | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/test/main.sh b/test/main.sh
index e13bd2b..5d976e5 100755
--- a/test/main.sh
+++ b/test/main.sh
@@ -306,6 +306,10 @@ wipe() {
   rm -Rf "${1}"
 }
 
+# Must be set before cleanup()
+TEST_CURRENT=setup
+TEST_RESULT=failure
+
 trap cleanup EXIT HUP INT TERM
 
 # Import all the testsuites
@@ -339,9 +343,6 @@ spawn_lxd "${LXD2_DIR}"
 LXD2_ADDR=$(cat "${LXD2_DIR}/lxd.addr")
 export LXD2_ADDR
 
-TEST_CURRENT=setup
-TEST_RESULT=failure
-
 # allow for running a specific set of tests
 if [ "$#" -gt 0 ]; then
   "test_${1}"


More information about the lxc-devel mailing list