From 2215d244c79c6eab55f7a68f7bce554533f486b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20P=C3=A9rez-Aradros=20Herce?= Date: Wed, 11 Jul 2018 10:15:13 +0200 Subject: [PATCH] Bring back support for really old Docker versions (#7553) Introducing API version negotiation (#7165) brough support for new Docker versions, but broke support for versions in EOL. This change puts old API version (1.229 back on the default fallback, plus introduces version hardcoding through `DOCKER_API_VERSION` environment variable. I added some integration tests to check things are working. Fixes #7542 --- CHANGELOG.asciidoc | 1 + libbeat/common/docker/client.go | 39 +++++++++-------- libbeat/common/docker/client_test.go | 62 ++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 libbeat/common/docker/client_test.go diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 872dc690fc61..c1f24f9b37b9 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -77,6 +77,7 @@ https://github.com/elastic/beats/compare/v6.2.3...master[Check the HEAD diff] - When we fail to build a Kubernetes' indexer or matcher we produce a warning but we don't add them to the execution. {pull}7466[7466] - Fix default value for logging.files.keepfiles. It was being set to 0 and now it's set to the documented value of 7. {issue}7494[7494] +- Retain compatibility with older Docker server versions. {issue}7542[7542] *Auditbeat* diff --git a/libbeat/common/docker/client.go b/libbeat/common/docker/client.go index 3470602e2ab9..61175b30503c 100644 --- a/libbeat/common/docker/client.go +++ b/libbeat/common/docker/client.go @@ -19,6 +19,7 @@ package docker import ( "net/http" + "os" "github.com/elastic/beats/libbeat/logp" @@ -28,31 +29,35 @@ import ( "golang.org/x/net/context" ) -// Select Docker API version -const dockerAPIVersion = api.DefaultVersion - // NewClient builds and returns a new Docker client -// It uses version 1.26 by default, and negotiates it with the server so it is downgraded if 1.26 is too high +// It uses version 1.30 by default, and negotiates it with the server so it is downgraded if 1.30 is too high func NewClient(host string, httpClient *http.Client, httpHeaders map[string]string) (*client.Client, error) { - c, err := client.NewClient(host, dockerAPIVersion, httpClient, nil) - if err != nil { - return c, err + version := os.Getenv("DOCKER_API_VERSION") + if version == "" { + version = api.DefaultVersion } - logp.Debug("docker", "Negotiating client version") - ping, err := c.Ping(context.Background()) + c, err := client.NewClient(host, version, httpClient, nil) if err != nil { - logp.Debug("docker", "Failed to perform ping: %s", err) + return c, err } - // try the latest version before versioning headers existed - if ping.APIVersion == "" { - ping.APIVersion = "1.24" - } + if os.Getenv("DOCKER_API_VERSION") == "" { + logp.Debug("docker", "Negotiating client version") + ping, err := c.Ping(context.Background()) + if err != nil { + logp.Debug("docker", "Failed to perform ping: %s", err) + } + + // try a really old version, before versioning headers existed + if ping.APIVersion == "" { + ping.APIVersion = "1.22" + } - // if server version is lower than the client version, downgrade - if versions.LessThan(ping.APIVersion, dockerAPIVersion) { - c.UpdateClientVersion(ping.APIVersion) + // if server version is lower than the client version, downgrade + if versions.LessThan(ping.APIVersion, version) { + c.UpdateClientVersion(ping.APIVersion) + } } logp.Debug("docker", "Client version set to %s", c.ClientVersion()) diff --git a/libbeat/common/docker/client_test.go b/libbeat/common/docker/client_test.go new file mode 100644 index 000000000000..a9a93a0f755c --- /dev/null +++ b/libbeat/common/docker/client_test.go @@ -0,0 +1,62 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration + +package docker + +import ( + "os" + "testing" + + "github.com/docker/docker/api" + "github.com/docker/docker/api/types" + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +func TestNewClient(t *testing.T) { + host := "unix:///var/run/docker.sock" + + client, err := NewClient(host, nil, nil) + assert.NoError(t, err) + assert.NotNil(t, client) + + _, err = client.ContainerList(context.Background(), types.ContainerListOptions{}) + assert.NoError(t, err) + + // This test only works on newer Docker versions (any supported one really) + switch client.ClientVersion() { + case "1.22": + t.Skip("Docker version is too old for this test") + case api.DefaultVersion: + t.Logf("Using default API version: %s", api.DefaultVersion) + default: + t.Logf("Negotiated version: %s", client.ClientVersion()) + } + + // Test we can hardcode version + os.Setenv("DOCKER_API_VERSION", "1.22") + + client, err = NewClient(host, nil, nil) + assert.NoError(t, err) + assert.NotNil(t, client) + assert.Equal(t, "1.22", client.ClientVersion()) + + _, err = client.ContainerList(context.Background(), types.ContainerListOptions{}) + assert.NoError(t, err) +}