client/lxd_images.go
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")
		}
	}

	// Send the JSON based request
	if args == nil {
		op, _, err := r.queryOperation("POST", "/images", image, "")
		if err != nil {
			return nil, err
		}

		return op, nil
	}

	// Prepare an image upload
	if args.MetaFile == nil {
		return nil, fmt.Errorf("Metadata file is required")
	}

	// Prepare the body
	var body io.Reader
	var contentType string
	if args.RootfsFile == nil {
		// If unified image, just pass it through
		body = args.MetaFile

		contentType = "application/octet-stream"
	} else {
		// If split image, we need mime encoding
		tmpfile, err := ioutil.TempFile("", "lxc_image_")
		if err != nil {
			return nil, err
		}
		defer os.Remove(tmpfile.Name())

		// Setup the multipart writer
		w := multipart.NewWriter(tmpfile)

		// Metadata file
		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
		}

		// Rootfs file
		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
		}

		// Done writing to multipart
		w.Close()

		// Figure out the size of the whole thing
		size, err := tmpfile.Seek(0, 2)
		if err != nil {
			return nil, err
		}

		_, err = tmpfile.Seek(0, 0)
		if err != nil {
			return nil, err
		}

		// Setup progress handler
		if args.ProgressHandler != nil {
			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))})
					},
				},
			}
		} else {
			body = tmpfile
		}

		contentType = w.FormDataContentType()
	}

	// Prepare the HTTP request
	reqURL, err := r.setQueryAttributes(fmt.Sprintf("%s/1.0/images", r.httpHost))
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", reqURL, body)
	if err != nil {
		return nil, err
	}

	// Setup the headers
	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())
	}

	// Set the user agent
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}

	// Send the request
	resp, err := r.do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Handle errors
	response, _, err := lxdParseResponse(resp)
	if err != nil {
		return nil, err
	}

	// Get to the operation
	respOperation, err := response.MetadataAsOperation()
	if err != nil {
		return nil, err
	}

	// Setup an Operation wrapper
	op := operation{
		Operation: *respOperation,
		r:         r,
		chActive:  make(chan bool),
	}

	return &op, 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")
		}
	}

	// Send the JSON based request
	if args == nil {
		op, _, err := r.queryOperation("POST", "/images", image, "")
		if err != nil {
			return nil, err
		}

		return op, nil
	}

	// Prepare an image upload
	if args.MetaFile == nil {
		return nil, fmt.Errorf("Metadata file is required")
	}

	// Prepare the body
	var body io.Reader
	var contentType string
	if args.RootfsFile == nil {
		// If unified image, just pass it through
		body = args.MetaFile

		contentType = "application/octet-stream"
	} else {
		// If split image, we need mime encoding
		tmpfile, err := ioutil.TempFile("", "lxc_image_")
		if err != nil {
			return nil, err
		}
		defer os.Remove(tmpfile.Name())

		// Setup the multipart writer
		w := multipart.NewWriter(tmpfile)

		// Metadata file
		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
		}

		// Rootfs file
		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
		}

		// Done writing to multipart
		w.Close()

		// Figure out the size of the whole thing
		size, err := tmpfile.Seek(0, 2)
		if err != nil {
			return nil, err
		}

		_, err = tmpfile.Seek(0, 0)
		if err != nil {
			return nil, err
		}

		// Setup progress handler
		if args.ProgressHandler != nil {
			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))})
					},
				},
			}
		} else {
			body = tmpfile
		}

		contentType = w.FormDataContentType()
	}

	// Prepare the HTTP request
	reqURL, err := r.setQueryAttributes(fmt.Sprintf("%s/1.0/images", r.httpHost))
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", reqURL, body)
	if err != nil {
		return nil, err
	}

	// Setup the headers
	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())
	}

	// Set the user agent
	if r.httpUserAgent != "" {
		req.Header.Set("User-Agent", r.httpUserAgent)
	}

	// Send the request
	resp, err := r.do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Handle errors
	response, _, err := lxdParseResponse(resp)
	if err != nil {
		return nil, err
	}

	// Get to the operation
	respOperation, err := response.MetadataAsOperation()
	if err != nil {
		return nil, err
	}

	// Setup an Operation wrapper
	op := operation{
		Operation: *respOperation,
		r:         r,
		chActive:  make(chan bool),
	}

	return &op, nil
}
// CreateImage requests that LXD creates, copies or import a new image