Skip to content

Commit bddf552

Browse files
committed
Merge branch 'add-file-locks' into 'main'
Lock the cache dir for doing operations Closes #11 See merge request bootc-org/podman-bootc-cli!62
2 parents 7105a78 + 7f16d67 commit bddf552

File tree

13 files changed

+202
-12
lines changed

13 files changed

+202
-12
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: 22 additions & 6 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"
@@ -103,6 +104,27 @@ func (p *BootcDisk) Install(quiet bool) (err error) {
103104
return
104105
}
105106

107+
// Create VM cache dir; one per oci bootc image
108+
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+
124+
if err := os.MkdirAll(p.Directory, os.ModePerm); err != nil {
125+
return fmt.Errorf("error while making bootc disk directory: %w", err)
126+
}
127+
106128
err = p.getOrInstallImageToDisk(quiet)
107129
if err != nil {
108130
return
@@ -246,12 +268,6 @@ func (p *BootcDisk) pullImage() (err error) {
246268
p.ImageId = imageId
247269
p.RepoTag = image.RepoTags[0]
248270

249-
// Create VM cache dir; one per oci bootc image
250-
p.Directory = filepath.Join(p.User.CacheDir(), imageId)
251-
if err := os.MkdirAll(p.Directory, os.ModePerm); err != nil {
252-
return fmt.Errorf("error while making bootc disk directory: %w", err)
253-
}
254-
255271
return
256272
}
257273

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)