10 changed files with 185 additions and 50 deletions
@ -0,0 +1,28 @@
|
||||
package imageadm |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"git.wegmueller.it/opencloud/opencloud/image/reference" |
||||
"github.com/spf13/cobra" |
||||
"github.com/ztrue/tracerr" |
||||
) |
||||
|
||||
var createCmd = &cobra.Command{ |
||||
Use: "create name base", |
||||
Short: "create a new image without creating a container or running a build in the process", |
||||
Args: cobra.MinimumNArgs(2), |
||||
RunE: func(cmd *cobra.Command, args []string) error { |
||||
baseRef := reference.NewReferenceString(args[1]) |
||||
img, err := h.CloneImage(reference.NewReferenceString(args[0]), baseRef) |
||||
if err != nil { |
||||
return tracerr.Wrap(err) |
||||
} |
||||
fmt.Printf("cloned image %s into %s\n", baseRef, img.Reference) |
||||
return nil |
||||
}, |
||||
} |
||||
|
||||
func init() { |
||||
RootCmd.AddCommand(createCmd) |
||||
} |
@ -0,0 +1,19 @@
|
||||
package image |
||||
|
||||
import ( |
||||
"git.wegmueller.it/opencloud/opencloud/image/reference" |
||||
"github.com/ztrue/tracerr" |
||||
) |
||||
|
||||
func (img *Image) Clone(cloneRef reference.Reference) (*Image, error) { |
||||
cloneImg, err := CreateEmptyImage(cloneRef, nil) |
||||
if err != nil { |
||||
return nil, tracerr.Wrap(err) |
||||
} |
||||
cloneImg.Tags = img.Tags |
||||
err = cloneImg.Save() |
||||
if err != nil { |
||||
return nil, tracerr.Wrap(err) |
||||
} |
||||
return cloneImg, nil |
||||
} |
@ -1,22 +1,50 @@
|
||||
package image |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/opencontainers/go-digest" |
||||
"github.com/ztrue/tracerr" |
||||
) |
||||
|
||||
// This Function destroys the image on disk and makes the Struct unusable
|
||||
// We are relying on zfs to error when an image can not be destroyed due to still dependant images
|
||||
func (img *Image) Destroy() error { |
||||
layerSet := make(map[string]bool) |
||||
func (img *Image) Destroy(layerSet map[digest.Digest][]string) error { |
||||
for _, m := range img.Tags { |
||||
for i := len(m.Layers) - 1; i > 0; i-- { |
||||
lName := m.Layers[i] |
||||
if _, ok := layerSet[lName.Digest.String()]; ok { |
||||
continue |
||||
linkedImages, ok := layerSet[lName.Digest] |
||||
// If the layer is used in more than one place abort the deletion
|
||||
// and come back for the next tag.
|
||||
if ok { |
||||
if len(linkedImages) > 1 { |
||||
// Knock out the instance here so the next loop that hits layer
|
||||
// sees that he is the only one left
|
||||
preLen := len(linkedImages) |
||||
for i, refStr := range linkedImages { |
||||
if refStr == img.Reference.String() { |
||||
linkedImages = append(linkedImages[:i], linkedImages[i+1:]...) |
||||
layerSet[lName.Digest] = linkedImages |
||||
} |
||||
} |
||||
if len(linkedImages) == preLen { |
||||
return fmt.Errorf("unable to remove reference to layer %s from images link. linked images are %#v", lName.Digest, linkedImages) |
||||
} |
||||
break |
||||
} |
||||
} |
||||
ds := img.GetLayerDS(lName.Digest) |
||||
//TODO Only have a layerlist that is not linked with any other image anymore
|
||||
//That would mean either keep an index off all layers and in which images they are or load in all images when destroying
|
||||
//The first is probably preferable
|
||||
_ = ds.Destroy(true) |
||||
layerSet[lName.Digest.String()] = true |
||||
err := ds.Destroy(true) |
||||
if err != nil { |
||||
return tracerr.Wrap(err) |
||||
} |
||||
} |
||||
if err := img.Save(); err != nil { |
||||
return tracerr.Wrap(err) |
||||
} |
||||
} |
||||
if err := img.removeConfig(); err != nil { |
||||
return tracerr.Wrap(err) |
||||
} |
||||
return nil |
||||
} |
||||
|
@ -0,0 +1,56 @@
|
||||
package image |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
|
||||
"git.wegmueller.it/opencloud/opencloud/zfs" |
||||
"github.com/opencontainers/go-digest" |
||||
) |
||||
|
||||
func GetAllLayers(images []*Image) map[digest.Digest][]string { |
||||
layers := make(map[digest.Digest][]string) |
||||
for _, img := range images { |
||||
for _, tag := range img.Tags { |
||||
for _, layer := range tag.Layers { |
||||
l, ok := layers[layer.Digest] |
||||
if !ok { |
||||
l = make([]string, 0) |
||||
} |
||||
l = append(l, img.Reference.String()) |
||||
} |
||||
} |
||||
} |
||||
return layers |
||||
} |
||||
|
||||
func GetAllImages(hostDataset *zfs.Dataset) []*Image { |
||||
manifests, _ := filepath.Glob(hostDataset.VFSPath("images/*.json")) |
||||
retValue := make([]*Image, 0, len(manifests)) |
||||
for _, m := range manifests { |
||||
if strings.Contains(m, "index.json") { |
||||
continue |
||||
} |
||||
var img Image |
||||
if err := func(fileName string, img *Image) error { |
||||
f, err := os.Open(fileName) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer f.Close() |
||||
if err = json.NewDecoder(f).Decode(img); err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
}(m, &img); err != nil { |
||||
continue |
||||
} |
||||
if err := img.Load(); err != nil { |
||||
continue |
||||
} |
||||
retValue = append(retValue, &img) |
||||
} |
||||
return retValue |
||||
} |
Loading…
Reference in new issue