func (r *ProtocolLXD) tryCopyImage(req api.ImagesPost, urls []string) (RemoteOperation, error) {
	if len(urls) == 0 {
		return nil, fmt.Errorf("The source server isn't listening on the network")
	}
	rop := remoteOperation{
		chDone: make(chan bool),
	}
	if !r.HasExtension("image_create_aliases") && req.Aliases != nil {
		rop.chPost = make(chan bool)
		go func() {
			defer close(rop.chPost)
			<-rop.chDone
			if rop.err != nil {
				return
			}
			op, err := rop.GetTarget()
			if err != nil {
				return
			}
			fingerprint := op.Metadata["fingerprint"].(string)
			for _, entry := range req.Aliases {
				alias := api.ImageAliasesPost{}
				alias.Name = entry.Name
				alias.Target = fingerprint
				r.CreateImageAlias(alias)
			}
		}()
	}
	go func() {
		success := false
		errors := map[string]error{}
		for _, serverURL := range urls {
			req.Source.Server = serverURL
			op, err := r.CreateImage(req, nil)
			if err != nil {
				errors[serverURL] = err
				continue
			}
			rop.targetOp = op
			for _, handler := range rop.handlers {
				rop.targetOp.AddHandler(handler)
			}
			err = rop.targetOp.Wait()
			if err != nil {
				errors[serverURL] = err
				continue
			}
			success = true
			break
		}
		if !success {
			rop.err = remoteOperationError("Failed remote image download", errors)
		}
		close(rop.chDone)
	}()
	return &rop, nil
}
tryCopyImage iterates through the source server URLs until one lets it download the image
package lxd
import (
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strings"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/cancel"
	"github.com/lxc/lxd/shared/ioprogress"
)
func (r *ProtocolLXD) GetImages() ([]api.Image, error) {
	images := []api.Image{}
	_, err := r.queryStruct("GET", "/images?recursion=1", nil, "", &images)
	if err != nil {
		return nil, err
	}
	return images, nil
}
func (r *ProtocolLXD) GetImageFingerprints() ([]string, error) {
	urls := []string{}
	_, err := r.queryStruct("GET", "/images", nil, "", &urls)
	if err != nil {
		return nil, err
	}
	fingerprints := []string{}
	for _, url := range urls {
		fields := strings.Split(url, "/images/")
		fingerprints = append(fingerprints, fields[len(fields)-1])
	}
	return fingerprints, nil
}
func (r *ProtocolLXD) GetImage(fingerprint string) (*api.Image, string, error) {
	return r.GetPrivateImage(fingerprint, "")
}
func (r *ProtocolLXD) GetImageFile(fingerprint string, req ImageFileRequest) (*ImageFileResponse, error) {
	return r.GetPrivateImageFile(fingerprint, "", req)
}
func (r *ProtocolLXD) GetImageSecret(fingerprint string) (string, error) {
	op, err := r.CreateImageSecret(fingerprint)
	if err != nil {
		return "", err
	}
	opAPI := op.Get()
	return opAPI.Metadata["secret"].(string), nil
}
func (r *ProtocolLXD) GetPrivateImage(fingerprint string, secret string) (*api.Image, string, error) {
	image := api.Image{}
	path := fmt.Sprintf("/images/%s", url.QueryEscape(fingerprint))
	if secret != "" {
		path = fmt.Sprintf("%s?secret=%s", path, url.QueryEscape(secret))
	}
	etag, err := r.queryStruct("GET", path, nil, "", &image)
	if err != nil {
		return nil, "", err
	}
	return &image, etag, nil
}
func (r *ProtocolLXD) GetPrivateImageFile(fingerprint string, secret string, req ImageFileRequest) (*ImageFileResponse, error) {
	if req.MetaFile == nil && req.RootfsFile == nil {
		return nil, fmt.Errorf("No file requested")
	}
	resp := ImageFileResponse{}
	uri := fmt.Sprintf("%s/1.0/images/%s/export", r.httpHost, url.QueryEscape(fingerprint))
	if secret != "" {
		uri = fmt.Sprintf("%s?secret=%s", uri, url.QueryEscape(secret))
	}
	request, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		return nil, err
	}
	if r.httpUserAgent != "" {
		request.Header.Set("User-Agent", r.httpUserAgent)
	}
	response, doneCh, err := cancel.CancelableDownload(req.Canceler, r.http, request)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	defer close(doneCh)
	if response.StatusCode != http.StatusOK {
		_, _, err := r.parseResponse(response)
		if err != nil {
			return nil, err
		}
	}
	ctype, ctypeParams, err := mime.ParseMediaType(response.Header.Get("Content-Type"))
	if err != nil {
		ctype = "application/octet-stream"
	}
	body := response.Body
	if req.ProgressHandler != nil {
		body = &ioprogress.ProgressReader{
			ReadCloser: response.Body,
			Tracker: &ioprogress.ProgressTracker{
				Length: response.ContentLength,
				Handler: func(percent int64, speed int64) {
					req.ProgressHandler(ioprogress.ProgressData{Text: fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2))})
				},
			},
		}
	}
	sha256 := sha256.New()
	if ctype == "multipart/form-data" {
		if req.MetaFile == nil || req.RootfsFile == nil {
			return nil, fmt.Errorf("Multi-part image but only one target file provided")
		}
		mr := multipart.NewReader(body, ctypeParams["boundary"])
		part, err := mr.NextPart()
		if err != nil {
			return nil, err
		}
		if part.FormName() != "metadata" {
			return nil, fmt.Errorf("Invalid multipart image")
		}
		size, err := io.Copy(io.MultiWriter(req.MetaFile, sha256), part)
		if err != nil {
			return nil, err
		}
		resp.MetaSize = size
		resp.MetaName = part.FileName()
		part, err = mr.NextPart()
		if err != nil {
			return nil, err
		}
		if part.FormName() != "rootfs" {
			return nil, fmt.Errorf("Invalid multipart image")
		}
		size, err = io.Copy(io.MultiWriter(req.RootfsFile, sha256), part)
		if err != nil {
			return nil, err
		}
		resp.RootfsSize = size
		resp.RootfsName = part.FileName()
		hash := fmt.Sprintf("%x", sha256.Sum(nil))
		if !strings.HasPrefix(hash, fingerprint) {
			return nil, fmt.Errorf("Image fingerprint doesn't match. Got %s expected %s", hash, fingerprint)
		}
		return &resp, nil
	}
	_, cdParams, err := mime.ParseMediaType(response.Header.Get("Content-Disposition"))
	if err != nil {
		return nil, err
	}
	filename, ok := cdParams["filename"]
	if !ok {
		return nil, fmt.Errorf("No filename in Content-Disposition header")
	}
	size, err := io.Copy(io.MultiWriter(req.MetaFile, sha256), body)
	if err != nil {
		return nil, err
	}
	resp.MetaSize = size
	resp.MetaName = filename
	hash := fmt.Sprintf("%x", sha256.Sum(nil))
	if !strings.HasPrefix(hash, fingerprint) {
		return nil, fmt.Errorf("Image fingerprint doesn't match. Got %s expected %s", hash, fingerprint)
	}
	return &resp, nil
}
func (r *ProtocolLXD) GetImageAliases() ([]api.ImageAliasesEntry, error) {
	aliases := []api.ImageAliasesEntry{}
	_, err := r.queryStruct("GET", "/images/aliases?recursion=1", nil, "", &aliases)
	if err != nil {
		return nil, err
	}
	return aliases, nil
}
func (r *ProtocolLXD) GetImageAliasNames() ([]string, error) {
	urls := []string{}
	_, err := r.queryStruct("GET", "/images/aliases", nil, "", &urls)
	if err != nil {
		return nil, err
	}
	names := []string{}
	for _, url := range urls {
		fields := strings.Split(url, "/images/aliases/")
		names = append(names, fields[len(fields)-1])
	}
	return names, nil
}
func (r *ProtocolLXD) GetImageAlias(name string) (*api.ImageAliasesEntry, string, error) {
	alias := api.ImageAliasesEntry{}
	etag, err := r.queryStruct("GET", fmt.Sprintf("/images/aliases/%s", url.QueryEscape(name)), nil, "", &alias)
	if err != nil {
		return nil, "", err
	}
	return &alias, etag, nil
}
func (r *ProtocolLXD) CreateImage(image api.ImagesPost, args *ImageCreateArgs) (Operation, error) {
	if image.CompressionAlgorithm != "" {
		if !r.HasExtension("image_compression_algorithm") {
			return nil, fmt.Errorf("The server is missing the required \"image_compression_algorithm\" API extension")
		}
	}
	if args == nil {
		op, _, err := r.queryOperation("POST", "/images", image, "")
		if err != nil {
			return nil, err
		}
		return op, nil
	}
	if args.MetaFile == nil {
		return nil, fmt.Errorf("Metadata file is required")
	}
	var body io.Reader
	var contentType string
	if args.RootfsFile == nil {
		body = args.MetaFile
		contentType = "application/octet-stream"
	} else {
		tmpfile, err := ioutil.TempFile("", "lxc_image_")
		if err != nil {
			return nil, err
		}
		defer os.Remove(tmpfile.Name())
		w := multipart.NewWriter(tmpfile)
		fw, err := w.CreateFormFile("metadata", args.MetaName)
		if err != nil {
			return nil, err
		}
		_, err = io.Copy(fw, args.MetaFile)
		if err != nil {
			return nil, err
		}
		fw, err = w.CreateFormFile("rootfs", args.RootfsName)
		if err != nil {
			return nil, err
		}
		_, err = io.Copy(fw, args.RootfsFile)
		if err != nil {
			return nil, err
		}
		w.Close()
		size, err := tmpfile.Seek(0, 2)
		if err != nil {
			return nil, err
		}
		_, err = tmpfile.Seek(0, 0)
		if err != nil {
			return nil, err
		}
		body = &ioprogress.ProgressReader{
			ReadCloser: tmpfile,
			Tracker: &ioprogress.ProgressTracker{
				Length: size,
				Handler: func(percent int64, speed int64) {
					args.ProgressHandler(ioprogress.ProgressData{Text: fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2))})
				},
			},
		}
		contentType = w.FormDataContentType()
	}
	reqURL := fmt.Sprintf("%s/1.0/images", r.httpHost)
	req, err := http.NewRequest("POST", reqURL, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", contentType)
	if image.Public {
		req.Header.Set("X-LXD-public", "true")
	}
	if image.Filename != "" {
		req.Header.Set("X-LXD-filename", image.Filename)
	}
	if len(image.Properties) > 0 {
		imgProps := url.Values{}
		for k, v := range image.Properties {
			imgProps.Set(k, v)
		}
		req.Header.Set("X-LXD-properties", imgProps.Encode())
	}
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}
	resp, err := r.do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	response, _, err := r.parseResponse(resp)
	if err != nil {
		return nil, err
	}
	respOperation, err := response.MetadataAsOperation()
	if err != nil {
		return nil, err
	}
	op := operation{
		Operation: *respOperation,
		r:         r,
		chActive:  make(chan bool),
	}
	return &op, nil
}