Skip to content

Commit 7f16d67

Browse files
committed
Lock the cache dir for doing operations
This first implementation is far from ideal. To make it better requires refactoring the code, since the cache logic is all over the place. In any case, I think this is better than nothing. I do not use 'github.com/containers/storage/pkg/lockfile' because it does not allow to acquire an exclusive lock and after releasing it, to acquire a shared one. After refactoring I'll try it again. Signed-off-by: German Maglione <[email protected]>
1 parent afda503 commit 7f16d67

File tree

13 files changed

+196
-6
lines changed

13 files changed

+196
-6
lines changed

cmd/list.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package cmd
22

33
import (
4-
"github.com/sirupsen/logrus"
54
"os"
65

76
"gitlab.com/bootc-org/podman-bootc/pkg/config"
87
"gitlab.com/bootc-org/podman-bootc/pkg/user"
8+
"gitlab.com/bootc-org/podman-bootc/pkg/utils"
99
"gitlab.com/bootc-org/podman-bootc/pkg/vm"
1010

1111
"github.com/containers/common/pkg/report"
12+
"github.com/sirupsen/logrus"
1213
"github.com/spf13/cobra"
1314
)
1415

@@ -83,13 +84,20 @@ func getVMInfo(user user.User, libvirtUri string, imageId string) (*vm.BootcVMCo
8384
ImageID: imageId,
8485
User: user,
8586
LibvirtUri: libvirtUri,
87+
Locking: utils.Shared,
8688
})
8789

8890
if err != nil {
8991
return nil, err
9092
}
9193

92-
defer bootcVM.CloseConnection()
94+
// Let's be explicit instead of relying on the defer exec order
95+
defer func() {
96+
bootcVM.CloseConnection()
97+
if err := bootcVM.Unlock(); err != nil {
98+
logrus.Warningf("unable to unlock VM %s: %v", imageId, err)
99+
}
100+
}()
93101

94102
cfg, err := bootcVM.GetConfig()
95103
if err != nil {

cmd/rm.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"gitlab.com/bootc-org/podman-bootc/pkg/config"
88
"gitlab.com/bootc-org/podman-bootc/pkg/user"
9+
"gitlab.com/bootc-org/podman-bootc/pkg/utils"
910
"gitlab.com/bootc-org/podman-bootc/pkg/vm"
1011

1112
"github.com/sirupsen/logrus"
@@ -60,12 +61,19 @@ func prune(id string) error {
6061
ImageID: id,
6162
LibvirtUri: config.LibvirtUri,
6263
User: user,
64+
Locking: utils.Exclusive,
6365
})
6466
if err != nil {
6567
return fmt.Errorf("unable to get VM %s: %v", id, err)
6668
}
6769

68-
defer bootcVM.CloseConnection()
70+
// Let's be explicit instead of relying on the defer exec order
71+
defer func() {
72+
bootcVM.CloseConnection()
73+
if err := bootcVM.Unlock(); err != nil {
74+
logrus.Warningf("unable to unlock VM %s: %v", id, err)
75+
}
76+
}()
6977

7078
if force {
7179
err := forceKillVM(bootcVM)

cmd/run.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,20 @@ func doRun(flags *cobra.Command, args []string) error {
116116
ImageID: bootcDisk.GetImageId(),
117117
User: user,
118118
LibvirtUri: config.LibvirtUri,
119+
Locking: utils.Shared,
119120
})
120121

121122
if err != nil {
122123
return fmt.Errorf("unable to initialize VM: %w", err)
123124
}
124125

125-
defer bootcVM.CloseConnection()
126+
// Let's be explicit instead of relying on the defer exec order
127+
defer func() {
128+
bootcVM.CloseConnection()
129+
if err := bootcVM.Unlock(); err != nil {
130+
logrus.Warningf("unable to unlock VM %s: %v", bootcDisk.GetImageId(), err)
131+
}
132+
}()
126133

127134
cmd := args[1:]
128135
err = bootcVM.Run(vm.RunVMParameters{

cmd/ssh.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"gitlab.com/bootc-org/podman-bootc/pkg/utils"
77
"gitlab.com/bootc-org/podman-bootc/pkg/vm"
88

9+
"github.com/sirupsen/logrus"
910
"github.com/spf13/cobra"
1011
)
1112

@@ -35,13 +36,20 @@ func doSsh(_ *cobra.Command, args []string) error {
3536
ImageID: id,
3637
User: user,
3738
LibvirtUri: config.LibvirtUri,
39+
Locking: utils.Shared,
3840
})
3941

