1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
package main
import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"os"
"github.com/spf13/cobra"
cli "github.com/lxc/incus/v6/internal/cmd"
"github.com/lxc/incus/v6/shared/simplestreams"
)
type cmdRemove struct {
global *cmdGlobal
flagVerbose bool
}
// Command generates the command definition.
func (c *cmdRemove) Command() *cobra.Command {
cmd := &cobra.Command{}
cmd.Use = "remove <fingerprint>"
cmd.Short = "Remove an image"
cmd.Long = cli.FormatSection("Description",
`Remove an image from the server
This command locates the image from its fingerprint and removes it from the index.
`)
cmd.RunE = c.Run
cmd.Flags().BoolVarP(&c.flagVerbose, "verbose", "v", false, "Show all information messages")
return cmd
}
func (c *cmdRemove) remove(path string) error {
if c.flagVerbose {
fmt.Printf("deleting: %s\n", path)
}
err := os.Remove(path)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return err
}
return nil
}
// Run runs the actual command logic.
func (c *cmdRemove) Run(cmd *cobra.Command, args []string) error {
// Quick checks.
exit, err := c.global.CheckArgs(cmd, args, 1, 1)
if exit {
return err
}
// Get a simplestreams client.
ss := simplestreams.NewLocalClient("")
// Get the image.
image, err := ss.GetImage(args[0])
if err != nil {
return err
}
// Load the images file.
body, err := os.ReadFile("streams/v1/images.json")
if err != nil {
return err
}
products := simplestreams.Products{}
err = json.Unmarshal(body, &products)
if err != nil {
return err
}
// Delete the image entry.
for kProduct, product := range products.Products {
if product.OperatingSystem != image.Properties["os"] || product.Release != image.Properties["release"] || product.Variant != image.Properties["variant"] || product.Architecture != image.Properties["architecture"] {
continue
}
for kVersion, version := range product.Versions {
// Get the metadata entry.
metaEntry, ok := version.Items["incus.tar.xz"]
if ok {
if metaEntry.CombinedSha256DiskKvmImg == image.Fingerprint {
// Deleting a VM image.
err = c.remove(version.Items["disk-kvm.img"].Path)
if err != nil {
return err
}
delete(version.Items, "disk-kvm.img")
metaEntry.CombinedSha256DiskKvmImg = ""
} else if metaEntry.CombinedSha256SquashFs == image.Fingerprint {
// Deleting a container image.
err = c.remove(version.Items["squashfs"].Path)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return err
}
delete(version.Items, "squashfs")
metaEntry.CombinedSha256SquashFs = ""
} else {
continue
}
// Update the metadata entry.
version.Items["incus.tar.xz"] = metaEntry
// Delete the version if it's now empty.
if len(version.Items) == 1 {
err = c.remove(metaEntry.Path)
if err != nil {
return err
}
delete(product.Versions, kVersion)
}
}
metaEntry, ok = version.Items["incus_combined.tar.gz"]
if ok {
if metaEntry.HashSha256 == image.Fingerprint {
err = c.remove(metaEntry.Path)
if err != nil {
return err
}
delete(version.Items, "incus_combined.tar.gz")
}
// Delete the version if it's now empty.
if len(version.Items) == 0 {
delete(product.Versions, kVersion)
}
}
}
if len(product.Versions) == 0 {
delete(products.Products, kProduct)
}
break
}
// Write back the images file.
body, err = json.Marshal(&products)
if err != nil {
return err
}
err = os.WriteFile("streams/v1/images.json", body, 0o644)
if err != nil {
return err
}
// Re-generate the index.
err = writeIndex(&products)
if err != nil {
return err
}
return nil
}
|