From 830dbef9b7addc40c6401f319a5fa06c942de16e Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Wed, 24 Jan 2018 18:58:25 -0800 Subject: [PATCH] Use docker error message to negotiate api version as a fallback (#99) * Use docker error message to negotiate api version as a fallback * Add comments describing regex * Use regex.MustCompile * Hoist regex outside function * Flatten logic * Update changelog --- CHANGELOG | 3 +- pkg/plugins/docker/producers/docker.go | 38 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 928ad9ab2..ea4ef87af 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -44,5 +44,6 @@ * Updated docker client library to latest * Docker client now negotiates a version -0.10.1 /2018-01-08 +0.10.1 /2018-01-24 * Patch CVE-2017-17512 sensible-utils-0.0.9 + * Added fallback logic for Docker client version negotiation with Docker server API versions < 1.24 diff --git a/pkg/plugins/docker/producers/docker.go b/pkg/plugins/docker/producers/docker.go index 308466eb2..31de5c3f2 100644 --- a/pkg/plugins/docker/producers/docker.go +++ b/pkg/plugins/docker/producers/docker.go @@ -3,6 +3,8 @@ package producers import ( "context" "errors" + "log" + "regexp" "github.com/docker/docker/api/types" docker "github.com/docker/docker/client" @@ -12,8 +14,44 @@ type Docker struct { client *docker.Client } +// this matches server version within error strings like this: +// `Error response from daemon: client is newer than server (client API version: 1.24, server API version: 1.19)` +var dockerErrorVersionRegexp *regexp.Regexp = regexp.MustCompile(`server API version:\s*(\d\.\d+)\s*\)`) + func New(client *docker.Client) *Docker { client.NegotiateAPIVersion(context.Background()) + + if client.ClientVersion() != "1.24" { + return &Docker{client} + } + + // there is a possibility that negotiation failed as this is the default value for that case + // so we send a ping and check ourselves + ping, _ := client.Ping(context.Background()) + if ping.APIVersion != "" { + return &Docker{client} + } + + // negotiation failed, so we get to fake it + log.Printf("Docker API version negotiation failed. Attempting fallback...") + _, err := client.ServerVersion(context.Background()) + + if err == nil { + // ironically, this is actually a bit of a failure + return &Docker{client} + } + + matches := dockerErrorVersionRegexp.FindStringSubmatch(err.Error()) + + if len(matches) < 2 { + log.Printf("Docker API version negotiation fallback failed") + } else { + log.Printf("Fallback API version detection: %+v", matches[1]) + var fakePing types.Ping + fakePing.APIVersion = matches[1] + client.NegotiateAPIVersionPing(fakePing) + } + return &Docker{client} }