[lxc-devel] [lxd/master] LXD client API tweaks

stgraber on Github lxc-bot at linuxcontainers.org
Wed Feb 24 15:40:21 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 399 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160224/676d6aa8/attachment.bin>
-------------- next part --------------
From d4d4c43397d39921b029785bf5b0158434a5f87f 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/7] 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 c32524ba141cd710a3edc5f6f6af667b44777e46 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/7] 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 5a3bcb085f79b85fff129ae8c2eac57ee704bdc9 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/7] 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 965dd06ed55310a767275c42494b53615304af1d 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/7] 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 8f0d680aff44c1bb467c00ec102d0784cf31690c 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/7] 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 56c59c174707fc015fd4d2b5f96bc0f6c49e3051 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/7] 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}"

From 4b466c2a5112637e54751567f8642a917047454b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 24 Feb 2016 10:37:47 -0500
Subject: [PATCH 7/7] Fix invalid unix socket addresses
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 client.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/client.go b/client.go
index d9da65d..2413cac 100644
--- a/client.go
+++ b/client.go
@@ -165,10 +165,11 @@ func NewClient(config *Config, remote string) (*Client, error) {
 		Name: remote,
 		Addr: r.Addr,
 	}
+
 	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"))
+		if info.Addr == "unix://" {
+			info.Addr = fmt.Sprintf("unix:%s", shared.VarPath("unix.socket"))
 		}
 	} else {
 		certf, keyf, err := ensureMyCert(config.ConfigDir)


More information about the lxc-devel mailing list