4042
if err != nil {
4143
return err
4244
}
4345

44-
defer vm.CloseConnection()
46+
// Let's be explicit instead of relying on the defer exec order
47+
defer func() {
48+
vm.CloseConnection()
49+
if err := vm.Unlock(); err != nil {
50+
logrus.Warningf("unable to unlock VM %s: %v", id, err)
51+
}
52+
}()
4553

4654
err = vm.SetUser(sshUser)
4755
if err != nil {

cmd/stop.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package cmd
33
import (
44
"gitlab.com/bootc-org/podman-bootc/pkg/config"
55
"gitlab.com/bootc-org/podman-bootc/pkg/user"
6+
"gitlab.com/bootc-org/podman-bootc/pkg/utils"
67
"gitlab.com/bootc-org/podman-bootc/pkg/vm"
78

9+
"github.com/sirupsen/logrus"
810
"github.com/spf13/cobra"
911
)
1012

@@ -31,10 +33,19 @@ func doStop(_ *cobra.Command, args []string) (err error) {
3133
ImageID: id,
3234
LibvirtUri: config.LibvirtUri,
3335
User: user,
36+
Locking: utils.Exclusive,
3437
})
3538
if err != nil {
3639
return err
3740
}
38-
defer bootcVM.CloseConnection()
41+
42+
// Let's be explicit instead of relying on the defer exec order
43+
defer func() {
44+
bootcVM.CloseConnection()
45+
if err := bootcVM.Unlock(); err != nil {
46+
logrus.Warningf("unable to unlock VM %s: %v", id, err)
47+
}
48+
}()
49+
3950
return bootcVM.Delete()
4051
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/containers/common v0.58.1
88
github.com/containers/podman/v5 v5.0.1
99
github.com/docker/go-units v0.5.0
10+
github.com/gofrs/flock v0.8.1
1011
github.com/onsi/ginkgo/v2 v2.17.1
1112
github.com/onsi/gomega v1.32.0
1213
github.com/opencontainers/runtime-spec v1.2.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
229229
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
230230
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
231231
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
232+
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
233+
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
232234
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
233235
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
234236
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=

pkg/bootc/bootc_disk.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"gitlab.com/bootc-org/podman-bootc/pkg/config"
1515
"gitlab.com/bootc-org/podman-bootc/pkg/user"
16+
"gitlab.com/bootc-org/podman-bootc/pkg/utils"
1617

