Exemple #1
0
func imageBuildFromInfo(d *Daemon, info shared.ImageInfo) (metadata map[string]string, err error) {
	err = d.Storage.ImageCreate(info.Fingerprint)
	if err != nil {
		return metadata, err
	}

	err = dbInsertImage(
		d,
		info.Fingerprint,
		info.Filename,
		info.Size,

		// FIXME: InterfaceToBool is there for backward compatibility
		shared.InterfaceToBool(info.Public),

		info.Architecture,
		info.CreationDate,
		info.ExpiryDate,
		info.Properties)
	if err != nil {
		return metadata, err
	}

	metadata = make(map[string]string)
	metadata["fingerprint"] = info.Fingerprint
	metadata["size"] = strconv.FormatInt(info.Size, 10)

	return metadata, nil
}
Exemple #2
0
func showImages(images []shared.ImageInfo) error {
	data := [][]string{}
	for _, image := range images {
		shortest := shortestAlias(image.Aliases)
		if len(image.Aliases) > 1 {
			shortest = fmt.Sprintf(gettext.Gettext("%s (%d more)"), shortest, len(image.Aliases)-1)
		}
		fp := image.Fingerprint[0:12]
		public := gettext.Gettext("no")
		description := findDescription(image.Properties)
		if shared.InterfaceToBool(image.Public) {
			public = gettext.Gettext("yes")
		}
		const layout = "Jan 2, 2006 at 3:04pm (MST)"
		uploaded := time.Unix(image.UploadDate, 0).Format(layout)
		arch, _ := shared.ArchitectureName(image.Architecture)
		data = append(data, []string{shortest, fp, public, description, arch, uploaded})
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetColWidth(50)
	table.SetHeader([]string{
		gettext.Gettext("ALIAS"),
		gettext.Gettext("FINGERPRINT"),
		gettext.Gettext("PUBLIC"),
		gettext.Gettext("DESCRIPTION"),
		gettext.Gettext("ARCH"),
		gettext.Gettext("UPLOAD DATE")})
	sort.Sort(ByName(data))
	table.AppendBulk(data)
	table.Render()

	return nil
}
Exemple #3
0
func showImages(images []shared.ImageInfo, filters []string) error {
	data := [][]string{}
	for _, image := range images {
		if !imageShouldShow(filters, &image) {
			continue
		}

		shortest := shortestAlias(image.Aliases)
		if len(image.Aliases) > 1 {
			shortest = fmt.Sprintf(i18n.G("%s (%d more)"), shortest, len(image.Aliases)-1)
		}
		fp := image.Fingerprint[0:12]
		public := i18n.G("no")
		description := findDescription(image.Properties)

		// FIXME: InterfaceToBool is there for backward compatibility
		if shared.InterfaceToBool(image.Public) {
			public = i18n.G("yes")
		}

		const layout = "Jan 2, 2006 at 3:04pm (MST)"
		uploaded := time.Unix(image.UploadDate, 0).Format(layout)
		arch, _ := shared.ArchitectureName(image.Architecture)
		size := fmt.Sprintf("%.2fMB", float64(image.Size)/1024.0/1024.0)
		data = append(data, []string{shortest, fp, public, description, arch, size, uploaded})
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetColWidth(50)
	table.SetHeader([]string{
		i18n.G("ALIAS"),
		i18n.G("FINGERPRINT"),
		i18n.G("PUBLIC"),
		i18n.G("DESCRIPTION"),
		i18n.G("ARCH"),
		i18n.G("SIZE"),
		i18n.G("UPLOAD DATE")})
	sort.Sort(ByName(data))
	table.AppendBulk(data)
	table.Render()

	return nil
}
Exemple #4
0
func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliases []string, public bool, progressHandler func(progress string)) error {
	fingerprint := c.GetAlias(image)
	if fingerprint == "" {
		fingerprint = image
	}

	info, err := c.GetImageInfo(fingerprint)
	if err != nil {
		return err
	}

	source := shared.Jmap{
		"type":        "image",
		"mode":        "pull",
		"server":      c.BaseURL,
		"fingerprint": fingerprint}

	// FIXME: InterfaceToBool is there for backward compatibility
	if !shared.InterfaceToBool(info.Public) {
		var secret string

		resp, err := c.post("images/"+fingerprint+"/secret", nil, Async)
		if err != nil {
			return err
		}

		op, err := resp.MetadataAsOperation()
		if err == nil && op.Metadata != nil {
			secret, err = op.Metadata.GetString("secret")
			if err != nil {
				return err
			}
		} else {
			// FIXME: This is a backward compatibility codepath
			md := secretMd{}
			if err := json.Unmarshal(resp.Metadata, &md); err != nil {
				return err
			}

			secret = md.Secret
		}

		source["secret"] = secret
	}

	addresses, err := c.Addresses()
	if err != nil {
		return err
	}

	operation := ""
	handler := func(msg interface{}) {
		if msg == nil {
			return
		}

		event := msg.(map[string]interface{})
		if event["type"].(string) != "operation" {
			return
		}

		if event["metadata"] == nil {
			return
		}

		md := event["metadata"].(map[string]interface{})
		if !strings.HasSuffix(operation, md["id"].(string)) {
			return
		}

		if md["metadata"] == nil {
			return
		}

		opMd := md["metadata"].(map[string]interface{})
		_, ok := opMd["download_progress"]
		if ok {
			progressHandler(opMd["download_progress"].(string))
		}
	}

	if progressHandler != nil {
		go dest.Monitor([]string{"operation"}, handler)
	}

	for _, addr := range addresses {
		sourceUrl := "https://" + addr

		source["server"] = sourceUrl
		body := shared.Jmap{"public": public, "source": source}

		resp, err := dest.post("images", body, Async)
		if err != nil {
			continue
		}

		operation = resp.Operation

		err = dest.WaitForSuccess(resp.Operation)
		if err != nil {
			return err
		}

		break
	}

	if err != nil {
		return err
	}

	/* copy aliases from source image */
	if copy_aliases {
		for _, alias := range info.Aliases {
			dest.DeleteAlias(alias.Name)
			err = dest.PostAlias(alias.Name, alias.Description, info.Fingerprint)
			if err != nil {
				fmt.Printf(i18n.G("Error adding alias %s")+"\n", alias.Name)
			}
		}
	}

	/* add new aliases */
	for _, alias := range aliases {
		dest.DeleteAlias(alias)
		err = dest.PostAlias(alias, alias, info.Fingerprint)
		if err != nil {
			fmt.Printf(i18n.G("Error adding alias %s")+"\n", alias)
		}
	}

	return nil
}
Exemple #5
0
// Init creates a container from either a fingerprint or an alias; you must
// provide at least one.
func (c *Client) Init(name string, imgremote string, image string, profiles *[]string, config map[string]string, ephem bool) (*Response, error) {
	var tmpremote *Client
	var err error

	serverStatus, err := c.ServerStatus()
	if err != nil {
		return nil, err
	}
	architectures := serverStatus.Environment.Architectures

	source := shared.Jmap{"type": "image"}

	if image == "" {
		return nil, fmt.Errorf(i18n.G("You must provide an image hash or alias name."))
	}

	if imgremote != c.Name {
		source["type"] = "image"
		source["mode"] = "pull"
		tmpremote, err = NewClient(&c.Config, imgremote)
		if err != nil {
			return nil, err
		}

		fingerprint := tmpremote.GetAlias(image)
		if fingerprint == "" {
			fingerprint = image
		}

		imageinfo, err := tmpremote.GetImageInfo(fingerprint)
		if err != nil {
			return nil, err
		}

		if len(architectures) != 0 && !shared.IntInSlice(imageinfo.Architecture, architectures) {
			return nil, fmt.Errorf(i18n.G("The image architecture is incompatible with the target server"))
		}

		// FIXME: InterfaceToBool is there for backward compatibility
		if !shared.InterfaceToBool(imageinfo.Public) {
			var secret string

			resp, err := tmpremote.post("images/"+fingerprint+"/secret", nil, Async)
			if err != nil {
				return nil, err
			}

			op, err := resp.MetadataAsOperation()
			if err == nil && op.Metadata != nil {
				secret, err = op.Metadata.GetString("secret")
				if err != nil {
					return nil, err
				}
			} else {
				// FIXME: This is a backward compatibility codepath
				md := secretMd{}
				if err := json.Unmarshal(resp.Metadata, &md); err != nil {
					return nil, err
				}

				secret = md.Secret
			}

			source["secret"] = secret
		}

		source["server"] = tmpremote.BaseURL
		source["fingerprint"] = fingerprint
	} else {
		fingerprint := c.GetAlias(image)
		if fingerprint == "" {
			fingerprint = image
		}

		imageinfo, err := c.GetImageInfo(fingerprint)
		if err != nil {
			return nil, fmt.Errorf(i18n.G("can't get info for image '%s': %s"), image, err)
		}

		if len(architectures) != 0 && !shared.IntInSlice(imageinfo.Architecture, architectures) {
			return nil, fmt.Errorf(i18n.G("The image architecture is incompatible with the target server"))
		}
		source["fingerprint"] = fingerprint
	}

	body := shared.Jmap{"source": source}

	if name != "" {
		body["name"] = name
	}

	if profiles != nil {
		body["profiles"] = *profiles
	}

	if config != nil {
		body["config"] = config
	}

	if ephem {
		body["ephemeral"] = ephem
	}

	var resp *Response

	if imgremote != c.Name {
		var addresses []string
		addresses, err = tmpremote.Addresses()
		if err != nil {
			return nil, err
		}

		for _, addr := range addresses {
			body["source"].(shared.Jmap)["server"] = "https://" + addr

			resp, err = c.post("containers", body, Async)
			if err != nil {
				continue
			}

			break
		}
	} else {
		resp, err = c.post("containers", body, Async)
	}

	if err != nil {
		if LXDErrors[http.StatusNotFound] == err {
			return nil, fmt.Errorf("image doesn't exist")
		}
		return nil, err
	}

	return resp, nil
}
Exemple #6
0
func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliases []string, public bool) error {
	fingerprint := c.GetAlias(image)
	if fingerprint == "" {
		fingerprint = image
	}

	info, err := c.GetImageInfo(fingerprint)
	if err != nil {
		return err
	}

	source := shared.Jmap{
		"type":        "image",
		"mode":        "pull",
		"server":      c.BaseURL,
		"fingerprint": fingerprint}

	if !shared.InterfaceToBool(info.Public) {
		var operation string

		resp, err := c.post("images/"+fingerprint+"/secret", nil, Async)
		if err != nil {
			return err
		}

		toScan := strings.Replace(resp.Operation, "/", " ", -1)
		version := ""
		count, err := fmt.Sscanf(toScan, " %s operations %s", &version, &operation)
		if err != nil || count != 2 {
			return err
		}

		md := secretMd{}
		if err := json.Unmarshal(resp.Metadata, &md); err != nil {
			return err
		}

		source["secret"] = md.Secret
	}

	addresses, err := c.Addresses()
	if err != nil {
		return err
	}

	for _, addr := range addresses {
		sourceUrl := "https://" + addr

		source["server"] = sourceUrl
		body := shared.Jmap{"public": public, "source": source}

		resp, err := dest.post("images", body, Async)
		if err != nil {
			continue
		}

		err = dest.WaitForSuccess(resp.Operation)
		if err != nil {
			return err
		}

		break
	}

	if err != nil {
		return err
	}

	/* copy aliases from source image */
	if copy_aliases {
		for _, alias := range info.Aliases {
			dest.DeleteAlias(alias.Name)
			err = dest.PostAlias(alias.Name, alias.Description, info.Fingerprint)
			if err != nil {
				fmt.Printf(gettext.Gettext("Error adding alias %s")+"\n", alias.Name)
			}
		}
	}

	/* add new aliases */
	for _, alias := range aliases {
		dest.DeleteAlias(alias)
		err = dest.PostAlias(alias, alias, info.Fingerprint)
		if err != nil {
			fmt.Printf(gettext.Gettext("Error adding alias %s")+"\n", alias)
		}
	}

	return nil
}
Exemple #7
0
// Init creates a container from either a fingerprint or an alias; you must
// provide at least one.
func (c *Client) Init(name string, imgremote string, image string, profiles *[]string, config map[string]string, ephem bool) (*Response, error) {
	var operation string
	var tmpremote *Client
	var err error

	serverStatus, err := c.ServerStatus()
	if err != nil {
		return nil, err
	}
	architectures := serverStatus.Environment.Architectures

	source := shared.Jmap{"type": "image"}

	if image == "" {
		return nil, fmt.Errorf(gettext.Gettext("You must provide an image hash or alias name."))
	}

	if imgremote != c.name {
		source["type"] = "image"
		source["mode"] = "pull"
		tmpremote, err = NewClient(&c.config, imgremote)
		if err != nil {
			return nil, err
		}

		fingerprint := tmpremote.GetAlias(image)
		if fingerprint == "" {
			fingerprint = image
		}

		imageinfo, err := tmpremote.GetImageInfo(fingerprint)
		if err != nil {
			return nil, err
		}

		if len(architectures) != 0 && !shared.IntInSlice(imageinfo.Architecture, architectures) {
			return nil, fmt.Errorf(gettext.Gettext("The image architecture is incompatible with the target server"))
		}

		if !shared.InterfaceToBool(imageinfo.Public) {
			resp, err := tmpremote.post("images/"+fingerprint+"/secret", nil, Async)
			if err != nil {
				return nil, err
			}

			toScan := strings.Replace(resp.Operation, "/", " ", -1)
			version := ""
			count, err := fmt.Sscanf(toScan, " %s operations %s", &version, &operation)
			if err != nil || count != 2 {
				return nil, err
			}

			md := secretMd{}
			if err := json.Unmarshal(resp.Metadata, &md); err != nil {
				return nil, err
			}

			source["secret"] = md.Secret
		}

		source["server"] = tmpremote.BaseURL
		source["fingerprint"] = fingerprint
	} else {
		fingerprint := c.GetAlias(image)
		if fingerprint == "" {
			fingerprint = image
		}

		imageinfo, err := c.GetImageInfo(fingerprint)
		if err != nil {
			return nil, fmt.Errorf(gettext.Gettext("can't get info for image '%s': %s"), image, err)
		}

		if len(architectures) != 0 && !shared.IntInSlice(imageinfo.Architecture, architectures) {
			return nil, fmt.Errorf(gettext.Gettext("The image architecture is incompatible with the target server"))
		}
		source["fingerprint"] = fingerprint
	}

	body := shared.Jmap{"source": source}

	if name != "" {
		body["name"] = name
	}

	if profiles != nil {
		body["profiles"] = *profiles
	}

	if config != nil {
		body["config"] = config
	}

	if ephem {
		body["ephemeral"] = ephem
	}

	var resp *Response

	if imgremote != c.name {
		var addresses []string
		addresses, err = tmpremote.Addresses()
		if err != nil {
			return nil, err
		}

		for _, addr := range addresses {
			body["source"].(shared.Jmap)["server"] = "https://" + addr

			resp, err = c.post("containers", body, Async)
			if err != nil {
				continue
			}

			break
		}
	} else {
		resp, err = c.post("containers", body, Async)
	}

	if operation != "" {
		_, _ = tmpremote.delete("operations/"+operation, nil, Sync)
	}

	if err != nil {
		if LXDErrors[http.StatusNotFound] == err {
			return nil, fmt.Errorf("image doesn't exist")
		}
		return nil, err
	}

	return resp, nil
}
Exemple #8
0
func (c *imageCmd) run(config *lxd.Config, args []string) error {
	var remote string

	if len(args) < 1 {
		return errArgs
	}

	switch args[0] {
	case "alias":
		if len(args) < 2 {
			return errArgs
		}
		return doImageAlias(config, args)

	case "copy":
		/* copy [<remote>:]<image> [<rmeote>:]<image> */
		if len(args) != 3 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		destRemote, outName := config.ParseRemoteAndContainer(args[2])
		if outName != "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}
		dest, err := lxd.NewClient(config, destRemote)
		if err != nil {
			return err
		}
		image := dereferenceAlias(d, inName)
		return d.CopyImage(image, dest, copyAliases, addAliases, publicImage)

	case "delete":
		/* delete [<remote>:]<image> */
		if len(args) < 2 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}
		image := dereferenceAlias(d, inName)
		err = d.DeleteImage(image)
		return err

	case "info":
		if len(args) < 2 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		image := dereferenceAlias(d, inName)
		info, err := d.GetImageInfo(image)
		if err != nil {
			return err
		}
		fmt.Printf(gettext.Gettext("Fingerprint: %s")+"\n", info.Fingerprint)
		public := gettext.Gettext("no")
		if shared.InterfaceToBool(info) {
			public = gettext.Gettext("yes")
		}
		fmt.Printf(gettext.Gettext("Size: %.2vMB")+"\n", float64(info.Size)/1024.0/1024.0)
		arch, _ := shared.ArchitectureName(info.Architecture)
		fmt.Printf(gettext.Gettext("Architecture: %s")+"\n", arch)
		fmt.Printf(gettext.Gettext("Public: %s")+"\n", public)
		fmt.Printf(gettext.Gettext("Timestamps:") + "\n")
		const layout = "2006/01/02 15:04 UTC"
		if info.CreationDate != 0 {
			fmt.Printf("    "+gettext.Gettext("Created: %s")+"\n", time.Unix(info.CreationDate, 0).UTC().Format(layout))
		}
		fmt.Printf("    "+gettext.Gettext("Uploaded: %s")+"\n", time.Unix(info.UploadDate, 0).UTC().Format(layout))
		if info.ExpiryDate != 0 {
			fmt.Printf("    "+gettext.Gettext("Expires: %s")+"\n", time.Unix(info.ExpiryDate, 0).UTC().Format(layout))
		} else {
			fmt.Printf("    " + gettext.Gettext("Expires: never") + "\n")
		}
		fmt.Println(gettext.Gettext("Properties:"))
		for key, value := range info.Properties {
			fmt.Printf("    %s: %s\n", key, value)
		}
		fmt.Println(gettext.Gettext("Aliases:"))
		for _, alias := range info.Aliases {
			fmt.Printf("    - %s\n", alias.Name)
		}
		return nil

	case "import":
		if len(args) < 2 {
			return errArgs
		}

		var imageFile string
		var rootfsFile string
		var properties []string
		var remote string

		for _, arg := range args[1:] {
			split := strings.Split(arg, "=")
			if len(split) == 1 || shared.PathExists(arg) {
				if strings.HasSuffix(arg, ":") {
					remote = config.ParseRemote(arg)
				} else {
					if imageFile == "" {
						imageFile = args[1]
					} else {
						rootfsFile = arg
					}
				}
			} else {
				properties = append(properties, arg)
			}
		}

		if imageFile == "" {
			return errArgs
		}

		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		fingerprint, err := d.PostImage(imageFile, rootfsFile, properties, publicImage, addAliases)
		if err != nil {
			return err
		}

		fmt.Printf(gettext.Gettext("Image imported with fingerprint: %s")+"\n", fingerprint)

		return nil

	case "list":
		if len(args) > 1 {
			remote, _ = config.ParseRemoteAndContainer(args[1])
		} else {
			remote, _ = config.ParseRemoteAndContainer("")
		}

		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		images, err := d.ListImages()
		if err != nil {
			return err
		}

		return showImages(images)

	case "edit":
		if len(args) < 2 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		image := dereferenceAlias(d, inName)
		if image == "" {
			image = inName
		}

		if !terminal.IsTerminal(syscall.Stdin) {
			contents, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				return err
			}

			newdata := shared.BriefImageInfo{}
			err = yaml.Unmarshal(contents, &newdata)
			if err != nil {
				return err
			}
			return d.PutImageInfo(image, newdata)
		}

		info, err := d.GetImageInfo(image)
		if err != nil {
			return err
		}

		properties := info.BriefInfo()
		editor := os.Getenv("VISUAL")
		if editor == "" {
			editor = os.Getenv("EDITOR")
			if editor == "" {
				editor = "vi"
			}
		}
		data, err := yaml.Marshal(&properties)
		f, err := ioutil.TempFile("", "lxd_lxc_image_")
		if err != nil {
			return err
		}
		fname := f.Name()
		if err = f.Chmod(0600); err != nil {
			f.Close()
			os.Remove(fname)
			return err
		}
		f.Write([]byte(imageEditHelp + "\n"))
		f.Write(data)
		f.Close()
		defer os.Remove(fname)

		for {
			cmdParts := strings.Fields(editor)
			cmd := exec.Command(cmdParts[0], append(cmdParts[1:], fname)...)
			cmd.Stdin = os.Stdin
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			err = cmd.Run()
			if err != nil {
				return err
			}
			contents, err := ioutil.ReadFile(fname)
			if err != nil {
				return err
			}
			newdata := shared.BriefImageInfo{}
			err = yaml.Unmarshal(contents, &newdata)
			if err != nil {
				fmt.Fprintf(os.Stderr, gettext.Gettext("YAML parse error %v")+"\n", err)
				fmt.Println(gettext.Gettext("Press enter to open the editor again"))
				_, err := os.Stdin.Read(make([]byte, 1))
				if err != nil {
					return err
				}

				continue
			}
			err = d.PutImageInfo(image, newdata)
			break
		}

		return err

	case "export":
		if len(args) < 2 {
			return errArgs
		}

		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		image := dereferenceAlias(d, inName)

		target := "."
		if len(args) > 2 {
			target = args[2]
		}
		_, outfile, err := d.ExportImage(image, target)
		if err != nil {
			return err
		}

		if target != "-" {
			fmt.Printf(gettext.Gettext("Output is in %s")+"\n", outfile)
		}
		return nil

	case "show":
		if len(args) < 2 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		image := dereferenceAlias(d, inName)
		info, err := d.GetImageInfo(image)
		if err != nil {
			return err
		}

		properties := info.BriefInfo()

		data, err := yaml.Marshal(&properties)
		fmt.Printf("%s", data)
		return err

	default:
		return fmt.Errorf(gettext.Gettext("Unknown image command %s"), args[0])
	}
}
Exemple #9
0
func (c *imageCmd) run(config *lxd.Config, args []string) error {
	var remote string

	if len(args) < 1 {
		return errArgs
	}

	switch args[0] {
	case "alias":
		if len(args) < 2 {
			return errArgs
		}
		return doImageAlias(config, args)

	case "copy":
		/* copy [<remote>:]<image> [<rmeote>:]<image> */
		if len(args) != 3 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		destRemote, outName := config.ParseRemoteAndContainer(args[2])
		if outName != "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}
		dest, err := lxd.NewClient(config, destRemote)
		if err != nil {
			return err
		}
		image := dereferenceAlias(d, inName)

		progressHandler := func(progress string) {
			fmt.Printf(i18n.G("Copying the image: %s")+"\r", progress)
		}

		return d.CopyImage(image, dest, copyAliases, addAliases, publicImage, progressHandler)

	case "delete":
		/* delete [<remote>:]<image> */
		if len(args) < 2 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}
		image := dereferenceAlias(d, inName)
		err = d.DeleteImage(image)
		return err

	case "info":
		if len(args) < 2 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		image := dereferenceAlias(d, inName)
		info, err := d.GetImageInfo(image)
		if err != nil {
			return err
		}
		fmt.Printf(i18n.G("Fingerprint: %s")+"\n", info.Fingerprint)
		public := i18n.G("no")

		// FIXME: InterfaceToBool is there for backward compatibility
		if shared.InterfaceToBool(info) {
			public = i18n.G("yes")
		}

		fmt.Printf(i18n.G("Size: %.2fMB")+"\n", float64(info.Size)/1024.0/1024.0)
		arch, _ := shared.ArchitectureName(info.Architecture)
		fmt.Printf(i18n.G("Architecture: %s")+"\n", arch)
		fmt.Printf(i18n.G("Public: %s")+"\n", public)
		fmt.Printf(i18n.G("Timestamps:") + "\n")
		const layout = "2006/01/02 15:04 UTC"
		if info.CreationDate != 0 {
			fmt.Printf("    "+i18n.G("Created: %s")+"\n", time.Unix(info.CreationDate, 0).UTC().Format(layout))
		}
		fmt.Printf("    "+i18n.G("Uploaded: %s")+"\n", time.Unix(info.UploadDate, 0).UTC().Format(layout))
		if info.ExpiryDate != 0 {
			fmt.Printf("    "+i18n.G("Expires: %s")+"\n", time.Unix(info.ExpiryDate, 0).UTC().Format(layout))
		} else {
			fmt.Printf("    " + i18n.G("Expires: never") + "\n")
		}
		fmt.Println(i18n.G("Properties:"))
		for key, value := range info.Properties {
			fmt.Printf("    %s: %s\n", key, value)
		}
		fmt.Println(i18n.G("Aliases:"))
		for _, alias := range info.Aliases {
			fmt.Printf("    - %s\n", alias.Name)
		}
		return nil

	case "import":
		if len(args) < 2 {
			return errArgs
		}

		var fingerprint string
		var imageFile string
		var rootfsFile string
		var properties []string
		var remote string

		for _, arg := range args[1:] {
			split := strings.Split(arg, "=")
			if len(split) == 1 || shared.PathExists(arg) {
				if strings.HasSuffix(arg, ":") {
					remote = config.ParseRemote(arg)
				} else {
					if imageFile == "" {
						imageFile = args[1]
					} else {
						rootfsFile = arg
					}
				}
			} else {
				properties = append(properties, arg)
			}
		}

		if remote == "" {
			remote = config.DefaultRemote
		}

		if imageFile == "" {
			return errArgs
		}

		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		if strings.HasPrefix(imageFile, "https://") {
			fingerprint, err = d.PostImageURL(imageFile, publicImage, addAliases)
		} else if strings.HasPrefix(imageFile, "http://") {
			return fmt.Errorf(i18n.G("Only https:// is supported for remote image import."))
		} else {
			fingerprint, err = d.PostImage(imageFile, rootfsFile, properties, publicImage, addAliases)
		}

		if err != nil {
			return err
		}
		fmt.Printf(i18n.G("Image imported with fingerprint: %s")+"\n", fingerprint)

		return nil

	case "list":
		filters := []string{}

		if len(args) > 1 {
			result := strings.SplitN(args[1], ":", 2)
			if len(result) == 1 {
				filters = append(filters, args[1])
				remote, _ = config.ParseRemoteAndContainer("")
			} else {
				remote, _ = config.ParseRemoteAndContainer(args[1])
			}
		} else {
			remote, _ = config.ParseRemoteAndContainer("")
		}

		if len(args) > 2 {
			for _, filter := range args[2:] {
				filters = append(filters, filter)
			}
		}

		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		images, err := d.ListImages()
		if err != nil {
			return err
		}

		return showImages(images, filters)

	case "edit":
		if len(args) < 2 {
			return errArgs
		}

		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}

		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		image := dereferenceAlias(d, inName)
		if image == "" {
			image = inName
		}

		return doImageEdit(d, image)

	case "export":
		if len(args) < 2 {
			return errArgs
		}

		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}

		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		image := dereferenceAlias(d, inName)

		target := "."
		if len(args) > 2 {
			target = args[2]
		}
		_, outfile, err := d.ExportImage(image, target)
		if err != nil {
			return err
		}

		if target != "-" {
			fmt.Printf(i18n.G("Output is in %s")+"\n", outfile)
		}
		return nil

	case "show":
		if len(args) < 2 {
			return errArgs
		}
		remote, inName := config.ParseRemoteAndContainer(args[1])
		if inName == "" {
			return errArgs
		}
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		image := dereferenceAlias(d, inName)
		info, err := d.GetImageInfo(image)
		if err != nil {
			return err
		}

		properties := info.BriefInfo()

		data, err := yaml.Marshal(&properties)
		fmt.Printf("%s", data)
		return err

	default:
		return errArgs
	}
}
Exemple #10
0
func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliases []string, public bool) error {
	fingerprint := c.GetAlias(image)
	if fingerprint == "" {
		fingerprint = image
	}

	info, err := c.GetImageInfo(fingerprint)
	if err != nil {
		return err
	}

	source := shared.Jmap{
		"type":        "image",
		"mode":        "pull",
		"server":      c.BaseURL,
		"fingerprint": fingerprint}

	// FIXME: InterfaceToBool is there for backward compatibility
	if !shared.InterfaceToBool(info.Public) {
		var secret string

		resp, err := c.post("images/"+fingerprint+"/secret", nil, Async)
		if err != nil {
			return err
		}

		op, err := resp.MetadataAsOperation()
		if err == nil && op.Metadata != nil {
			secret, err = op.Metadata.GetString("secret")
			if err != nil {
				return err
			}
		} else {
			// FIXME: This is a backward compatibility codepath
			md := secretMd{}
			if err := json.Unmarshal(resp.Metadata, &md); err != nil {
				return err
			}

			secret = md.Secret
		}

		source["secret"] = secret
	}

	addresses, err := c.Addresses()
	if err != nil {
		return err
	}

	for _, addr := range addresses {
		sourceUrl := "https://" + addr

		source["server"] = sourceUrl
		body := shared.Jmap{"public": public, "source": source}

		resp, err := dest.post("images", body, Async)
		if err != nil {
			continue
		}

		err = dest.WaitForSuccess(resp.Operation)
		if err != nil {
			return err
		}

		break
	}

	if err != nil {
		return err
	}

	/* copy aliases from source image */
	if copy_aliases {
		for _, alias := range info.Aliases {
			dest.DeleteAlias(alias.Name)
			err = dest.PostAlias(alias.Name, alias.Description, info.Fingerprint)
			if err != nil {
				fmt.Printf(gettext.Gettext("Error adding alias %s")+"\n", alias.Name)
			}
		}
	}

	/* add new aliases */
	for _, alias := range aliases {
		dest.DeleteAlias(alias)
		err = dest.PostAlias(alias, alias, info.Fingerprint)
		if err != nil {
			fmt.Printf(gettext.Gettext("Error adding alias %s")+"\n", alias)
		}
	}

	return nil
}