Skip to content

Commit 96d7a4d

Browse files
authored
Merge pull request #599 from ginglis13/patch-soci
soci-snapshotter: add patches from upstream
2 parents 9668a6a + a73e2be commit 96d7a4d

File tree

4 files changed

+341
-1
lines changed

4 files changed

+341
-1
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
From 8b2bf80a852b68e7bdc24fcb5d35772566376ada Mon Sep 17 00:00:00 2001
2+
From: David Son <[email protected]>
3+
Date: Tue, 8 Jul 2025 23:32:29 +0000
4+
Subject: [PATCH] Remove image if rebase or initial fetch fails
5+
6+
If an image is unable to be claimed, the associated image pull with this
7+
request will fail, so it would make sense to also remove the rest of the
8+
jobs associated with this image.
9+
10+
Additionally, if the initial authorization fetch fails, we should also
11+
remove the image from unpackJobs to avoid blocking future image pulls.
12+
13+
Signed-off-by: David Son <[email protected]>
14+
---
15+
fs/fs.go | 51 ++++++++++++++++++++++++++++++---------------------
16+
1 file changed, 30 insertions(+), 21 deletions(-)
17+
18+
diff --git a/fs/fs.go b/fs/fs.go
19+
index 1eaac517..7a789b91 100644
20+
--- a/fs/fs.go
21+
+++ b/fs/fs.go
22+
@@ -478,33 +478,41 @@ func (fs *filesystem) preloadAllLayers(ctx context.Context, desc ocispec.Descrip
23+
premountCtx = namespaces.WithNamespace(premountCtx, ns)
24+
imageJob := fs.inProgressImageUnpacks.GetOrAddImageJob(imageDigest, cancel)
25+
26+
- // We only want to premount all layers that don't exist yet.
27+
- // Since layer order is deterministic, we can safely assume that
28+
- // every layer after this needs to be premounted as well.
29+
- startPremounting := false
30+
- for _, l := range manifest.Layers {
31+
- if images.IsLayerType(l.MediaType) {
32+
- if l.Digest.String() == desc.Digest.String() {
33+
- startPremounting = true
34+
-
35+
- // We don't have to preauthorize if we only do one request at a time
36+
- if fs.inProgressImageUnpacks.imagePullCfg.MaxConcurrentDownloadsPerImage != 1 {
37+
- err = remoteStore.doInitialFetch(ctx, constructRef(refspec, desc))
38+
- if err != nil {
39+
- return fmt.Errorf("error doing initial client fetch: %w", err)
40+
+ // If we fail anywhere after making the image job, we must remove the associated image job
41+
+ premountAll := func() error {
42+
+ // We only want to premount all layers that don't exist yet.
43+
+ // Since layer order is deterministic, we can safely assume that
44+
+ // every layer after this needs to be premounted as well.
45+
+ startPremounting := false
46+
+ for _, l := range manifest.Layers {
47+
+ if images.IsLayerType(l.MediaType) {
48+
+ if l.Digest.String() == desc.Digest.String() {
49+
+ startPremounting = true
50+
+
51+
+ // We don't have to preauthorize if we only do one request at a time
52+
+ if fs.inProgressImageUnpacks.imagePullCfg.MaxConcurrentDownloadsPerImage != 1 {
53+
+ err = remoteStore.doInitialFetch(ctx, constructRef(refspec, desc))
54+
+ if err != nil {
55+
+ return fmt.Errorf("error doing initial client fetch: %w", err)
56+
+ }
57+
}
58+
}
59+
- }
60+
- if startPremounting {
61+
- layerJob, err := fs.inProgressImageUnpacks.AddLayerJob(imageJob, l.Digest.String())
62+
- if err != nil {
63+
- return fmt.Errorf("error adding layer job: %w", err)
64+
+ if startPremounting {
65+
+ layerJob, err := fs.inProgressImageUnpacks.AddLayerJob(imageJob, l.Digest.String())
66+
+ if err != nil {
67+
+ return fmt.Errorf("error adding layer job: %w", err)
68+
+ }
69+
+ go fs.premount(premountCtx, l, refspec, remoteStore, diffIDMap, layerJob)
70+
}
71+
- go fs.premount(premountCtx, l, refspec, remoteStore, diffIDMap, layerJob)
72+
}
73+
}
74+
+ return nil
75+
}
76+
- return nil
77+
+
78+
+ if err := premountAll(); err != nil {
79+
+ fs.inProgressImageUnpacks.RemoveImageWithError(imageDigest, err)
80+
+ }
81+
+ return err
82+
}
83+
84+
func (fs *filesystem) premount(ctx context.Context, desc ocispec.Descriptor, refspec reference.Spec, remoteStore resolverStorage, diffIDMap map[string]digest.Digest, layerJob *layerUnpackJob) error {
85+
@@ -559,6 +567,7 @@ func (fs *filesystem) premount(ctx context.Context, desc ocispec.Descriptor, ref
86+
func (fs *filesystem) rebase(ctx context.Context, dgst digest.Digest, imageDigest, mountpoint string) error {
87+
layerJob, err := fs.inProgressImageUnpacks.Claim(imageDigest, dgst.String())
88+
if err != nil {
89+
+ fs.inProgressImageUnpacks.RemoveImageWithError(imageDigest, err)
90+
return fmt.Errorf("error attempting to claim job to rebase: %w", err)
91+
}
92+
defer func() {
93+
--
94+
2.47.1
95+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
From 3aa90d17adf339e04972d33f3925c0fcf5e691f9 Mon Sep 17 00:00:00 2001
2+
From: David Son <[email protected]>
3+
Date: Fri, 18 Jul 2025 19:47:42 +0000
4+
Subject: [PATCH] Move some ParallelPull integration to helper funcs
5+
6+
Signed-off-by: David Son <[email protected]>
7+
---
8+
integration/pull_test.go | 12 ++----------
9+
integration/util_test.go | 12 ++++++++++++
10+
2 files changed, 14 insertions(+), 10 deletions(-)
11+
12+
diff --git a/integration/pull_test.go b/integration/pull_test.go
13+
index 686fa10e..bf8ad2c7 100644
14+
--- a/integration/pull_test.go
15+
+++ b/integration/pull_test.go
16+
@@ -885,11 +885,7 @@ func TestPullWithParallelism(t *testing.T) {
17+
},
18+
{
19+
name: "set chunk size",
20+
- opts: []snapshotterConfigOpt{
21+
- func(cfg *config.Config) {
22+
- cfg.PullModes.Parallel.ConcurrentDownloadChunkSize = 1_000_000
23+
- },
24+
- },
25+
+ opts: []snapshotterConfigOpt{withConcurrentDownloadChunkSize(1_000_000)},
26+
},
27+
}
28+
29+
@@ -902,11 +898,7 @@ func TestPullWithParallelism(t *testing.T) {
30+
},
31+
{
32+
name: "parallel unpacking",
33+
- opts: []snapshotterConfigOpt{
34+
- func(cfg *config.Config) {
35+
- cfg.PullModes.Parallel.MaxConcurrentUnpacks = 3
36+
- },
37+
- },
38+
+ opts: []snapshotterConfigOpt{withConcurrentUnpacks(3)},
39+
},
40+
}
41+
42+
diff --git a/integration/util_test.go b/integration/util_test.go
43+
index ac2c84df..332a95ef 100644
44+
--- a/integration/util_test.go
45+
+++ b/integration/util_test.go
46+
@@ -377,6 +377,18 @@ func withUnboundedPullUnpack() snapshotterConfigOpt {
47+
}
48+
}
49+
50+
+func withConcurrentDownloadChunkSize(chunkSize int64) snapshotterConfigOpt {
51+
+ return func(c *config.Config) {
52+
+ c.PullModes.Parallel.ConcurrentDownloadChunkSize = chunkSize
53+
+ }
54+
+}
55+
+
56+
+func withConcurrentUnpacks(n int64) snapshotterConfigOpt {
57+
+ return func(c *config.Config) {
58+
+ c.PullModes.Parallel.MaxConcurrentUnpacks = n
59+
+ }
60+
+}
61+
+
62+
func withDiscardUnpackedLayers() snapshotterConfigOpt {
63+
return func(c *config.Config) {
64+
c.PullModes.Parallel.DiscardUnpackedLayers = true
65+
--
66+
2.47.1
67+
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
From 73dd46eed7a77f5f5b454eaeb1c227fb5efab320 Mon Sep 17 00:00:00 2001
2+
From: David Son <[email protected]>
3+
Date: Fri, 18 Jul 2025 19:42:35 +0000
4+
Subject: [PATCH] Hard-fail on config parsing errors
5+
6+
Signed-off-by: David Son <[email protected]>
7+
---
8+
config/config.go | 21 ++++++++++++-----
9+
config/config_test.go | 12 ++++++++++
10+
integration/startup_test.go | 46 +++++++++++++++++++++++++++++++++++++
11+
integration/util_test.go | 6 +++++
12+
4 files changed, 79 insertions(+), 6 deletions(-)
13+
14+
diff --git a/config/config.go b/config/config.go
15+
index 25251c0f..6deb4818 100644
16+
--- a/config/config.go
17+
+++ b/config/config.go
18+
@@ -39,6 +39,7 @@
19+
package config
20+
21+
import (
22+
+ "errors"
23+
"fmt"
24+
"os"
25+
26+
@@ -84,8 +85,8 @@ func NewConfig() *Config {
27+
28+
// Set any defaults which do not align with Go zero values.
29+
var initParsers = []configParser{defaultPullModes, defaultDirectoryCacheConfig}
30+
- for _, p := range append(initParsers, parsers...) {
31+
- p(cfg)
32+
+ if err := parseConfig(cfg, append(initParsers, parsers...)); err != nil {
33+
+ return nil
34+
}
35+
36+
return cfg
37+
@@ -102,18 +103,26 @@ func NewConfigFromToml(cfgPath string) (*Config, error) {
38+
defer f.Close()
39+
40+
cfg := NewConfig()
41+
+ if cfg == nil {
42+
+ return nil, errors.New("error creating default config")
43+
+ }
44+
// Get configuration from specified file
45+
if err = toml.NewDecoder(f).Decode(cfg); err != nil {
46+
return nil, fmt.Errorf("failed to load config file %q: %w", cfgPath, err)
47+
}
48+
- parseConfig(cfg)
49+
+ if err := parseConfig(cfg, parsers); err != nil {
50+
+ return nil, fmt.Errorf("config file at %q: %w", cfgPath, err)
51+
+ }
52+
return cfg, nil
53+
}
54+
55+
-func parseConfig(cfg *Config) {
56+
- for _, p := range parsers {
57+
- p(cfg)
58+
+func parseConfig(cfg *Config, cfgParsers []configParser) error {
59+
+ for _, p := range cfgParsers {
60+
+ if err := p(cfg); err != nil {
61+
+ return fmt.Errorf("failed to parse config: %v", err)
62+
+ }
63+
}
64+
+ return nil
65+
}
66+
67+
func parseRootConfig(cfg *Config) error {
68+
diff --git a/config/config_test.go b/config/config_test.go
69+
index c1b73720..4facc5e3 100644
70+
--- a/config/config_test.go
71+
+++ b/config/config_test.go
72+
@@ -265,6 +265,18 @@ concurrent_download_chunk_size = "1MB"
73+
}
74+
},
75+
},
76+
+ {
77+
+ name: "IncorrectChunkConfig",
78+
+ config: []byte(`
79+
+[pull_modes.parallel_pull_unpack]
80+
+concurrent_download_chunk_size = "badchunksize"
81+
+`),
82+
+ assert: func(t *testing.T, actual *Config, err error) {
83+
+ if err == nil {
84+
+ t.Error("Expected error, got none")
85+
+ }
86+
+ },
87+
+ },
88+
}
89+
90+
for _, test := range tests {
91+
diff --git a/integration/startup_test.go b/integration/startup_test.go
92+
index f69488bb..e1774205 100644
93+
--- a/integration/startup_test.go
94+
+++ b/integration/startup_test.go
95+
@@ -21,9 +21,11 @@ import (
96+
"regexp"
97+
"strings"
98+
"testing"
99+
+ "time"
100+
101+
"github.com/awslabs/soci-snapshotter/config"
102+
shell "github.com/awslabs/soci-snapshotter/util/dockershell"
103+
+ "github.com/awslabs/soci-snapshotter/util/testutil"
104+
"github.com/rs/xid"
105+
)
106+
107+
@@ -137,3 +139,47 @@ func TestSnapshotterSystemdStartup(t *testing.T) {
108+
})
109+
}
110+
}
111+
+
112+
+// TestSnapshotterStartupWithBadConfig ensures snapshotter does not start if snapshotter values are incorrect.
113+
+// Note that incorrect fields are ignored by TOML and thus are expected to work
114+
+func TestSnapshotterStartupWithBadConfig(t *testing.T) {
115+
+ tests := []struct {
116+
+ name string
117+
+ opts []snapshotterConfigOpt
118+
+ expectedErrStr string
119+
+ }{
120+
+ {
121+
+ name: "Bad parallel pull size",
122+
+ opts: []snapshotterConfigOpt{withParallelPullMode(), withConcurrentDownloadChunkSizeStr("badstring")},
123+
+ expectedErrStr: "invalid size format",
124+
+ },
125+
+ }
126+
+
127+
+ for _, tc := range tests {
128+
+ t.Run(tc.name, func(tt *testing.T) {
129+
+ sh, cleanup := newSnapshotterBaseShell(t)
130+
+ defer cleanup()
131+
+ // rebootContainerd would be ideal here, but that always uses a config,
132+
+ // so we just manually start SOCI with/without a config file.
133+
+ err := testutil.KillMatchingProcess(sh, "soci-snapshotter-grpc")
134+
+ if err != nil {
135+
+ sh.Fatal("failed to kill soci: %v", err)
136+
+ }
137+
+ configToml := getSnapshotterConfigToml(t, tc.opts...)
138+
+ snRunCmd := []string{"/usr/local/bin/soci-snapshotter-grpc", "--log-level", sociLogLevel}
139+
+ snRunCmd = addConfig(t, sh, configToml, snRunCmd...)
140+
+
141+
+ errCh := make(chan error, 1)
142+
+ go func() {
143+
+ _, err := sh.OLog(snRunCmd...)
144+
+ errCh <- err
145+
+ }()
146+
+
147+
+ select {
148+
+ case <-errCh:
149+
+ case <-time.After(2 * time.Second):
150+
+ t.Fatalf("expected err %s but snapshotter did not fail within 2 seconds", tc.expectedErrStr)
151+
+ }
152+
+ })
153+
+ }
154+
+}
155+
diff --git a/integration/util_test.go b/integration/util_test.go
156+
index 332a95ef..dda17f5f 100644
157+
--- a/integration/util_test.go
158+
+++ b/integration/util_test.go
159+
@@ -383,6 +383,12 @@ func withConcurrentDownloadChunkSize(chunkSize int64) snapshotterConfigOpt {
160+
}
161+
}
162+
163+
+func withConcurrentDownloadChunkSizeStr(chunkSizeStr string) snapshotterConfigOpt {
164+
+ return func(c *config.Config) {
165+
+ c.PullModes.Parallel.ConcurrentDownloadChunkSizeStr = chunkSizeStr
166+
+ }
167+
+}
168+
+
169+
func withConcurrentUnpacks(n int64) snapshotterConfigOpt {
170+
return func(c *config.Config) {
171+
c.PullModes.Parallel.MaxConcurrentUnpacks = n
172+
--
173+
2.47.1
174+

packages/soci-snapshotter/soci-snapshotter.spec

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ Source101: soci-snapshotter.service
1717
Source102: soci-snapshotter.socket
1818
Source1000: clarify.toml
1919

20+
Patch1001: 1001-remove-image-if-rebase-or-initial-fetch-fails.patch
21+
Patch1002: 1002-move-some-parallelpull-integration-to-helper-funcs.patch
22+
Patch1003: 1003-hard-fail-on-config-parsing-errors.patch
23+
2024
BuildRequires: %{_cross_os}glibc-devel
2125
BuildRequires: %{_cross_os}libz-devel
2226
Requires: %{name}(binaries)
@@ -43,7 +47,7 @@ Conflicts: (%{_cross_os}image-feature(no-fips) or %{name}-bin)
4347
%{summary}.
4448

4549
%prep
46-
%setup -n %{gorepo}-%{gover} -q
50+
%autosetup -n %{gorepo}-%{gover} -p1
4751
%setup -T -D -n %{gorepo}-%{gover} -b 1 -q
4852
%setup -T -D -n %{gorepo}-%{gover} -b 2 -q
4953

0 commit comments

Comments
 (0)