forked from OpenCloud/opencloud
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
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 |
|
}
|
|
|