You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

177 lines
4.8 KiB

package image
import (
"fmt"
"os"
"path"
"git.wegmueller.it/opencloud/opencloud/config"
"git.wegmueller.it/opencloud/opencloud/image/oci"
"git.wegmueller.it/opencloud/opencloud/volume"
"git.wegmueller.it/opencloud/opencloud/zfs"
"github.com/sirupsen/logrus"
"github.com/ztrue/tracerr"
)
func (i *Image) UnpackLayers(rd *oci.Reader, keepLayerFile bool) (Gerr error) {
layerDatasets := make([]*zfs.Dataset, 0)
defer func() {
//TODO allow failures to stick around in some cases
if Gerr != nil {
// If we have an error reverse the whole unpack operation backwards so we don't fail because clones exist
for i := len(layerDatasets) - 1; i >= 0; i-- {
l := layerDatasets[i]
_ = l.Destroy(true)
}
}
}()
for layerId, layer := range i.Layers {
if layer.Dataset != nil {
continue
}
// safety check against runtime problems
// i.e if somebody forgot to load the dataset
if HasLayer(layer.Descriptor.Digest) {
logrus.Warnf("dataset for layer %s was not loaded properly", layer.Descriptor.Digest)
continue
}
if layerId == 0 {
var err error
layer.Dataset, err = imagesDataset.CreateChildDataset(layer.Descriptor.Digest.Encoded(), zfs.Properties{
zfs.PropertyCompression: zfs.CompressionLZ4,
})
layerDatasets = append(layerDatasets, layer.Dataset)
if err != nil {
return tracerr.Wrap(err)
}
for _, vol := range i.Volumes {
vol, err := volume.CreateVolume(vol, layer.Dataset)
if err != nil {
return fmt.Errorf("can not create initial layer for image %s, volume creation failed: %w", i.Name, err)
}
if err := vol.AssignToImage(layer.Descriptor.Digest); err != nil {
return fmt.Errorf("could not assign volume %s to image %s: %w", vol.GetDatasetPath(), i.Name, err)
}
layer.volumeList.Add(vol)
}
i.Layers[layerId] = layer
} else if parentLayer := i.Layers[layerId-1]; parentLayer.Dataset != nil {
snap, err := parentLayer.Dataset.GetSnapshot(config.SealSnapshotName)
if err != nil {
return tracerr.Wrap(err)
}
layer.Dataset, err = snap.Clone(path.Join(imagesDataset.Path, layer.Descriptor.Digest.Encoded()), nil)
if err != nil {
return tracerr.Wrap(err)
}
layerDatasets = append(layerDatasets, layer.Dataset)
// First we go through all already added volumes and clone them into the new layer dataset new
for _, vol := range parentLayer.volumeList {
volClone, err := vol.Clone(layer.Dataset)
if err != nil {
return fmt.Errorf("could not clone volume %s: %w", vol.GetDatasetPath(), err)
}
if err := volClone.AssignToImage(layer.Descriptor.Digest); err != nil {
return fmt.Errorf("could not assign volume %s to image %s: %w", volClone.GetName(), i.Name, err)
}
layer.volumeList.Add(vol)
}
// Check if we need to create new volumes additionally to the ones created
if len(layer.volumeList) < len(i.Volumes) {
for _, vol := range i.Volumes[len(layer.volumeList):] {
volumeObj, err := volume.CreateVolume(vol, layer.Dataset)
if err != nil {
return fmt.Errorf("can not create volume %s for layer %s: %w", vol.Name, layer.Descriptor.Digest, err)
}
if err := volumeObj.AssignToImage(layer.Descriptor.Digest); err != nil {
return fmt.Errorf("could not assign volume %s to image %s: %w", volumeObj.GetName(), i.Name, err)
}
layer.volumeList.Add(volumeObj)
}
}
i.Layers[layerId] = layer
} else {
var err error
layer.Dataset, err = imagesDataset.CreateChildDataset(layer.Descriptor.Digest.Encoded(), zfs.Properties{
zfs.PropertyCompression: zfs.CompressionLZ4,
})
layerDatasets = append(layerDatasets, layer.Dataset)
if err != nil {
return tracerr.Wrap(err)
}
for _, vol := range i.Volumes {
vol, err := volume.CreateVolume(vol, layer.Dataset)
if err != nil {
return fmt.Errorf("can not create initial layer for image %s, volume creation failed: %w", i.Name, err)
}
if err := vol.AssignToImage(layer.Descriptor.Digest); err != nil {
return fmt.Errorf("could not assign volume %s to image %s: %w", vol.GetDatasetPath(), i.Name, err)
}
layer.volumeList.Add(vol)
}
i.Layers[layerId] = layer
}
layerReader, err := rd.GetLayerReader(layer.Descriptor)
if err != nil {
return tracerr.Wrap(err)
}
err = layerReader.ExtractTreeInto(layer.Dataset.VFSPath())
if err != nil {
return tracerr.Wrap(err)
}
_, err = layer.Dataset.Snapshot(config.SealSnapshotName)
if err != nil {
return tracerr.Wrap(err)
}
for _, vol := range layer.volumeList {
if err := vol.Seal(); err != nil {
return fmt.Errorf("could not seal volume %s: %w", vol.GetName(), err)
}
}
err = layerReader.Close()
if err != nil {
return tracerr.Wrap(err)
}
if keepLayerFile {
continue
}
if err := os.Remove(layerReader.GetFilePath()); err != nil {
return tracerr.Wrap(err)
}
}
return nil
}