1718
"github.com/containers/podman/v5/pkg/bindings/containers"
1819
"github.com/containers/podman/v5/pkg/bindings/images"
@@ -105,6 +106,21 @@ func (p *BootcDisk) Install(quiet bool) (err error) {
105106

106107
// Create VM cache dir; one per oci bootc image
107108
p.Directory = filepath.Join(p.User.CacheDir(), p.ImageId)
109+
lock := utils.NewCacheLock(p.User.RunDir(), p.Directory)
110+
locked, err := lock.TryLock(utils.Exclusive)
111+
if err != nil {
112+
return fmt.Errorf("error locking the VM cache path: %w", err)
113+
}
114+
if !locked {
115+
return fmt.Errorf("unable to lock the VM cache path")
116+
}
117+
118+
defer func() {
119+
if err := lock.Unlock(); err != nil {
120+
logrus.Errorf("unable to unlock VM %s: %v", p.ImageId, err)
121+
}
122+
}()
123+
108124
if err := os.MkdirAll(p.Directory, os.ModePerm); err != nil {
109125
return fmt.Errorf("error while making bootc disk directory: %w", err)
110126
}

pkg/utils/locks.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package utils
2+
3+
import (
4+
"path/filepath"
5+
6+
"github.com/gofrs/flock"
7+
)
8+
9+
type AccessMode uint
10+
11+
const (
12+
Exclusive AccessMode = iota
13+
Shared
14+
)
15+
16+
type CacheLock struct {
17+
inner *flock.Flock
18+
}
19+
20+
// NewCacheLock returns a new instance of *CacheLock. It takes the path to the VM cache dir.
21+
func NewCacheLock(lockDir, cacheDir string) CacheLock {
22+
imageLongID := filepath.Base(cacheDir)
23+
cacheDirLockFile := filepath.Join(lockDir, imageLongID+".lock")
24+
return CacheLock{inner: flock.New(cacheDirLockFile)}
25+
}
26+
27+
// TryLock takes an exclusive or shared lock, based on the parameter mode.
28+
// The lock is non-blocking, if we are unable to lock the cache directory,
29+
// the function will return false instead of waiting for the lock.
30+
func (l CacheLock) TryLock(mode AccessMode) (bool, error) {
31+
if mode == Exclusive {
32+
return l.inner.TryLock()
33+
} else {
34+
return l.inner.TryRLock()
35+
}
36+
}
37+
38+
// Unlock unlocks the cache lock.
39+
func (l CacheLock) Unlock() error {
40+
return l.inner.Unlock()
41+
}

pkg/vm/vm.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package vm
33
import (
44
"encoding/base64"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"os"
89
"os/exec"
@@ -14,12 +15,15 @@ import (
1415
"gitlab.com/bootc-org/podman-bootc/pkg/bootc"
1516
"gitlab.com/bootc-org/podman-bootc/pkg/config"
1617
"gitlab.com/bootc-org/podman-bootc/pkg/user"
18+
"gitlab.com/bootc-org/podman-bootc/pkg/utils"
1719

1820
"github.com/docker/go-units"
1921
"github.com/sirupsen/logrus"
2022
"golang.org/x/crypto/ssh"
2123
)
2224

25+
var ErrVMInUse = errors.New("VM already in use")
26+
2327
// GetVMCachePath returns the path to the VM cache directory
2428
func GetVMCachePath(imageId string, user user.User) (path string, err error) {
2529
files, err := os.ReadDir(user.CacheDir())
@@ -45,6 +49,7 @@ type NewVMParameters struct {
4549
ImageID string
4650
User user.User //user who is running the podman bootc command
4751
LibvirtUri string //linux only
52+
Locking utils.AccessMode
4853
}
4954

5055
type RunVMParameters struct {
@@ -71,6 +76,7 @@ type BootcVM interface {
7176
GetConfig() (*BootcVMConfig, error)
7277
CloseConnection()
7378
PrintConsole() error
79+
Unlock() error
7480
}
7581

7682
type BootcVMCommon struct {
@@ -92,6 +98,7 @@ type BootcVMCommon struct {
9298
cloudInitDir string
9399
cloudInitArgs string
94100
bootcDisk bootc.BootcDisk
101+
cacheDirLock utils.CacheLock
95102
}
96103

97104
type BootcVMConfig struct {
@@ -274,3 +281,31 @@ func (b *BootcVMCommon) tmpFileInjectSshKeyEnc() (string, error) {
274281
tmpFileCmdEnc := base64.StdEncoding.EncodeToString([]byte(tmpFileCmd))
275282
return tmpFileCmdEnc, nil
276283
}
284+
285+
func lockVM(params NewVMParameters, cacheDir string) (utils.CacheLock, error) {
286+
lock := utils.NewCacheLock(params.User.RunDir(), cacheDir)
287+
locked, err := lock.TryLock(params.Locking)
288+
if err != nil {
289+
return lock, fmt.Errorf("unable to lock the VM cache path: %w", err)
290+
}
291+
292+
if !locked {
293+
return lock, ErrVMInUse
294+
}
295+
296+
cacheDirExists, err := utils.FileExists(cacheDir)
297+
if err != nil {
298+
if err := lock.Unlock(); err != nil {
299+
logrus.Debugf("unlock failed: %v", err)
300+
}
301+
return lock, fmt.Errorf("unable to check cache path: %w", err)
302+
}
303+
if !cacheDirExists {
304+
if err := lock.Unlock(); err != nil {
305+
logrus.Debugf("unlock failed: %v", err)
306+
}
307+
return lock, fmt.Errorf("'%s' does not exists", params.ImageID)
308+
}
309+
310+
return lock, nil
311+
}

0 commit comments

Comments
 (0)