forked from apptainer/singularity
/
bundle.go
140 lines (124 loc) · 3.81 KB
/
bundle.go
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
// Copyright (c) 2019, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
package sifbundle
import (
"fmt"
"os"
"path/filepath"
"runtime"
"syscall"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/sylabs/sif/pkg/sif"
"github.com/sylabs/singularity/pkg/ocibundle"
"github.com/sylabs/singularity/pkg/ocibundle/tools"
)
type sifBundle struct {
image string
bundlePath string
writable bool
ocibundle.Bundle
}
// Create creates an OCI bundle from a SIF image
func (s *sifBundle) Create(ociConfig *specs.Spec) error {
if s.image == "" {
return fmt.Errorf("image wasn't set, need one to create bundle")
}
flag := os.O_RDONLY
if s.writable {
flag = os.O_RDWR
}
file, err := os.OpenFile(s.image, flag, 0)
if err != nil {
return fmt.Errorf("can't open image %s: %s", s.image, err)
}
defer file.Close()
fimg, err := sif.LoadContainerFp(file, !s.writable)
if err != nil {
return fmt.Errorf("could not load image fp: %v", err)
}
part, _, err := fimg.GetPartPrimSys()
if err != nil {
return fmt.Errorf("could not get primaty partitions: %v", err)
}
fstype, err := part.GetFsType()
if err != nil {
return fmt.Errorf("could not get fs type: %v", err)
}
if fstype != sif.FsSquash {
return fmt.Errorf("unsuported image fs type: %v", fstype)
}
offset := uint64(part.Fileoff)
size := uint64(part.Filelen)
// TODO: embed OCI image config in SIF directly and generate
// runtime configuration from this one for environment variables
// and command/entrypoint
config := ociConfig
if config == nil {
g, err := generate.New(runtime.GOOS)
if err != nil {
return fmt.Errorf("failed to generate OCI config: %s", err)
}
g.SetProcessArgs([]string{"/.singularity.d/actions/run"})
config = g.Config
}
// create OCI bundle
if err := tools.CreateBundle(s.bundlePath, config); err != nil {
return fmt.Errorf("failed to create OCI bundle: %s", err)
}
// associate SIF image with a block
loop, err := tools.CreateLoop(file, offset, size)
if err != nil {
tools.DeleteBundle(s.bundlePath)
return fmt.Errorf("failed to find loop device: %s", err)
}
rootFs := tools.RootFs(s.bundlePath).Path()
if err := syscall.Mount(loop, rootFs, "squashfs", syscall.MS_RDONLY, "errors=remount-ro"); err != nil {
tools.DeleteBundle(s.bundlePath)
return fmt.Errorf("failed to mount SIF partition: %s", err)
}
if s.writable {
if err := tools.CreateOverlay(s.bundlePath); err != nil {
// best effort to release loop device
syscall.Unmount(rootFs, syscall.MNT_DETACH)
tools.DeleteBundle(s.bundlePath)
return fmt.Errorf("failed to create overlay: %s", err)
}
}
return nil
}
// Delete erases OCI bundle create from SIF image
func (s *sifBundle) Delete() error {
if s.writable {
if err := tools.DeleteOverlay(s.bundlePath); err != nil {
return fmt.Errorf("delete error: %s", err)
}
}
// Umount rootfs
rootFsDir := tools.RootFs(s.bundlePath).Path()
if err := syscall.Unmount(rootFsDir, syscall.MNT_DETACH); err != nil {
return fmt.Errorf("failed to unmount %s: %s", rootFsDir, err)
}
// delete bundle directory
return tools.DeleteBundle(s.bundlePath)
}
// FromSif returns a bundle interface to create/delete OCI bundle from SIF image
func FromSif(image, bundle string, writable bool) (ocibundle.Bundle, error) {
var err error
s := &sifBundle{
writable: writable,
}
s.bundlePath, err = filepath.Abs(bundle)
if err != nil {
return nil, fmt.Errorf("failed to determine bundle path: %s", err)
}
if image != "" {
s.image, err = filepath.Abs(image)
if err != nil {
return nil, fmt.Errorf("failed to determine image path: %s", err)
}
}
return s, nil
}