Verified Commit f26c698f authored by Tomasz Maczukin's avatar Tomasz Maczukin

Merge branch '1606-use-docker-docker-client' into 'master'

Change dependency from `github.com/fsouza/go-dockerclient` to `github.com/docker/docker/client`

See merge request !301
parents a0b870ef 4b1a7746
......@@ -13,9 +13,11 @@ before_script:
.docker: &docker
before_script:
- source ci/prepare
- wget -q -O /usr/bin/docker https://get.docker.com/builds/Linux/x86_64/docker-1.10.3
- chmod +x /usr/bin/docker
services: ["docker:1.12.4-dind"]
- wget -q https://get.docker.com/builds/Linux/x86_64/docker-1.13.1.tgz -O /tmp/docker.tar.gz
- tar -xzf /tmp/docker.tar.gz -C /tmp/
- cp /tmp/docker/docker* /usr/bin
- chmod +x /usr/bin/docker*
services: ["docker:dind"]
variables:
DOCKER_DRIVER: overlay
DOCKER_HOST: tcp://docker:2375
......
This diff is collapsed.
This diff is collapsed.
......@@ -4,15 +4,15 @@ import (
"bytes"
"errors"
"github.com/fsouza/go-dockerclient"
"github.com/docker/docker/api/types"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors"
)
type commandExecutor struct {
executor
predefinedContainer *docker.Container
buildContainer *docker.Container
predefinedContainer *types.ContainerJSON
buildContainer *types.ContainerJSON
}
func (s *commandExecutor) Prepare(globalConfig *common.Config, config *common.RunnerConfig, build *common.Build) error {
......@@ -52,17 +52,17 @@ func (s *commandExecutor) Prepare(globalConfig *common.Config, config *common.Ru
}
func (s *commandExecutor) Run(cmd common.ExecutorCommand) error {
var container *docker.Container
var runOn *types.ContainerJSON
if cmd.Predefined {
container = s.predefinedContainer
runOn = s.predefinedContainer
} else {
container = s.buildContainer
runOn = s.buildContainer
}
s.Debugln("Executing on", container.Name, "the", cmd.Script)
s.Debugln("Executing on", runOn.Name, "the", cmd.Script)
return s.watchContainer(container, bytes.NewBufferString(cmd.Script), cmd.Abort)
return s.watchContainer(runOn.ID, bytes.NewBufferString(cmd.Script), cmd.Abort)
}
func init() {
......
......@@ -15,6 +15,8 @@ import (
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors/docker"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/helpers"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/helpers/docker"
"golang.org/x/net/context"
)
func TestDockerCommandSuccessRun(t *testing.T) {
......@@ -83,7 +85,7 @@ func TestDockerCommandMissingImage(t *testing.T) {
err := build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
require.Error(t, err)
assert.IsType(t, err, &common.BuildError{})
assert.IsType(t, &common.BuildError{}, err)
assert.Contains(t, err.Error(), "not found")
}
......@@ -105,7 +107,7 @@ func TestDockerCommandMissingTag(t *testing.T) {
err := build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
require.Error(t, err)
assert.IsType(t, err, &common.BuildError{})
assert.IsType(t, &common.BuildError{}, err)
assert.Contains(t, err.Error(), "not found")
}
......@@ -268,7 +270,7 @@ func waitForDocker(credentials docker_helpers.DockerCredentials) error {
}
for i := 0; i < 20; i++ {
_, err = client.Info()
_, err = client.Info(context.TODO())
if err == nil {
break
}
......
......@@ -3,9 +3,13 @@ package docker
import (
"errors"
"github.com/docker/docker/api/types"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/helpers/ssh"
"golang.org/x/net/context"
)
type sshExecutor struct {
......@@ -37,12 +41,12 @@ func (s *sshExecutor) Prepare(globalConfig *common.Config, config *common.Runner
}
s.Debugln("Starting container", container.ID, "...")
err = s.client.StartContainer(container.ID, nil)
err = s.client.ContainerStart(context.TODO(), container.ID, types.ContainerStartOptions{})
if err != nil {
return err
}
containerData, err := s.client.InspectContainer(container.ID)
containerData, err := s.client.ContainerInspect(context.TODO(), container.ID)
if err != nil {
return err
}
......
This diff is collapsed.
package docker_helpers
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
......@@ -8,13 +11,25 @@ import (
"path"
"strings"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cliconfig/configfile"
"github.com/docker/docker/pkg/homedir"
"github.com/fsouza/go-dockerclient"
)
// DefaultDockerRegistry is the name of the index
const DefaultDockerRegistry = "docker.io"
// EncodeAuthConfig constructs a token from an AuthConfig, suitable for
// authorizing against the Docker API with.
func EncodeAuthConfig(authConfig *types.AuthConfig) (string, error) {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(authConfig); err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(buf.Bytes()), nil
}
// SplitDockerImageName breaks a reposName into an index name and remote name
func SplitDockerImageName(reposName string) (string, string) {
nameParts := strings.SplitN(reposName, "/", 2)
......@@ -38,10 +53,9 @@ func SplitDockerImageName(reposName string) (string, string) {
var HomeDirectory = homedir.Get()
func ReadDockerAuthConfigsFromHomeDir(userName string) (_ *docker.AuthConfigurations, err error) {
var r io.ReadCloser
func ReadDockerAuthConfigsFromHomeDir(userName string) (map[string]types.AuthConfig, error) {
homeDir := HomeDirectory
if userName != "" {
u, err := user.Lookup(userName)
if err != nil {
......@@ -49,33 +63,43 @@ func ReadDockerAuthConfigsFromHomeDir(userName string) (_ *docker.AuthConfigurat
}
homeDir = u.HomeDir
}
if homeDir == "" {
err = fmt.Errorf("Failed to get home directory")
return
return nil, fmt.Errorf("Failed to get home directory")
}
p := path.Join(homeDir, ".docker", "config.json")
r, err = os.Open(p)
r, err := os.Open(p)
defer r.Close()
if err != nil {
p := path.Join(homeDir, ".dockercfg")
r, err = os.Open(p)
if os.IsNotExist(err) {
// Ignore does not exist errors
err = nil
}
if err != nil {
if err != nil && !os.IsNotExist(err) {
return nil, err
}
}
if r != nil {
defer r.Close()
if r == nil {
return make(map[string]types.AuthConfig), nil
}
return ReadAuthConfigsFromReader(r)
}
func ReadAuthConfigsFromReader(r io.Reader) (map[string]types.AuthConfig, error) {
config := &configfile.ConfigFile{}
if err := config.LoadFromReader(r); err != nil {
return nil, err
}
return docker.NewAuthConfigurations(r)
return config.AuthConfigs, nil
}
// ResolveDockerAuthConfig taken from: https://github.com/docker/docker/blob/master/registry/auth.go
func ResolveDockerAuthConfig(indexName string, configs *docker.AuthConfigurations) *docker.AuthConfiguration {
func ResolveDockerAuthConfig(indexName string, configs map[string]types.AuthConfig) *types.AuthConfig {
if configs == nil {
return nil
}
......@@ -97,7 +121,7 @@ func ResolveDockerAuthConfig(indexName string, configs *docker.AuthConfiguration
// Maybe they have a legacy config file, we will iterate the keys converting
// them to the new format and testing
for registry, authConfig := range configs.Configs {
for registry, authConfig := range configs {
if indexName == convertToHostname(registry) {
return &authConfig
}
......
package docker_helpers
import "github.com/fsouza/go-dockerclient"
import (
"io"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"golang.org/x/net/context"
)
type Client interface {
InspectImage(name string) (*docker.Image, error)
PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error
ImportImage(opts docker.ImportImageOptions) error
CreateContainer(opts docker.CreateContainerOptions) (*docker.Container, error)
StartContainer(id string, hostConfig *docker.HostConfig) error
KillContainer(opts docker.KillContainerOptions) error
InspectContainer(id string) (*docker.Container, error)
AttachToContainerNonBlocking(opts docker.AttachToContainerOptions) (docker.CloseWaiter, error)
RemoveContainer(opts docker.RemoveContainerOptions) error
DisconnectNetwork(id string, opts docker.NetworkConnectionOptions) error
ListNetworks() ([]docker.Network, error)
Logs(opts docker.LogsOptions) error
Info() (*docker.Env, error)
ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error)
ImagePullBlocking(ctx context.Context, ref string, options types.ImagePullOptions) error
ImageImportBlocking(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) error
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error)
ContainerStart(ctx context.Context, containerID string, options types.ContainerStartOptions) error
ContainerWait(ctx context.Context, containerID string) (int64, error)
ContainerKill(ctx context.Context, containerID, signal string) error
ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
ContainerRemove(ctx context.Context, containerID string, options types.ContainerRemoveOptions) error
ContainerLogs(ctx context.Context, container string, options types.ContainerLogsOptions) (io.ReadCloser, error)
NetworkDisconnect(ctx context.Context, networkID, containerID string, force bool) error
NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
Info(ctx context.Context) (types.Info, error)
io.Closer
}
package docker_helpers
import (
"fmt"
"net/url"
"sync"
)
type clientCache struct {
lock sync.RWMutex
clients map[string]Client
}
func (c *clientCache) isCacheable(endpoint string) bool {
u, err := url.Parse(endpoint)
if err != nil {
return false
}
return u.Scheme == "unix"
}
func (c *clientCache) fromCache(endpoint string, params ...interface{}) Client {
if !c.isCacheable(endpoint) {
return nil
}
c.lock.RLock()
defer c.lock.RUnlock()
key := endpoint + fmt.Sprintln(params...)
return c.clients[key]
}
func (c *clientCache) cache(client Client, endpoint string, params ...interface{}) bool {
if !c.isCacheable(endpoint) {
return false
}
c.lock.Lock()
defer c.lock.Unlock()
key := endpoint + fmt.Sprintln(params...)
c.clients[key] = client
return true
}
package docker_helpers
import (
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"time"
"github.com/Sirupsen/logrus"
"github.com/fsouza/go-dockerclient"
)
var dockerDialer = &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
var cache = clientCache{
clients: make(map[string]Client),
}
func httpTransportFix(host string, client Client) {
dockerClient, ok := client.(*docker.Client)
if !ok || dockerClient == nil {
return
}
logrus.WithField("host", host).Debugln("Applying docker.Client transport fix:", dockerClient)
dockerClient.Dialer = dockerDialer
dockerClient.HTTPClient = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: dockerDialer.Dial,
TLSClientConfig: dockerClient.TLSConfig,
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 30 * time.Second,
ExpectContinueTimeout: 30 * time.Second,
IdleConnTimeout: 5 * time.Minute,
},
}
}
func New(c DockerCredentials, apiVersion string) (client Client, err error) {
endpoint := "unix:///var/run/docker.sock"
tlsVerify := false
tlsCertPath := ""
defer func() {
if client != nil {
httpTransportFix(endpoint, client)