The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3356

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===

From 7247204ecd39869bc05ec727ba8cc6f88bca8a39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <[email protected]>
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 <[email protected]>
---
 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?= <[email protected]>
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 <[email protected]>
---
 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?= <[email protected]>
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 <[email protected]>
---
 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
 }
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to