Skip to content

Commit bd0bf10

Browse files
author
OpenShift Bot
authored
Merge pull request #13380 from guangxuli/fix_docker_pull_fail
Merged by openshift-bot
2 parents 65a0cbf + ca07fa9 commit bd0bf10

File tree

2 files changed

+122
-39
lines changed

2 files changed

+122
-39
lines changed

pkg/build/builder/dockerutil.go

+50-31
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"io/ioutil"
1010
"os"
11+
"reflect"
1112
"strings"
1213
"time"
1314

@@ -21,11 +22,11 @@ import (
2122
)
2223

2324
var (
24-
// DefaultPushRetryCount is the number of retries of pushing the built Docker image
25+
// DefaultPushOrPullRetryCount is the number of retries of pushing or pulling the built Docker image
2526
// into a configured repository
26-
DefaultPushRetryCount = 6
27-
// DefaultPushRetryDelay is the time to wait before triggering a push retry
28-
DefaultPushRetryDelay = 5 * time.Second
27+
DefaultPushOrPullRetryCount = 6
28+
// DefaultPushOrPullRetryDelay is the time to wait before triggering a push or pull retry
29+
DefaultPushOrPullRetryDelay = 5 * time.Second
2930
// RetriableErrors is a set of strings that indicate that an retriable error occurred.
3031
RetriableErrors = []string{
3132
"ping attempt failed with error",
@@ -54,6 +55,47 @@ type DockerClient interface {
5455
TagImage(name string, opts docker.TagImageOptions) error
5556
}
5657

58+
func RetryImageAction(client DockerClient, opts interface{}, authConfig docker.AuthConfiguration) error {
59+
var err error
60+
var retriableError = false
61+
var actionName string
62+
63+
pullOpt := docker.PullImageOptions{}
64+
pushOpt := docker.PushImageOptions{}
65+
66+
for retries := 0; retries <= DefaultPushOrPullRetryCount; retries++ {
67+
if reflect.TypeOf(opts) == reflect.TypeOf(pullOpt) {
68+
actionName = "Pull"
69+
err = client.PullImage(opts.(docker.PullImageOptions), authConfig)
70+
} else if reflect.TypeOf(opts) == reflect.TypeOf(pushOpt) {
71+
actionName = "Push"
72+
err = client.PushImage(opts.(docker.PushImageOptions), authConfig)
73+
} else {
74+
return errors.New("not match Pull or Push action")
75+
}
76+
77+
if err == nil {
78+
return nil
79+
}
80+
81+
errMsg := fmt.Sprintf("%s", err)
82+
for _, errorString := range RetriableErrors {
83+
if strings.Contains(errMsg, errorString) {
84+
retriableError = true
85+
break
86+
}
87+
}
88+
if !retriableError {
89+
return err
90+
}
91+
92+
glog.V(0).Infof("Warning: %s failed, retrying in %s ...", actionName, DefaultPushOrPullRetryDelay)
93+
time.Sleep(DefaultPushOrPullRetryDelay)
94+
}
95+
96+
return fmt.Errorf("After retrying %d times, %s image still failed", DefaultPushOrPullRetryDelay, actionName)
97+
}
98+
5799
func pullImage(client DockerClient, name string, authConfig docker.AuthConfiguration) error {
58100
logProgress := func(s string) {
59101
glog.V(0).Infof("%s", s)
@@ -67,11 +109,7 @@ func pullImage(client DockerClient, name string, authConfig docker.AuthConfigura
67109
opts.OutputStream = os.Stderr
68110
opts.RawJSONStream = false
69111
}
70-
err := client.PullImage(opts, authConfig)
71-
if err == nil {
72-
return nil
73-
}
74-
return err
112+
return RetryImageAction(client, opts, authConfig)
75113
}
76114

77115
// pushImage pushes a docker image to the registry specified in its tag.
@@ -102,30 +140,11 @@ func pushImage(client DockerClient, name string, authConfig docker.AuthConfigura
102140
OutputStream: io.MultiWriter(progressWriter, digestWriter),
103141
RawJSONStream: true,
104142
}
105-
var err error
106-
var retriableError = false
107-
108-
for retries := 0; retries <= DefaultPushRetryCount; retries++ {
109-
err = client.PushImage(opts, authConfig)
110-
if err == nil {
111-
return digestWriter.Digest, nil
112-
}
113-
114-
errMsg := fmt.Sprintf("%s", err)
115-
for _, errorString := range RetriableErrors {
116-
if strings.Contains(errMsg, errorString) {
117-
retriableError = true
118-
break
119-
}
120-
}
121-
if !retriableError {
122-
return "", err
123-
}
124143

125-
glog.V(0).Infof("Warning: Push failed, retrying in %s ...", DefaultPushRetryDelay)
126-
time.Sleep(DefaultPushRetryDelay)
144+
if err := RetryImageAction(client, opts, authConfig); err != nil {
145+
return "", err
127146
}
128-
return "", err
147+
return digestWriter.Digest, nil
129148
}
130149

131150
func removeImage(client DockerClient, name string) error {

pkg/build/builder/dockerutil_test.go

+72-8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
type FakeDocker struct {
1919
pushImageFunc func(opts docker.PushImageOptions, auth docker.AuthConfiguration) error
20+
pullImageFunc func(opts docker.PullImageOptions, auth docker.AuthConfiguration) error
2021
buildImageFunc func(opts docker.BuildImageOptions) error
2122
removeImageFunc func(name string) error
2223

@@ -51,6 +52,20 @@ func fakePushImageFunc(opts docker.PushImageOptions, auth docker.AuthConfigurati
5152
}
5253
return nil
5354
}
55+
56+
func fakePullImageFunc(opts docker.PullImageOptions, auth docker.AuthConfiguration) error {
57+
switch opts.Repository {
58+
case "repo_test_succ_foo_bar":
59+
return nil
60+
case "repo_test_err_exist_foo_bar":
61+
fooBarRunTimes++
62+
return errors.New(RetriableErrors[0])
63+
case "repo_test_err_no_exist_foo_bar":
64+
return errors.New("no_exist_err_foo_bar")
65+
}
66+
return nil
67+
}
68+
5469
func (d *FakeDocker) BuildImage(opts docker.BuildImageOptions) error {
5570
if d.buildImageFunc != nil {
5671
return d.buildImageFunc(opts)
@@ -77,6 +92,9 @@ func (d *FakeDocker) DownloadFromContainer(id string, opts docker.DownloadFromCo
7792
return nil
7893
}
7994
func (d *FakeDocker) PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error {
95+
if d.pullImageFunc != nil {
96+
return d.pullImageFunc(opts, auth)
97+
}
8098
return nil
8199
}
82100
func (d *FakeDocker) RemoveContainer(opts docker.RemoveContainerOptions) error {
@@ -141,23 +159,23 @@ func TestTagImage(t *testing.T) {
141159
func TestPushImage(t *testing.T) {
142160
var testImageName string
143161

144-
bakRetryCount := DefaultPushRetryCount
145-
bakRetryDelay := DefaultPushRetryDelay
162+
bakRetryCount := DefaultPushOrPullRetryCount
163+
bakRetryDelay := DefaultPushOrPullRetryDelay
146164

147165
fakeDocker := NewFakeDockerClient()
148166
fakeDocker.pushImageFunc = fakePushImageFunc
149167
testAuth := docker.AuthConfiguration{
150-
Username: "usernname_foo_bar",
168+
Username: "username_foo_bar",
151169
Password: "password_foo_bar",
152170
Email: "email_foo_bar",
153171
ServerAddress: "serveraddress_foo_bar",
154172
}
155173

156174
//make test quickly, and recover the value after testing
157-
DefaultPushRetryCount = 2
158-
defer func() { DefaultPushRetryCount = bakRetryCount }()
159-
DefaultPushRetryDelay = 1
160-
defer func() { DefaultPushRetryDelay = bakRetryDelay }()
175+
DefaultPushOrPullRetryCount = 2
176+
defer func() { DefaultPushOrPullRetryCount = bakRetryCount }()
177+
DefaultPushOrPullRetryDelay = 1
178+
defer func() { DefaultPushOrPullRetryDelay = bakRetryDelay }()
161179

162180
//expect succ
163181
testImageName = "repo_foo_bar:tag_test_succ_foo_bar"
@@ -172,7 +190,7 @@ func TestPushImage(t *testing.T) {
172190
t.Errorf("Unexpect push image : %v, want error", err)
173191
}
174192
//expect run 3 times
175-
if fooBarRunTimes != (DefaultPushRetryCount + 1) {
193+
if fooBarRunTimes != (DefaultPushOrPullRetryCount + 1) {
176194
t.Errorf("Unexpect run times : %d, we expect run three times", fooBarRunTimes)
177195
}
178196

@@ -184,6 +202,52 @@ func TestPushImage(t *testing.T) {
184202
defer func() { fooBarRunTimes = 0 }()
185203
}
186204

205+
func TestPullImage(t *testing.T) {
206+
var testImageName string
207+
208+
bakRetryCount := DefaultPushOrPullRetryCount
209+
bakRetryDelay := DefaultPushOrPullRetryDelay
210+
211+
fakeDocker := NewFakeDockerClient()
212+
fakeDocker.pullImageFunc = fakePullImageFunc
213+
testAuth := docker.AuthConfiguration{
214+
Username: "username_foo_bar",
215+
Password: "password_foo_bar",
216+
Email: "email_foo_bar",
217+
ServerAddress: "serveraddress_foo_bar",
218+
}
219+
220+
//make test quickly, and recover the value after testing
221+
DefaultPushOrPullRetryCount = 2
222+
defer func() { DefaultPushOrPullRetryCount = bakRetryCount }()
223+
DefaultPushOrPullRetryDelay = 1
224+
defer func() { DefaultPushOrPullRetryDelay = bakRetryDelay }()
225+
226+
//expect succ
227+
testImageName = "repo_test_succ_foo_bar"
228+
if err := pullImage(fakeDocker, testImageName, testAuth); err != nil {
229+
t.Errorf("Unexpect pull image : %v, want succ", err)
230+
}
231+
232+
//expect fail
233+
testImageName = "repo_test_err_exist_foo_bar"
234+
err := pullImage(fakeDocker, testImageName, testAuth)
235+
if err == nil {
236+
t.Errorf("Unexpect pull image : %v, want error", err)
237+
}
238+
//expect run 3 times
239+
if fooBarRunTimes != (DefaultPushOrPullRetryCount + 1) {
240+
t.Errorf("Unexpect run times : %d, we expect run three times", fooBarRunTimes)
241+
}
242+
243+
//expect fail
244+
testImageName = "repo_test_err_no_exist_foo_bar"
245+
if err := pullImage(fakeDocker, testImageName, testAuth); err == nil {
246+
t.Errorf("Unexpect pull image : %v, want error", err)
247+
}
248+
defer func() { fooBarRunTimes = 0 }()
249+
}
250+
187251
func TestGetContainerNameOrID(t *testing.T) {
188252
c := &docker.Container{}
189253
c.ID = "ID"

0 commit comments

Comments
 (0)