[lxc-devel] [lxd/master] Better handling for remote operations in new client
stgraber on Github
lxc-bot at linuxcontainers.org
Fri May 26 22:41:06 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170526/6fff1a09/attachment.bin>
-------------- next part --------------
From 7247204ecd39869bc05ec727ba8cc6f88bca8a39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Fri, 26 May 2017 17:59:39 -0400
Subject: [PATCH 1/3] client: Keep track of protocol
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/connection.go | 7 +++++--
client/lxd.go | 3 ++-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/client/connection.go b/client/connection.go
index fab941fd5..02e0e6aa6 100644
--- a/client/connection.go
+++ b/client/connection.go
@@ -48,9 +48,10 @@ func ConnectLXD(url string, args *ConnectionArgs) (ContainerServer, error) {
// Initialize the client struct
server := ProtocolLXD{
+ httpCertificate: args.TLSServerCert,
httpHost: url,
+ httpProtocol: "https",
httpUserAgent: args.UserAgent,
- httpCertificate: args.TLSServerCert,
}
// Setup the HTTP client
@@ -84,6 +85,7 @@ func ConnectLXDUnix(path string, args *ConnectionArgs) (ContainerServer, error)
// Initialize the client struct
server := ProtocolLXD{
httpHost: "http://unix.socket",
+ httpProtocol: "unix",
httpUserAgent: args.UserAgent,
}
@@ -129,9 +131,10 @@ func ConnectPublicLXD(url string, args *ConnectionArgs) (ImageServer, error) {
// Initialize the client struct
server := ProtocolLXD{
+ httpCertificate: args.TLSServerCert,
httpHost: url,
+ httpProtocol: "https",
httpUserAgent: args.UserAgent,
- httpCertificate: args.TLSServerCert,
}
// Setup the HTTP client
diff --git a/client/lxd.go b/client/lxd.go
index 3f8663b38..90b11e6dc 100644
--- a/client/lxd.go
+++ b/client/lxd.go
@@ -22,9 +22,10 @@ type ProtocolLXD struct {
eventListenersLock sync.Mutex
http *http.Client
+ httpCertificate string
httpHost string
+ httpProtocol string
httpUserAgent string
- httpCertificate string
}
// RawQuery allows directly querying the LXD API
From 9bfebc0d736464657aa1d3f47635b47ed93c3b3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Fri, 26 May 2017 18:01:23 -0400
Subject: [PATCH 2/3] client: Implement remote operations
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/operations.go | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/client/operations.go b/client/operations.go
index 7f381a011..6520556cb 100644
--- a/client/operations.go
+++ b/client/operations.go
@@ -224,3 +224,44 @@ func (op *Operation) extractOperation(data interface{}) *api.Operation {
return &newOp
}
+
+// The RemoteOperation type represents an ongoing LXD operation between two servers
+type RemoteOperation struct {
+ targetOp *Operation
+
+ handlers []func(api.Operation)
+
+ chDone chan bool
+ err error
+}
+
+// Wait lets you wait until the operation reaches a final state
+func (op *RemoteOperation) Wait() error {
+ <-op.chDone
+ return op.err
+}
+
+// AddHandler adds a function to be called whenever an event is received
+func (op *RemoteOperation) AddHandler(function func(api.Operation)) (*EventTarget, error) {
+ var err error
+ var target *EventTarget
+
+ // Attach to the existing target operation
+ if op.targetOp != nil {
+ target, err = op.targetOp.AddHandler(function)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ // Generate a mock EventTarget
+ target = &EventTarget{
+ function: func(interface{}) { function(api.Operation{}) },
+ types: []string{"operation"},
+ }
+ }
+
+ // Add the handler to our list
+ op.handlers = append(op.handlers, function)
+
+ return target, nil
+}
From d783a2c6e681b187476d07ab30f6564d1a55e4b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Fri, 26 May 2017 18:30:45 -0400
Subject: [PATCH 3/3] client: Use RemoteOperation for CopyImage
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/interfaces.go | 2 +-
client/lxd.go | 21 +++++++++++++++
client/lxd_images.go | 61 +++++++++++++++++++++++++++++++++++++++---
client/simplestreams_images.go | 20 ++++++++++++--
4 files changed, 98 insertions(+), 6 deletions(-)
diff --git a/client/interfaces.go b/client/interfaces.go
index 2cc6d2297..f11adce0e 100644
--- a/client/interfaces.go
+++ b/client/interfaces.go
@@ -25,7 +25,7 @@ type ImageServer interface {
GetImageAlias(name string) (alias *api.ImageAliasesEntry, ETag string, err error)
- CopyImage(image api.Image, target ContainerServer, args *ImageCopyArgs) (op *Operation, err error)
+ CopyImage(image api.Image, target ContainerServer, args *ImageCopyArgs) (op *RemoteOperation, err error)
}
// The ContainerServer type represents a full featured LXD server.
diff --git a/client/lxd.go b/client/lxd.go
index 90b11e6dc..6a5892353 100644
--- a/client/lxd.go
+++ b/client/lxd.go
@@ -222,3 +222,24 @@ func (r *ProtocolLXD) websocket(path string) (*websocket.Conn, error) {
return r.rawWebsocket(url)
}
+
+// getServerUrls returns a prioritized list of potential URLs for the server
+func (r *ProtocolLXD) getServerUrls() ([]string, error) {
+ if len(r.server.Environment.Addresses) == 0 {
+ return nil, fmt.Errorf("Source server isn't reachable over the network")
+ }
+
+ urls := []string{}
+ if r.httpProtocol == "https" {
+ urls = append(urls, r.httpHost)
+ }
+
+ for _, addr := range r.server.Environment.Addresses {
+ url := fmt.Sprintf("https://%s", addr)
+ if !shared.StringInSlice(url, urls) {
+ urls = append(urls, url)
+ }
+ }
+
+ return urls, nil
+}
diff --git a/client/lxd_images.go b/client/lxd_images.go
index bea83bd09..1abf5f7a4 100644
--- a/client/lxd_images.go
+++ b/client/lxd_images.go
@@ -431,15 +431,70 @@ func (r *ProtocolLXD) CreateImage(image api.ImagesPost, args *ImageCreateArgs) (
return &op, nil
}
+// tryCopyImage iterates through the source server URLs until one lets it download the image
+func (r *ProtocolLXD) tryCopyImage(target ContainerServer, req api.ImagesPost, urls []string) (*RemoteOperation, error) {
+ rop := RemoteOperation{
+ chDone: make(chan bool),
+ }
+
+ // Forward targetOp to remote op
+ go func() {
+ success := false
+ errors := []string{}
+ for _, serverURL := range urls {
+ req.Source.Server = serverURL
+
+ op, err := target.CreateImage(req, nil)
+ if err != nil {
+ errors = append(errors, fmt.Sprintf("%s: %v", serverURL, err))
+ continue
+ }
+
+ rop.targetOp = op
+
+ for _, handler := range rop.handlers {
+ rop.targetOp.AddHandler(handler)
+ }
+
+ err = rop.targetOp.Wait()
+ if err != nil {
+ errors = append(errors, fmt.Sprintf("%s: %v", serverURL, err))
+ continue
+ }
+
+ success = true
+ break
+ }
+
+ if !success {
+ rop.err = fmt.Errorf("%s", strings.Join(errors, "\n"))
+ }
+
+ close(rop.chDone)
+ }()
+
+ return &rop, nil
+}
+
// CopyImage copies an existing image to a remote server. Additional options can be passed using ImageCopyArgs
-func (r *ProtocolLXD) CopyImage(image api.Image, target ContainerServer, args *ImageCopyArgs) (*Operation, error) {
+func (r *ProtocolLXD) CopyImage(image api.Image, target ContainerServer, args *ImageCopyArgs) (*RemoteOperation, error) {
+ // Sanity checks
+ if r == target {
+ return nil, fmt.Errorf("The source and target servers must be different")
+ }
+
+ // Get a list of addresses for the source server
+ urls, err := r.getServerUrls()
+ if err != nil {
+ return nil, err
+ }
+
// Prepare the copy request
req := api.ImagesPost{
Source: &api.ImagesPostSource{
ImageSource: api.ImageSource{
Certificate: r.httpCertificate,
Protocol: "lxd",
- Server: r.httpHost,
},
Fingerprint: image.Fingerprint,
Mode: "pull",
@@ -471,7 +526,7 @@ func (r *ProtocolLXD) CopyImage(image api.Image, target ContainerServer, args *I
}
}
- return target.CreateImage(req, nil)
+ return r.tryCopyImage(target, req, urls)
}
// UpdateImage updates the image definition
diff --git a/client/simplestreams_images.go b/client/simplestreams_images.go
index f04a0801a..3d9b7ecef 100644
--- a/client/simplestreams_images.go
+++ b/client/simplestreams_images.go
@@ -144,7 +144,7 @@ func (r *ProtocolSimpleStreams) GetImageAlias(name string) (*api.ImageAliasesEnt
}
// CopyImage copies an existing image to a remote server. Additional options can be passed using ImageCopyArgs
-func (r *ProtocolSimpleStreams) CopyImage(image api.Image, target ContainerServer, args *ImageCopyArgs) (*Operation, error) {
+func (r *ProtocolSimpleStreams) CopyImage(image api.Image, target ContainerServer, args *ImageCopyArgs) (*RemoteOperation, error) {
// Prepare the copy request
req := api.ImagesPost{
Source: &api.ImagesPostSource{
@@ -173,5 +173,21 @@ func (r *ProtocolSimpleStreams) CopyImage(image api.Image, target ContainerServe
}
}
- return target.CreateImage(req, nil)
+ op, err := target.CreateImage(req, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ rop := RemoteOperation{
+ targetOp: op,
+ chDone: make(chan bool),
+ }
+
+ // Forward targetOp to remote op
+ go func() {
+ rop.err = rop.targetOp.Wait()
+ close(rop.chDone)
+ }()
+
+ return &rop, nil
}
More information about the lxc-devel
mailing list