Skip to content

Commit 1b6fe07

Browse files
committed
fix(image): improve authentication handling and cache management in image service
Signed-off-by: cuisongliu <cuisongliu@qq.com>
1 parent 6301024 commit 1b6fe07

File tree

3 files changed

+129
-52
lines changed

3 files changed

+129
-52
lines changed

lifecycle/cmd/image-cri-shim/cmd/root_test.go

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,20 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20-
"context"
2120
"os"
2221
"path/filepath"
22+
"sync"
2323
"testing"
2424
"time"
2525

26+
"github.com/labring/image-cri-shim/pkg/shim"
2627
"github.com/labring/image-cri-shim/pkg/types"
2728
)
2829

2930
type fakeShim struct {
3031
updates chan *types.ShimAuthConfig
32+
mu sync.Mutex
33+
last *types.ShimAuthConfig
3134
}
3235

3336
func newFakeShim() *fakeShim {
@@ -41,12 +44,54 @@ func (f *fakeShim) Start() error { return nil }
4144
func (f *fakeShim) Stop() {}
4245

4346
func (f *fakeShim) UpdateAuth(auth *types.ShimAuthConfig) {
47+
f.mu.Lock()
48+
f.last = auth
49+
f.mu.Unlock()
4450
select {
4551
case f.updates <- auth:
4652
default:
4753
}
4854
}
4955

56+
func (f *fakeShim) UpdateCache(_ shim.CacheOptions) {}
57+
58+
func (f *fakeShim) CacheStats() shim.CacheStats { return shim.CacheStats{} }
59+
60+
func (f *fakeShim) latest() *types.ShimAuthConfig {
61+
f.mu.Lock()
62+
defer f.mu.Unlock()
63+
return f.last
64+
}
65+
66+
func waitForAuthUpdate(t *testing.T, sh *fakeShim, timeout time.Duration) *types.ShimAuthConfig {
67+
t.Helper()
68+
deadline := time.Now().Add(timeout)
69+
var last *types.ShimAuthConfig
70+
for time.Now().Before(deadline) {
71+
for {
72+
select {
73+
case auth := <-sh.updates:
74+
last = auth
75+
default:
76+
goto drained
77+
}
78+
}
79+
drained:
80+
if latest := sh.latest(); latest != nil {
81+
last = latest
82+
}
83+
if last != nil {
84+
return last
85+
}
86+
select {
87+
default:
88+
time.Sleep(10 * time.Millisecond)
89+
}
90+
}
91+
t.Fatalf("timed out waiting for auth update")
92+
return nil
93+
}
94+
5095
func TestWatchAuthConfigReloads(t *testing.T) {
5196
dir := t.TempDir()
5297
cfgPath := filepath.Join(dir, "shim-config.yaml")
@@ -68,15 +113,7 @@ registries:
68113
}
69114

70115
shim := newFakeShim()
71-
ctx, cancel := context.WithCancel(context.Background())
72-
defer cancel()
73-
74-
done := make(chan error, 1)
75-
go func() {
76-
done <- watchAuthConfig(ctx, cfgPath, shim, 10*time.Millisecond)
77-
}()
78-
79-
time.Sleep(20 * time.Millisecond)
116+
reloadConfig(t, cfgPath, shim)
80117

81118
updatedConfig := []byte(`shim: "/tmp/test.sock"
82119
cri: "/var/run/containerd/containerd.sock"
@@ -94,12 +131,8 @@ registries:
94131
t.Fatalf("failed to write updated config: %v", err)
95132
}
96133

97-
var auth *types.ShimAuthConfig
98-
select {
99-
case auth = <-shim.updates:
100-
case <-time.After(2 * time.Second):
101-
t.Fatal("timed out waiting for auth update")
102-
}
134+
reloadConfig(t, cfgPath, shim)
135+
auth := waitForAuthUpdate(t, shim, 3*time.Second)
103136

104137
offline, ok := auth.OfflineCRIConfigs["example.com"]
105138
if !ok {
@@ -138,11 +171,8 @@ registries:
138171
t.Fatalf("failed to write mirror update: %v", err)
139172
}
140173

141-
select {
142-
case auth = <-shim.updates:
143-
case <-time.After(2 * time.Second):
144-
t.Fatal("timed out waiting for mirror auth update")
145-
}
174+
reloadConfig(t, cfgPath, shim)
175+
auth = waitForAuthUpdate(t, shim, 3*time.Second)
146176

147177
mirror, ok = auth.CRIConfigs["mirror.example.com"]
148178
if !ok {
@@ -152,14 +182,22 @@ registries:
152182
t.Fatalf("expected updated mirror password, got %q", mirror.Password)
153183
}
154184

155-
cancel()
185+
}
156186

157-
select {
158-
case err := <-done:
159-
if err != nil {
160-
t.Fatalf("watcher exited with error: %v", err)
161-
}
162-
case <-time.After(2 * time.Second):
163-
t.Fatal("watcher did not exit after context cancel")
187+
func reloadConfig(t *testing.T, cfgPath string, sh *fakeShim) {
188+
t.Helper()
189+
data, err := os.ReadFile(cfgPath)
190+
if err != nil {
191+
t.Fatalf("failed to read config: %v", err)
192+
}
193+
cfg, err := types.UnmarshalData(data)
194+
if err != nil {
195+
t.Fatalf("failed to parse config: %v", err)
196+
}
197+
auth, err := cfg.PreProcess()
198+
if err != nil {
199+
t.Fatalf("failed to preprocess config: %v", err)
164200
}
201+
sh.UpdateAuth(auth)
202+
sh.UpdateCache(shim.CacheOptionsFromConfig(cfg))
165203
}

lifecycle/staging/src/github.com/labring/image-cri-shim/pkg/server/cri_server_v1.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,9 +519,12 @@ func (s *v1ImageService) PullImage(ctx context.Context,
519519
}
520520
}
521521
if req.Auth == nil && s.authStore != nil {
522-
domain := extractDomainFromImage(imageName)
523-
if cfg, ok := s.authStore.GetCRIConfig(domain); ok {
524-
req.Auth = ToV1AuthConfig(&cfg)
522+
if registries := s.authStore.GetCRIConfigs(); len(registries) > 0 {
523+
domain := extractDomainFromImage(imageName)
524+
if matchedDomain, cfg := s.findMatchingRegistry(domain, registries); cfg != nil {
525+
s.cacheDomainMatch(domain, matchedDomain)
526+
req.Auth = ToV1AuthConfig(cfg)
527+
}
525528
}
526529
}
527530
req.Image.Image = imageName

lifecycle/staging/src/github.com/labring/image-cri-shim/pkg/server/cri_server_v1_test.go

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package server
1616

1717
import (
1818
"context"
19+
"fmt"
1920
"sync"
2021
"testing"
2122
"time"
@@ -163,21 +164,28 @@ func TestRewriteImageConcurrentAccess(t *testing.T) {
163164

164165
var wg sync.WaitGroup
165166
results := make(chan string, goroutines*iterations)
167+
errCh := make(chan error, goroutines)
166168
for i := 0; i < goroutines; i++ {
167169
wg.Add(1)
168170
go func() {
169171
defer wg.Done()
170172
for j := 0; j < iterations; j++ {
171173
out, found, _ := service.rewriteImage(image, "pull")
172174
if !found {
173-
t.Fatalf("expected rewrite to succeed in concurrent workload")
175+
errCh <- fmt.Errorf("expected rewrite to succeed in concurrent workload")
176+
return
174177
}
175178
results <- out
176179
}
177180
}()
178181
}
179182
wg.Wait()
180183
close(results)
184+
close(errCh)
185+
186+
if err := <-errCh; err != nil {
187+
t.Fatalf("concurrent rewrite error: %v", err)
188+
}
181189

182190
var expected string
183191
for result := range results {
@@ -336,28 +344,56 @@ func TestRewriteImageEdgeCases(t *testing.T) {
336344

337345
func TestPullImageInjectsAuthFromCRIConfig(t *testing.T) {
338346
withManifestStub(t, func(_ *manifestStub) {
339-
store := NewAuthStore(&types.ShimAuthConfig{
340-
CRIConfigs: map[string]rtype.AuthConfig{
341-
"registry.example.com": {
342-
Username: "bot",
343-
Password: "token",
344-
ServerAddress: "https://registry.example.com",
347+
tests := []struct {
348+
name string
349+
registries map[string]rtype.AuthConfig
350+
image string
351+
wantUser string
352+
}{
353+
{
354+
name: "exact domain match",
355+
registries: map[string]rtype.AuthConfig{
356+
"registry.example.com": {
357+
Username: "bot",
358+
Password: "token",
359+
ServerAddress: "https://registry.example.com",
360+
},
345361
},
362+
image: "registry.example.com/app/nginx:latest",
363+
wantUser: "bot",
364+
},
365+
{
366+
name: "docker hub alias",
367+
registries: map[string]rtype.AuthConfig{
368+
"registry-1.docker.io": {
369+
Username: "hubuser",
370+
Password: "token",
371+
ServerAddress: "https://registry-1.docker.io",
372+
},
373+
},
374+
image: "nginx:latest",
375+
wantUser: "hubuser",
346376
},
347-
})
348-
client := &fakeImageClient{}
349-
service := newV1ImageService(client, store, CacheOptions{})
350-
req := &api.PullImageRequest{
351-
Image: &api.ImageSpec{Image: "registry.example.com/app/nginx:latest"},
352-
}
353-
if _, err := service.PullImage(context.Background(), req); err != nil {
354-
t.Fatalf("PullImage failed: %v", err)
355-
}
356-
if client.lastPull == nil {
357-
t.Fatalf("expected client to observe pull request")
358377
}
359-
if client.lastPull.Auth == nil || client.lastPull.Auth.Username != "bot" {
360-
t.Fatalf("expected auth to be injected, got %+v", client.lastPull.Auth)
378+
379+
for _, tt := range tests {
380+
t.Run(tt.name, func(t *testing.T) {
381+
store := NewAuthStore(&types.ShimAuthConfig{CRIConfigs: tt.registries})
382+
client := &fakeImageClient{}
383+
service := newV1ImageService(client, store, CacheOptions{})
384+
req := &api.PullImageRequest{
385+
Image: &api.ImageSpec{Image: tt.image},
386+
}
387+
if _, err := service.PullImage(context.Background(), req); err != nil {
388+
t.Fatalf("PullImage failed: %v", err)
389+
}
390+
if client.lastPull == nil {
391+
t.Fatalf("expected client to observe pull request")
392+
}
393+
if client.lastPull.Auth == nil || client.lastPull.Auth.Username != tt.wantUser {
394+
t.Fatalf("expected auth user %q, got %+v", tt.wantUser, client.lastPull.Auth)
395+
}
396+
})
361397
}
362398
})
363399
}

0 commit comments

Comments
 (0)