Skip to content

Commit 2b50012

Browse files
authored
Make FakeAPIClient threadsafe (#4790)
1 parent 0917dba commit 2b50012

File tree

3 files changed

+85
-42
lines changed

3 files changed

+85
-42
lines changed

pkg/skaffold/build/buildpacks/fetcher_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func TestFetcher(t *testing.T) {
5454
f := newFetcher(&out, docker)
5555
f.Fetch(context.Background(), "image", true, test.pull)
5656

57-
t.CheckDeepEqual(test.expectedPulled, api.Pulled)
57+
t.CheckDeepEqual(test.expectedPulled, api.Pulled())
5858
})
5959
}
6060
}

pkg/skaffold/build/local/local_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ func TestLocalRun(t *testing.T) {
260260

261261
t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, res)
262262
t.CheckDeepEqual(test.expectedWarnings, fakeWarner.Warnings)
263-
t.CheckDeepEqual(test.expectedPushed, test.api.Pushed)
263+
t.CheckDeepEqual(test.expectedPushed, test.api.Pushed())
264264
})
265265
}
266266
}

testutil/fake_image_api.go

+83-40
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
"io/ioutil"
2626
"os"
2727
"strings"
28+
"sync"
29+
"sync/atomic"
2830

2931
"github.com/docker/docker/api/types"
3032
"github.com/docker/docker/api/types/registry"
@@ -44,18 +46,20 @@ const (
4446
type FakeAPIClient struct {
4547
client.CommonAPIClient
4648

47-
tagToImageID map[string]string
4849
ErrImageBuild bool
4950
ErrImageInspect bool
5051
ErrImagePush bool
5152
ErrImagePull bool
5253
ErrStream bool
5354
ErrVersion bool
5455

55-
nextImageID int
56-
Pushed map[string]string
57-
Pulled []string
58-
Built []types.ImageBuildOptions
56+
nextImageID int32
57+
tagToImageID sync.Map // map[string]string
58+
pushed sync.Map // map[string]string
59+
pulled sync.Map // map[string]string
60+
61+
mux sync.Mutex
62+
Built []types.ImageBuildOptions
5963
}
6064

6165
func (f *FakeAPIClient) ServerVersion(ctx context.Context) (types.Version, error) {
@@ -66,18 +70,35 @@ func (f *FakeAPIClient) ServerVersion(ctx context.Context) (types.Version, error
6670
}
6771

6872
func (f *FakeAPIClient) Add(tag, imageID string) *FakeAPIClient {
69-
if f.tagToImageID == nil {
70-
f.tagToImageID = make(map[string]string)
71-
}
72-
73-
f.tagToImageID[imageID] = imageID
74-
f.tagToImageID[tag] = imageID
73+
f.tagToImageID.Store(imageID, imageID)
74+
f.tagToImageID.Store(tag, imageID)
7575
if !strings.Contains(tag, ":") {
76-
f.tagToImageID[tag+":latest"] = imageID
76+
f.tagToImageID.Store(tag+":latest", imageID)
7777
}
7878
return f
7979
}
8080

81+
func (f *FakeAPIClient) Pulled() []string {
82+
var p []string
83+
f.pulled.Range(func(ref, _ interface{}) bool {
84+
p = append(p, ref.(string))
85+
return true
86+
})
87+
return p
88+
}
89+
90+
func (f *FakeAPIClient) Pushed() map[string]string {
91+
p := make(map[string]string)
92+
f.pushed.Range(func(ref, id interface{}) bool {
93+
p[ref.(string)] = id.(string)
94+
return true
95+
})
96+
if len(p) == 0 {
97+
return nil
98+
}
99+
return p
100+
}
101+
81102
type notFoundError struct {
82103
error
83104
}
@@ -103,49 +124,69 @@ func (f *FakeAPIClient) ImageBuild(_ context.Context, _ io.Reader, options types
103124
return types.ImageBuildResponse{}, fmt.Errorf("")
104125
}
105126

106-
f.nextImageID++
107-
imageID := fmt.Sprintf("sha256:%d", f.nextImageID)
127+
next := atomic.AddInt32(&f.nextImageID, 1)
128+
imageID := fmt.Sprintf("sha256:%d", next)
108129

109130
for _, tag := range options.Tags {
110131
f.Add(tag, imageID)
111132
}
112133

134+
f.mux.Lock()
113135
f.Built = append(f.Built, options)
136+
f.mux.Unlock()
114137

115138
return types.ImageBuildResponse{
116139
Body: f.body(imageID),
117140
}, nil
118141
}
119142

120-
func (f *FakeAPIClient) ImageInspectWithRaw(_ context.Context, ref string) (types.ImageInspect, []byte, error) {
143+
func (f *FakeAPIClient) ImageInspectWithRaw(_ context.Context, refOrID string) (types.ImageInspect, []byte, error) {
121144
if f.ErrImageInspect {
122145
return types.ImageInspect{}, nil, fmt.Errorf("")
123146
}
124147

125-
for tag, imageID := range f.tagToImageID {
126-
if tag == ref || imageID == ref {
127-
rawConfig := []byte(fmt.Sprintf(`{"Config":{"Image":"%s"}}`, imageID))
148+
ref, imageID, err := f.findImageID(refOrID)
149+
if err != nil {
150+
return types.ImageInspect{}, nil, err
151+
}
128152

129-
var repoDigests []string
130-
if digest, found := f.Pushed[ref]; found {
131-
repoDigests = append(repoDigests, ref+"@"+digest)
132-
}
153+
rawConfig := []byte(fmt.Sprintf(`{"Config":{"Image":"%s"}}`, imageID))
133154

134-
return types.ImageInspect{
135-
ID: imageID,
136-
RepoDigests: repoDigests,
137-
}, rawConfig, nil
138-
}
155+
var repoDigests []string
156+
if digest, found := f.pushed.Load(ref); found {
157+
repoDigests = append(repoDigests, ref+"@"+digest.(string))
139158
}
140159

141-
return types.ImageInspect{}, nil, &notFoundError{}
160+
return types.ImageInspect{
161+
ID: imageID,
162+
RepoDigests: repoDigests,
163+
}, rawConfig, nil
164+
}
165+
166+
func (f *FakeAPIClient) findImageID(refOrID string) (string, string, error) {
167+
if id, found := f.tagToImageID.Load(refOrID); found {
168+
return refOrID, id.(string), nil
169+
}
170+
var ref, id string
171+
f.tagToImageID.Range(func(r, i interface{}) bool {
172+
if r == refOrID || i == refOrID {
173+
ref = r.(string)
174+
id = i.(string)
175+
return false
176+
}
177+
return true
178+
})
179+
if ref == "" {
180+
return "", "", &notFoundError{}
181+
}
182+
return ref, id, nil
142183
}
143184

144185
func (f *FakeAPIClient) DistributionInspect(ctx context.Context, ref, encodedRegistryAuth string) (registry.DistributionInspect, error) {
145-
if sha, found := f.Pushed[ref]; found {
186+
if sha, found := f.pushed.Load(ref); found {
146187
return registry.DistributionInspect{
147188
Descriptor: v1.Descriptor{
148-
Digest: digest.Digest(sha),
189+
Digest: digest.Digest(sha.(string)),
149190
},
150191
}, nil
151192
}
@@ -154,12 +195,12 @@ func (f *FakeAPIClient) DistributionInspect(ctx context.Context, ref, encodedReg
154195
}
155196

156197
func (f *FakeAPIClient) ImageTag(_ context.Context, image, ref string) error {
157-
imageID, ok := f.tagToImageID[image]
198+
imageID, ok := f.tagToImageID.Load(image)
158199
if !ok {
159200
return fmt.Errorf("image %s not found", image)
160201
}
161202

162-
f.Add(ref, imageID)
203+
f.Add(ref, imageID.(string))
163204
return nil
164205
}
165206

@@ -168,22 +209,24 @@ func (f *FakeAPIClient) ImagePush(_ context.Context, ref string, _ types.ImagePu
168209
return nil, fmt.Errorf("")
169210
}
170211

212+
// use the digest if previously pushed
213+
imageID, found := f.tagToImageID.Load(ref)
214+
if !found {
215+
imageID = ""
216+
}
171217
sha256Digester := sha256.New()
172-
if _, err := sha256Digester.Write([]byte(f.tagToImageID[ref])); err != nil {
218+
if _, err := sha256Digester.Write([]byte(imageID.(string))); err != nil {
173219
return nil, err
174220
}
175221

176222
digest := "sha256:" + fmt.Sprintf("%x", sha256Digester.Sum(nil))[0:64]
177-
if f.Pushed == nil {
178-
f.Pushed = make(map[string]string)
179-
}
180-
f.Pushed[ref] = digest
181223

224+
f.pushed.Store(ref, digest)
182225
return f.body(digest), nil
183226
}
184227

185228
func (f *FakeAPIClient) ImagePull(_ context.Context, ref string, _ types.ImagePullOptions) (io.ReadCloser, error) {
186-
f.Pulled = append(f.Pulled, ref)
229+
f.pulled.Store(ref, ref)
187230
if f.ErrImagePull {
188231
return nil, fmt.Errorf("")
189232
}
@@ -203,8 +246,8 @@ func (f *FakeAPIClient) ImageLoad(ctx context.Context, input io.Reader, quiet bo
203246
return types.ImageLoadResponse{}, fmt.Errorf("reading tar")
204247
}
205248

206-
f.nextImageID++
207-
imageID := fmt.Sprintf("sha256:%d", f.nextImageID)
249+
next := atomic.AddInt32(&f.nextImageID, 1)
250+
imageID := fmt.Sprintf("sha256:%d", next)
208251
f.Add(ref, imageID)
209252

210253
return types.ImageLoadResponse{

0 commit comments

Comments
 (0)