Browse Source

Added Image clone and destroy for cloned images

master
Till Wegmüller 4 years ago
parent
commit
215c210721
  1. 28
      cmd/imageadm/create.go
  2. 8
      cmd/imageadm/destroy.go
  3. 59
      host/image.go
  4. 4
      host/interface.go
  5. 19
      image/clone.go
  6. 46
      image/destroy.go
  7. 2
      image/image.go
  8. 56
      image/list.go
  9. 8
      image/meta.go
  10. 5
      namegenerator/docker.go

28
cmd/imageadm/create.go

@ -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)
}

8
cmd/imageadm/destroy.go

@ -13,13 +13,7 @@ var destroyCmd = &cobra.Command{
`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ref := reference.NewReferenceString(args[0])
img, err := h.GetLocalImage(ref)
if err != nil {
return tracerr.Wrap(err)
}
err = img.Destroy()
if err != nil {
if err := h.DestroyImage(reference.NewReferenceString(args[0])); err != nil {
return tracerr.Wrap(err)
}
return nil

59
host/image.go

@ -1,11 +1,6 @@
package host
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"git.wegmueller.it/opencloud/opencloud/image"
"git.wegmueller.it/opencloud/opencloud/image/reference"
"github.com/ztrue/tracerr"
@ -52,33 +47,35 @@ func (h *Host) FetchImage(ref reference.Reference) (*image.Image, error) {
return img, nil
}
func (h *Host) Images() []*image.Image {
manifests, _ := filepath.Glob(h.Path("images/*.json"))
retValue := make([]*image.Image, 0, len(manifests))
for _, m := range manifests {
if strings.Contains(m, "index.json") {
continue
}
var img image.Image
if err := func(fileName string, img *image.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)
func (h *Host) CloneImage(nameRef, baseRef reference.Reference) (*image.Image, error) {
baseImage, err := h.GetImage(baseRef)
if err != nil {
return nil, tracerr.Wrap(err)
}
img, err := baseImage.Clone(nameRef)
if err != nil {
return nil, tracerr.Wrap(err)
}
return retValue
return img, nil
}
func (h *Host) DestroyImage(ref reference.Reference) error {
img, err := h.GetLocalImage(ref)
if err != nil {
return tracerr.Wrap(err)
}
images := h.Images()
layerSet := image.GetAllLayers(images)
err = img.Destroy(layerSet)
if err != nil {
return tracerr.Wrap(err)
}
return nil
}
func (h *Host) Images() []*image.Image {
return image.GetAllImages(h.Dataset)
}
func (h *Host) ImportImage(ref reference.Reference) (*image.Image, error) {

4
host/interface.go

@ -4,7 +4,6 @@ import (
"net"
"git.wegmueller.it/opencloud/opencloud/image"
"git.wegmueller.it/opencloud/opencloud/image/build"
"git.wegmueller.it/opencloud/opencloud/image/reference"
"git.wegmueller.it/opencloud/opencloud/keystore"
opcNet "git.wegmueller.it/opencloud/opencloud/net"
@ -25,7 +24,8 @@ type ImageHost interface {
Images() []*image.Image
ImportImage(ref reference.Reference) (*image.Image, error)
HasImage(ref reference.Reference) bool
RunImageBuild(cfg *build.ImageConfig) error
CloneImage(nameRef, baseRef reference.Reference) (*image.Image, error)
DestroyImage(ref reference.Reference) error
}
type ContainerHost interface {

19
image/clone.go

@ -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
}

46
image/destroy.go

@ -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
}

2
image/image.go

@ -54,7 +54,7 @@ func NewImage(ref reference.Reference, imagesDataset *zfs.Dataset) (*Image, erro
imagesDS: imagesDataset,
}
if imagesDataset == nil {
if i.imagesDS == nil {
var err error
i.imagesDS, err = getImagesDataset()
if err != nil {

56
image/list.go

@ -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
}

8
image/meta.go

@ -41,6 +41,14 @@ func (img *Image) Load() error {
return nil
}
func (img *Image) removeConfig() error {
err := os.Remove(img.VFSPath() + ".json")
if err != nil {
return tracerr.Wrap(err)
}
return nil
}
func (img *Image) Save() error {
metaFile, err := os.Create(img.VFSPath() + ".json")
if err != nil {

5
namegenerator/docker.go

@ -5,8 +5,13 @@ package namesgenerator
import (
"fmt"
"math/rand"
"time"
)
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
var (
left = [...]string{
"admiring",

Loading…
Cancel
Save