Skip to content

Commit 6f4f8a0

Browse files
committed
volume: refactor volume.Set to be aware about GuestVolumeImageProvider
By knowing GuestVolumeImageProvider, callers can add any volumes regardless of its lifecycle. For example, demux-backed volumes are only accessible after starting a VM. Signed-off-by: Kazuyoshi Kato <[email protected]>
1 parent c2bc25c commit 6f4f8a0

File tree

4 files changed

+110
-52
lines changed

4 files changed

+110
-52
lines changed

snapshotter/volume_integ_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,15 @@ func TestGuestVolumeFrom_Isolated(t *testing.T) {
6161
remoteImage := volume.FromGuestImage(
6262
client, vmID, al2stargz, "al2-snapshot", []string{"/etc/yum"},
6363
volume.WithSnapshotter("demux"),
64+
volume.WithPullOptions(containerd.WithImageHandlerWrapper(
65+
source.AppendDefaultLabelsHandlerWrapper(al2stargz, 10*mib),
66+
)),
6467
)
68+
err = vs.AddFrom(ctx, remoteImage)
69+
require.NoError(t, err)
70+
71+
// PrepareDriveMount only copies images that are available before starting the VM.
72+
// In this case, only postgres.
6573
mount, err := vs.PrepareDriveMount(ctx, 10*mib)
6674
require.NoError(t, err)
6775

@@ -95,15 +103,9 @@ func TestGuestVolumeFrom_Isolated(t *testing.T) {
95103
})
96104
require.NoError(t, err, "Failed to configure VM metadata for registry resolution")
97105

98-
err = remoteImage.Pull(ctx,
99-
containerd.WithImageHandlerWrapper(source.AppendDefaultLabelsHandlerWrapper(al2stargz, 10*mib)),
100-
)
101-
require.NoError(t, err)
102-
103-
err = remoteImage.Copy(ctx, "image")
104-
require.NoError(t, err)
105-
106-
err = vs.AddFrom(ctx, remoteImage)
106+
// PrepareGuestVolumes only copies images that are only available after starting the VM.
107+
// In this case, only al2stargz.
108+
err = vs.PrepareInGuest(ctx, "prepare-in-guest")
107109
require.NoError(t, err)
108110

