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 }
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 }
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 }
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 }
// 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 }
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 }
// 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 }
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]) } }
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 } }
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 }