forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dockerutil.go
110 lines (99 loc) · 3.08 KB
/
dockerutil.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package builder
import (
"fmt"
"io"
"os"
"strings"
"time"
"k8s.io/kubernetes/pkg/util"
docker "github.com/fsouza/go-dockerclient"
"github.com/golang/glog"
"github.com/openshift/source-to-image/pkg/tar"
)
var (
// DefaultPushRetryCount is the number of retries of pushing the built Docker image
// into a configured repository
DefaultPushRetryCount = 6
// DefaultPushRetryDelay is the time to wait before triggering a push retry
DefaultPushRetryDelay = 5 * time.Second
// RetriableErrors is a set of strings that indicate that an retriable error occurred.
RetriableErrors = []string{
"ping attempt failed with error",
"is already in progress",
"connection reset by peer",
"transport closed before response was received",
}
)
// DockerClient is an interface to the Docker client that contains
// the methods used by the common builder
type DockerClient interface {
BuildImage(opts docker.BuildImageOptions) error
PushImage(opts docker.PushImageOptions, auth docker.AuthConfiguration) error
RemoveImage(name string) error
}
// pushImage pushes a docker image to the registry specified in its tag.
// The method will retry to push the image when following scenarios occur:
// - Docker registry is down temporarily or permanently
// - other image is being pushed to the registry
// If any other scenario the push will fail, without retries.
func pushImage(client DockerClient, name string, authConfig docker.AuthConfiguration) error {
repository, tag := docker.ParseRepositoryTag(name)
opts := docker.PushImageOptions{
Name: repository,
Tag: tag,
}
if glog.V(5) {
opts.OutputStream = os.Stderr
}
var err error
var retriableError = false
for retries := 0; retries <= DefaultPushRetryCount; retries++ {
err = client.PushImage(opts, authConfig)
if err == nil {
return nil
}
errMsg := fmt.Sprintf("%s", err)
for _, errorString := range RetriableErrors {
if strings.Contains(errMsg, errorString) {
retriableError = true
break
}
}
if !retriableError {
return err
}
util.HandleError(fmt.Errorf("push for image %s failed, will retry in %s seconds ...", name, DefaultPushRetryDelay))
glog.Flush()
time.Sleep(DefaultPushRetryDelay)
}
return err
}
func removeImage(client DockerClient, name string) error {
return client.RemoveImage(name)
}
// buildImage invokes a docker build on a particular directory
func buildImage(client DockerClient, dir string, noCache bool, tag string, tar tar.Tar, pullAuth *docker.AuthConfigurations, forcePull bool) error {
// TODO: be able to pass a stream directly to the Docker build to avoid the double temp hit
r, w := io.Pipe()
go func() {
defer util.HandleCrash()
defer w.Close()
if err := tar.CreateTarStream(dir, false, w); err != nil {
w.CloseWithError(err)
}
}()
defer w.Close()
glog.V(5).Infof("Invoking Docker build to create %q", tag)
opts := docker.BuildImageOptions{
Name: tag,
RmTmpContainer: true,
OutputStream: os.Stdout,
InputStream: r,
NoCache: noCache,
Pull: forcePull,
}
if pullAuth != nil {
opts.AuthConfigs = *pullAuth
}
return client.BuildImage(opts)
}