109111
image, err := client.Pull(ctx,

volume/guest_image.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ func (p *GuestVolumeImageProvider) Name() string {
8686
return p.image
8787
}
8888

89-
// Pull the image.
90-
func (p *GuestVolumeImageProvider) Pull(ctx context.Context, opts ...containerd.RemoteOpt) error {
89+
// pull the image.
90+
func (p *GuestVolumeImageProvider) pull(ctx context.Context) error {
9191
remoteOpts := []containerd.RemoteOpt{
9292
containerd.WithPullUnpack,
9393
containerd.WithPullSnapshotter(p.config.snapshotter),
9494
}
95-
remoteOpts = append(remoteOpts, opts...)
95+
remoteOpts = append(remoteOpts, p.config.pullOpts...)
9696
image, err := p.client.Pull(ctx, p.image, remoteOpts...)
9797
if err != nil {
9898
return err
@@ -122,30 +122,30 @@ func mountHostDirs(image string) oci.SpecOpts {
122122
}
123123
}
124124

125-
// Copy files from the image by launching the container.
125+
// copy files from the image by launching the container.
126126
// The name must be unique within the VM and must be /[A-Z0-9a-z][A-Z0-9a-z._-]*/.
127-
func (p *GuestVolumeImageProvider) Copy(ctx context.Context, name string) error {
128-
err := identifiers.Validate(name)
127+
func (p *GuestVolumeImageProvider) copy(ctx context.Context, containerName string) error {
128+
err := identifiers.Validate(containerName)
129129
if err != nil {
130130
return err
131131
}
132132
container, err := p.client.NewContainer(ctx,
133-
name,
133+
containerName,
134134
containerd.WithSnapshotter(p.config.snapshotter),
135135
containerd.WithNewSnapshot(p.snapshot, p.cImage),
136136
containerd.WithNewSpec(
137137
firecrackeroci.WithVMID(p.vmID),
138138
oci.WithProcessArgs(filepath.Join(containerBinDir, "volume-init")),
139139
oci.WithProcessCwd("/"),
140-
mountHostDirs(name),
140+
mountHostDirs(containerName),
141141
),
142142
)
143143
if err != nil {
144-
return fmt.Errorf("failed to create container %q with %q: %w", name, p.config.snapshotter, err)
144+
return fmt.Errorf("failed to create container %q with %q: %w", containerName, p.config.snapshotter, err)
145145
}
146146
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
147147

148-
input := GuestVolumeImageInput{From: "/", To: filepath.Join(containerVolumesDir, name), Volumes: p.volumes}
148+
input := GuestVolumeImageInput{From: "/", To: filepath.Join(containerVolumesDir, containerName), Volumes: p.volumes}
149149
b, err := json.Marshal(&input)
150150
if err != nil {
151151
return err
@@ -197,7 +197,7 @@ func (p *GuestVolumeImageProvider) Copy(ctx context.Context, name string) error
197197
return ctx.Err()
198198
}
199199

200-
p.vmDir = filepath.Join(vmVolumesDir, name)
200+
p.vmDir = filepath.Join(vmVolumesDir, containerName)
201201
return nil
202202
}
203203

volume/image.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333

3434
type imageProviderConfig struct {
3535
snapshotter string
36+
pullOpts []containerd.RemoteOpt
3637
}
3738

3839
type imageVolumeProvider struct {
@@ -53,6 +54,13 @@ func WithSnapshotter(ss string) ImageOpt {
5354
}
5455
}
5556

57+
// WithPullOptions sets the snapshotter's pull options.
58+
func WithPullOptions(opts ...containerd.RemoteOpt) ImageOpt {
59+
return func(c *imageProviderConfig) {
60+
c.pullOpts = opts
61+
}
62+
}
63+
5664
func getV1ImageConfig(ctx context.Context, image containerd.Image) (*v1.Image, error) {
5765
var result v1.Image
5866

volume/set.go

Lines changed: 80 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ const (
3838
// Set is a set of volumes.
3939
type Set struct {
4040
volumes map[string]*Volume
41-
providers map[string][]Mount
41+
mounts map[string][]Mount
42+
providers map[string]Provider
4243
tempDir string
4344
runtime string
4445
volumeDir string
@@ -65,7 +66,8 @@ func NewSet(runtime string) *Set {
6566
func NewSetWithTempDir(runtime, tempDir string) *Set {
6667
return &Set{
6768
runtime: runtime,
68-
providers: make(map[string][]Mount),
69+
mounts: make(map[string][]Mount),
70+
providers: make(map[string]Provider),
6971
volumes: make(map[string]*Volume),
7072
tempDir: tempDir,
7173
}
@@ -81,7 +83,12 @@ func (vs *Set) AddFrom(ctx context.Context, vp Provider) error {
8183
if _, exists := vs.providers[vp.Name()]; exists {
8284
return fmt.Errorf("failed to add %q: %w", vp.Name(), errdefs.ErrAlreadyExists)
8385
}
84-
dir, err := os.MkdirTemp(vs.tempDir, "AddFrom")
86+
vs.providers[vp.Name()] = vp
87+
return nil
88+
}
89+
90+
func (vs *Set) copyToHostFromProvider(ctx context.Context, vp Provider) error {
91+
dir, err := os.MkdirTemp(vs.tempDir, "copyToHostFromProvider")
8592
if err != nil {
8693
return err
8794
}
@@ -100,7 +107,7 @@ func (vs *Set) AddFrom(ctx context.Context, vp Provider) error {
100107

101108
mounts = append(mounts, Mount{key: v.name, Destination: v.containerPath, ReadOnly: false})
102109
}
103-
vs.providers[vp.Name()] = mounts
110+
vs.mounts[vp.Name()] = mounts
104111
return nil
105112
}
106113

@@ -147,7 +154,7 @@ func mountDiskImage(source, target string) error {
147154

148155
// PrepareDirectory creates a directory that have volumes.
149156
func (vs *Set) PrepareDirectory(ctx context.Context) (retErr error) {
150-
dir, err := os.MkdirTemp(vs.tempDir, "Prepare")
157+
dir, err := os.MkdirTemp(vs.tempDir, "PrepareDirectory")
151158
if err != nil {
152159
retErr = err
153160
return
@@ -161,19 +168,10 @@ func (vs *Set) PrepareDirectory(ctx context.Context) (retErr error) {
161168
}
162169
}()
163170

164-
for _, v := range vs.volumes {
165-
path, err := fs.RootPath(dir, v.name)
166-
if err != nil {
167-
return err
168-
}
169-
if v.hostPath == "" {
170-
continue
171-
}
172-
err = fs.CopyDir(path, v.hostPath)
173-
if err != nil {
174-
retErr = fmt.Errorf("failed to copy volume %q: %w", v.name, err)
175-
return
176-
}
171+
err = vs.copyToHost(ctx, dir)
172+
if err != nil {
173+
retErr = err
174+
return
177175
}
178176

179177
vs.volumeDir = dir
@@ -221,30 +219,75 @@ func (vs *Set) PrepareDriveMount(ctx context.Context, size int64) (dm *proto.Fir
221219
}
222220
}()
223221

222+
err = vs.copyToHost(ctx, dir)
223+
if err != nil {
224+
retErr = err
225+
return
226+
}
227+
228+
dm = &proto.FirecrackerDriveMount{
229+
HostPath: path,
230+
VMPath: vmVolumePath,
231+
FilesystemType: fsType,
232+
IsWritable: true,
233+
}
234+
vs.volumeDir = vmVolumePath
235+
return
236+
}
237+
238+
// PrepareInGuest prepares volumes inside the VM.
239+
func (vs *Set) PrepareInGuest(ctx context.Context, container string) error {
240+
for _, provider := range vs.providers {
241+
gp, guest := provider.(*GuestVolumeImageProvider)
242+
if !guest {
243+
continue
244+
}
245+
246+
err := gp.pull(ctx)
247+
if err != nil {
248+
return err
249+
}
250+
251+
err = gp.copy(ctx, container)
252+
if err != nil {
253+
return err
254+
}
255+
256+
err = vs.copyToHostFromProvider(ctx, provider)
257+
if err != nil {
258+
return err
259+
}
260+
}
261+
return nil
262+
}
263+
264+
func (vs *Set) copyToHost(ctx context.Context, dir string) error {
265+
for _, provider := range vs.providers {
266+
_, guest := provider.(*GuestVolumeImageProvider)
267+
if guest {
268+
continue
269+
}
270+
271+
err := vs.copyToHostFromProvider(ctx, provider)
272+
if err != nil {
273+
return err
274+
}
275+
}
276+
224277
for _, v := range vs.volumes {
225278
path, err := fs.RootPath(dir, v.name)
226279
if err != nil {
227-
retErr = err
228-
return
280+
return err
229281
}
230282
if v.hostPath == "" {
231283
continue
232284
}
233285
err = fs.CopyDir(path, v.hostPath)
234286
if err != nil {
235-
retErr = fmt.Errorf("failed to copy volume %q: %w", v.name, err)
236-
return
287+
return fmt.Errorf("failed to copy volume %q: %w", v.name, err)
237288
}
238289
}
239-
240-
dm = &proto.FirecrackerDriveMount{
241-
HostPath: path,
242-
VMPath: vmVolumePath,
243-
FilesystemType: fsType,
244-
IsWritable: true,
245-
}
246-
vs.volumeDir = vmVolumePath
247-
return
290+
return nil
248291
}
249292

250293
// Mount is used to expose volumes to containers.
@@ -303,5 +346,10 @@ func (vs *Set) WithMounts(mountpoints []Mount) (oci.SpecOpts, error) {
303346

304347
// WithMountsFromProvider exposes volumes from the provider.
305348
func (vs *Set) WithMountsFromProvider(name string) (oci.SpecOpts, error) {
306-
return vs.WithMounts(vs.providers[name])
349+
_, exists := vs.providers[name]
350+
if !exists {
351+
return nil, fmt.Errorf("failed to find volume %q: %w", name, errdefs.ErrNotFound)
352+
}
353+
354+
return vs.WithMounts(vs.mounts[name])
307355
}

0 commit comments

Comments
 (0)