Skip to content

Commit 1fff6f9

Browse files
committed
Option --overlay-size
1 parent d495ebf commit 1fff6f9

File tree

8 files changed

+174
-17
lines changed

8 files changed

+174
-17
lines changed

runsc/boot/gofer_conf.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,24 @@ func (l *GoferMountConfLowerType) Set(v string) error {
124124

125125
// GoferMountConf describes how a gofer mount is configured in the sandbox.
126126
type GoferMountConf struct {
127-
Upper GoferMountConfUpperType `json:"upper"`
128127
Lower GoferMountConfLowerType `json:"lower"`
128+
Upper GoferMountConfUpperType `json:"upper"`
129+
Size string `json:"size,omitempty"`
129130
}
130131

131132
// String returns a human-readable string representing the gofer mount config.
132133
func (g GoferMountConf) String() string {
133-
return fmt.Sprintf("%s:%s", g.Lower, g.Upper)
134+
res := fmt.Sprintf("%s:%s", g.Lower, g.Upper)
135+
if g.Size != "" {
136+
res += ":size=" + g.Size
137+
}
138+
return res
134139
}
135140

136141
// Set sets the value. Set(String()) should be idempotent.
137142
func (g *GoferMountConf) Set(v string) error {
138143
parts := strings.Split(v, ":")
139-
if len(parts) != 2 {
144+
if len(parts) < 2 || len(parts) > 3 {
140145
return fmt.Errorf("invalid gofer mount config format: %q", v)
141146
}
142147
if err := g.Lower.Set(parts[0]); err != nil {
@@ -145,8 +150,17 @@ func (g *GoferMountConf) Set(v string) error {
145150
if err := g.Upper.Set(parts[1]); err != nil {
146151
return err
147152
}
153+
g.Size = ""
154+
if len(parts) >= 3 {
155+
sizeArg := parts[2]
156+
size, cut := strings.CutPrefix(sizeArg, "size=")
157+
if !cut {
158+
return fmt.Errorf("invalid gofer mount config format: %q", v)
159+
}
160+
g.Size = size
161+
}
148162
if !g.valid() {
149-
return fmt.Errorf("invalid gofer mount config: %+v", g)
163+
return fmt.Errorf("invalid gofer mount config: %q", v)
150164
}
151165
return nil
152166
}

runsc/boot/gofer_conf_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,28 @@ func TestGoferConfFlags(t *testing.T) {
175175
}
176176
}
177177
}
178+
179+
func TestGoferMountConfSetGet(t *testing.T) {
180+
t.Run("Without size", func(t *testing.T) {
181+
conf := GoferMountConf{}
182+
err := conf.Set("lisafs:anon")
183+
if err != nil {
184+
t.Fatalf("Expect success: %v", err)
185+
}
186+
s := conf.String()
187+
if s != "lisafs:anon" {
188+
t.Fatalf("Expected lisafs:anon, got %s", s)
189+
}
190+
})
191+
t.Run("With size", func(t *testing.T) {
192+
conf := GoferMountConf{}
193+
err := conf.Set("lisafs:anon:size=1719")
194+
if err != nil {
195+
t.Fatalf("Expect success: %v", err)
196+
}
197+
s := conf.String()
198+
if s != "lisafs:anon:size=1719" {
199+
t.Fatalf("Expected lisafs:anon:size=1719, got %s", s)
200+
}
201+
})
202+
}

runsc/boot/mount_hints.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ func (p *PodMountHints) FindMount(mountSrc string) *MountHint {
232232
type RootfsHint struct {
233233
Mount specs.Mount
234234
Overlay config.OverlayMedium
235+
// Size of overlay tmpfs. Passed as `size={Size}` to tmpfs mount.
236+
// Use default if unspecified.
237+
Size string
235238
}
236239

237240
func (r *RootfsHint) setSource(val string) error {
@@ -252,6 +255,15 @@ func (r *RootfsHint) setType(val string) error {
252255
return nil
253256
}
254257

258+
func (r *RootfsHint) setOptions(val string) error {
259+
size, cut := strings.CutPrefix(val, "size=")
260+
if !cut {
261+
return fmt.Errorf("only size= option is supported: %q", val)
262+
}
263+
r.Size = size
264+
return nil
265+
}
266+
255267
func (r *RootfsHint) setField(key, val string) error {
256268
switch key {
257269
case "source":
@@ -260,6 +272,8 @@ func (r *RootfsHint) setField(key, val string) error {
260272
return r.setType(val)
261273
case "overlay":
262274
return r.Overlay.Set(val)
275+
case "options":
276+
return r.setOptions(val)
263277
default:
264278
return fmt.Errorf("invalid rootfs annotation: %s=%s", key, val)
265279
}

runsc/boot/vfs.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,12 @@ func (c *containerMounter) configureOverlay(ctx context.Context, conf *config.Co
585585
// filesystem specific options.
586586
upperOpts := *lowerOpts
587587
upperOpts.GetFilesystemOptions = vfs.GetFilesystemOptions{InternalMount: true}
588+
if mountConf.Size != "" {
589+
if upperOpts.GetFilesystemOptions.Data != "" {
590+
upperOpts.GetFilesystemOptions.Data += ","
591+
}
592+
upperOpts.GetFilesystemOptions.Data += "size=" + mountConf.Size
593+
}
588594

589595
overlayOpts := *lowerOpts
590596
overlayOpts.GetFilesystemOptions = vfs.GetFilesystemOptions{InternalMount: true}

runsc/config/config.go

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -906,24 +906,42 @@ type Overlay2 struct {
906906
rootMount bool
907907
subMounts bool
908908
medium OverlayMedium
909+
// Size of overlay upper layer.
910+
// Passed as is to tmpfs mount, as `size={size}`.
911+
// Empty means use default.
912+
// Size is applied to each overlay independently and not shared by overlays.
913+
size string
909914
}
910915

911916
func defaultOverlay2() *Overlay2 {
912917
// Rootfs overlay is enabled by default and backed by a file in rootfs itself.
913918
return &Overlay2{rootMount: true, subMounts: false, medium: SelfOverlay}
914919
}
915920

921+
func setOverlay2Err(v string) error {
922+
return fmt.Errorf("expected format is --overlay2={mount}:{medium}[,size={size}], got %q", v)
923+
}
924+
925+
// `--overlay2=...` param `size=`.
926+
const overlay2SizeEq = "size="
927+
916928
// Set implements flag.Value. Set(String()) should be idempotent.
917929
func (o *Overlay2) Set(v string) error {
918930
if v == "none" {
919931
o.rootMount = false
920932
o.subMounts = false
921933
o.medium = NoOverlay
934+
o.size = ""
922935
return nil
923936
}
924-
vs := strings.Split(v, ":")
937+
parts := strings.Split(v, ",")
938+
if len(parts) < 1 {
939+
return setOverlay2Err(v)
940+
}
941+
942+
vs := strings.Split(parts[0], ":")
925943
if len(vs) != 2 {
926-
return fmt.Errorf("expected format is --overlay2={mount}:{medium}, got %q", v)
944+
return setOverlay2Err(v)
927945
}
928946

929947
switch mount := vs[0]; mount {
@@ -936,7 +954,25 @@ func (o *Overlay2) Set(v string) error {
936954
return fmt.Errorf("unexpected mount specifier for --overlay2: %q", mount)
937955
}
938956

939-
return o.medium.Set(vs[1])
957+
err := o.medium.Set(vs[1])
958+
if err != nil {
959+
return err
960+
}
961+
962+
if len(parts) == 1 {
963+
o.size = ""
964+
} else if len(parts) == 2 {
965+
sizeArg := parts[1]
966+
size, cut := strings.CutPrefix(sizeArg, overlay2SizeEq)
967+
if !cut {
968+
return setOverlay2Err(v)
969+
}
970+
o.size = size
971+
} else {
972+
return setOverlay2Err(v)
973+
}
974+
975+
return nil
940976
}
941977

942978
// Get implements flag.Value.
@@ -958,7 +994,13 @@ func (o Overlay2) String() string {
958994
default:
959995
panic("invalid state of subMounts = true and rootMount = false")
960996
}
961-
return res + ":" + o.medium.String()
997+
998+
var sizeSuffix string
999+
if o.size != "" {
1000+
sizeSuffix = fmt.Sprintf(",%s%s", overlay2SizeEq, o.size)
1001+
}
1002+
1003+
return res + ":" + o.medium.String() + sizeSuffix
9621004
}
9631005

9641006
// Enabled returns true if the overlay option is enabled for any mounts.
@@ -974,6 +1016,13 @@ func (o *Overlay2) RootOverlayMedium() OverlayMedium {
9741016
return o.medium
9751017
}
9761018

1019+
func (o *Overlay2) RootOverlaySize() string {
1020+
if !o.rootMount {
1021+
return ""
1022+
}
1023+
return o.size
1024+
}
1025+
9771026
// SubMountOverlayMedium returns the overlay medium config of submounts.
9781027
func (o *Overlay2) SubMountOverlayMedium() OverlayMedium {
9791028
if !o.subMounts {
@@ -982,6 +1031,13 @@ func (o *Overlay2) SubMountOverlayMedium() OverlayMedium {
9821031
return o.medium
9831032
}
9841033

1034+
func (o *Overlay2) SubMountOverlaySize() string {
1035+
if !o.subMounts {
1036+
return ""
1037+
}
1038+
return o.size
1039+
}
1040+
9851041
// Medium returns the overlay medium config.
9861042
func (o Overlay2) Medium() OverlayMedium {
9871043
return o.medium

runsc/config/config_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ func TestInvalidFlags(t *testing.T) {
231231
value: "root:dir=tmp",
232232
error: "overlay host file directory should be an absolute path, got \"tmp\"",
233233
},
234+
{
235+
name: "overlay2",
236+
value: "root:memory,sz=sdg",
237+
error: "expected format is --overlay2",
238+
},
234239
} {
235240
t.Run(tc.name, func(t *testing.T) {
236241
testFlags := flag.NewFlagSet("test", flag.ContinueOnError)
@@ -781,3 +786,32 @@ root = "%s"
781786
}
782787

783788
}
789+
790+
func TestParseSerializeOverlay2(t *testing.T) {
791+
t.Run("Without size", func(t *testing.T) {
792+
o := Overlay2{}
793+
err := o.Set("all:memory")
794+
if err != nil {
795+
t.Fatalf("Set failed: %v", err)
796+
}
797+
if o.RootOverlaySize() != "" || o.RootOverlayMedium() != "" {
798+
t.Fatalf("Size mismatch, expecting empty, got %q, %q", o.RootOverlaySize(), o.SubMountOverlaySize())
799+
}
800+
if o.String() != "all:memory" {
801+
t.Fatalf("String mismatch, expecting all:memory, got %q", o.String())
802+
}
803+
})
804+
t.Run("With size", func(t *testing.T) {
805+
o := Overlay2{}
806+
err := o.Set("root:memory,size=1g")
807+
if err != nil {
808+
t.Fatalf("Set failed: %v", err)
809+
}
810+
if o.RootOverlaySize() != "1g" || o.SubMountOverlaySize() != "" {
811+
t.Fatalf("Size mismatch, expecting 1g, empty, got %q, %q", o.RootOverlaySize(), o.SubMountOverlaySize())
812+
}
813+
if o.String() != "all:memory,size=1g" {
814+
t.Fatalf("String mismatch, expecting all:memory, got %q", o.String())
815+
}
816+
})
817+
}

runsc/config/flags.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,12 @@ func RegisterFlags(flagSet *flag.FlagSet) {
116116
flagSet.Var(fileAccessTypePtr(FileAccessExclusive), "file-access", "specifies which filesystem validation to use for the root mount: exclusive (default), shared.")
117117
flagSet.Var(fileAccessTypePtr(FileAccessShared), "file-access-mounts", "specifies which filesystem validation to use for volumes other than the root mount: shared (default), exclusive.")
118118
flagSet.Bool("overlay", false, "DEPRECATED: use --overlay2=all:memory to achieve the same effect")
119-
flagSet.Var(defaultOverlay2(), flagOverlay2, "wrap mounts with overlayfs. Format is {mount}:{medium}, where 'mount' can be 'root' or 'all' and medium can be 'memory', 'self' or 'dir=/abs/dir/path' in which filestore will be created. 'none' will turn overlay mode off.")
119+
flagSet.Var(defaultOverlay2(), flagOverlay2, "wrap mounts with overlayfs. Format is\n"+
120+
"* 'none' to turn overlay mode off\n"+
121+
"* {mount}:{medium}[,size={size}], where\n"+
122+
" 'mount' can be 'root' or 'all'\n"+
123+
" 'medium' can be 'memory', 'self' or 'dir=/abs/dir/path' in which filestore will be created\n"+
124+
" 'size' optional parameter overrides default overlay upper layer size\n")
120125
flagSet.Bool("fsgofer-host-uds", false, "DEPRECATED: use host-uds=all")
121126
flagSet.Var(hostUDSPtr(HostUDSNone), flagHostUDS, "controls permission to access host Unix-domain sockets. Values: none|open|create|all, default: none")
122127
flagSet.Var(hostFifoPtr(HostFifoNone), "host-fifo", "controls permission to access host FIFOs (or named pipes). Values: none|open, default: none")

runsc/container/container.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ func (c *Container) forEachSelfMount(fn func(mountSrc string)) {
899899
}
900900
}
901901

902-
func createGoferConf(overlayMedium config.OverlayMedium, mountType string, mountSrc string) (boot.GoferMountConf, error) {
902+
func createGoferConf(overlayMedium config.OverlayMedium, overlaySize string, mountType string, mountSrc string) (boot.GoferMountConf, error) {
903903
var lower boot.GoferMountConfLowerType
904904
switch mountType {
905905
case boot.Bind:
@@ -915,20 +915,20 @@ func createGoferConf(overlayMedium config.OverlayMedium, mountType string, mount
915915
case config.NoOverlay:
916916
return boot.GoferMountConf{Lower: lower, Upper: boot.NoOverlay}, nil
917917
case config.MemoryOverlay:
918-
return boot.GoferMountConf{Lower: lower, Upper: boot.MemoryOverlay}, nil
918+
return boot.GoferMountConf{Lower: lower, Upper: boot.MemoryOverlay, Size: overlaySize}, nil
919919
case config.SelfOverlay:
920920
mountSrcInfo, err := os.Stat(mountSrc)
921921
if err != nil {
922922
return boot.GoferMountConf{}, fmt.Errorf("failed to stat mount %q to see if it were a directory: %v", mountSrc, err)
923923
}
924924
if !mountSrcInfo.IsDir() {
925925
log.Warningf("self filestore is only supported for directory mounts, but mount %q is not a directory, falling back to memory", mountSrc)
926-
return boot.GoferMountConf{Lower: lower, Upper: boot.MemoryOverlay}, nil
926+
return boot.GoferMountConf{Lower: lower, Upper: boot.MemoryOverlay, Size: overlaySize}, nil
927927
}
928-
return boot.GoferMountConf{Lower: lower, Upper: boot.SelfOverlay}, nil
928+
return boot.GoferMountConf{Lower: lower, Upper: boot.SelfOverlay, Size: overlaySize}, nil
929929
default:
930930
if overlayMedium.IsBackedByAnon() {
931-
return boot.GoferMountConf{Lower: lower, Upper: boot.AnonOverlay}, nil
931+
return boot.GoferMountConf{Lower: lower, Upper: boot.AnonOverlay, Size: overlaySize}, nil
932932
}
933933
return boot.GoferMountConf{}, fmt.Errorf("unexpected overlay medium %q", overlayMedium)
934934
}
@@ -939,17 +939,19 @@ func createGoferConf(overlayMedium config.OverlayMedium, mountType string, mount
939939
func (c *Container) initGoferConfs(ovlConf config.Overlay2, mountHints *boot.PodMountHints, rootfsHint *boot.RootfsHint) error {
940940
// Handle root mount first.
941941
overlayMedium := ovlConf.RootOverlayMedium()
942+
overlaySize := ovlConf.RootOverlaySize()
942943
mountType := boot.Bind
943944
if rootfsHint != nil {
944945
overlayMedium = rootfsHint.Overlay
945946
if !specutils.IsGoferMount(rootfsHint.Mount) {
946947
mountType = rootfsHint.Mount.Type
947948
}
949+
overlaySize = rootfsHint.Size
948950
}
949951
if c.Spec.Root.Readonly {
950952
overlayMedium = config.NoOverlay
951953
}
952-
goferConf, err := createGoferConf(overlayMedium, mountType, c.Spec.Root.Path)
954+
goferConf, err := createGoferConf(overlayMedium, overlaySize, mountType, c.Spec.Root.Path)
953955
if err != nil {
954956
return err
955957
}
@@ -960,7 +962,8 @@ func (c *Container) initGoferConfs(ovlConf config.Overlay2, mountHints *boot.Pod
960962
if !specutils.IsGoferMount(c.Spec.Mounts[i]) {
961963
continue
962964
}
963-
overlayMedium = ovlConf.SubMountOverlayMedium()
965+
overlayMedium := ovlConf.SubMountOverlayMedium()
966+
overlaySize := ovlConf.SubMountOverlaySize()
964967
mountType = boot.Bind
965968
if specutils.IsReadonlyMount(c.Spec.Mounts[i].Options) {
966969
overlayMedium = config.NoOverlay
@@ -973,7 +976,7 @@ func (c *Container) initGoferConfs(ovlConf config.Overlay2, mountHints *boot.Pod
973976
mountType = hint.Mount.Type
974977
}
975978
}
976-
goferConf, err := createGoferConf(overlayMedium, mountType, c.Spec.Mounts[i].Source)
979+
goferConf, err := createGoferConf(overlayMedium, overlaySize, mountType, c.Spec.Mounts[i].Source)
977980
if err != nil {
978981
return err
979982
}

0 commit comments

Comments
 (0)