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
