From 9e1ab88192808410f88438bb0c45d743e82ae478 Mon Sep 17 00:00:00 2001 From: timfeirg Date: Wed, 10 Jan 2018 13:40:30 +0800 Subject: [PATCH] support registry authentication --- cluster/calcium/build_image.go | 18 +++++++++++----- cluster/calcium/helper.go | 38 ++++++++++++++++++++++++++++++++++ cluster/calcium/helper_test.go | 11 ++++++++++ cluster/calcium/mock_test.go | 8 ++++++- glide.lock | 6 ++++++ types/config.go | 29 ++++++++++++++++++-------- 6 files changed, 95 insertions(+), 15 deletions(-) diff --git a/cluster/calcium/build_image.go b/cluster/calcium/build_image.go index ee99c5c1d..3fc3d8642 100644 --- a/cluster/calcium/build_image.go +++ b/cluster/calcium/build_image.go @@ -171,7 +171,9 @@ func (c *calcium) BuildImage(ctx context.Context, opts *types.BuildOptions) (cha lastMessage.Error = err.Error() break } - log.Errorf("[BuildImage] Decode build image message failed %v", err) + malformed := []byte{} + _, _ = decoder.Buffered().Read(malformed) + log.Errorf("[BuildImage] Decode build image message failed %v, buffered: %v", err, malformed) return } ch <- message @@ -182,9 +184,13 @@ func (c *calcium) BuildImage(ctx context.Context, opts *types.BuildOptions) (cha log.Errorf("[BuildImage] Build image failed %v", lastMessage.ErrorDetail.Message) return } - // About this "Khadgar", https://github.com/docker/docker/issues/10983#issuecomment-85892396 - // Just because Ben Schnetzer's cute Khadgar... - rc, err := node.Engine.ImagePush(ctx, tag, enginetypes.ImagePushOptions{RegistryAuth: "Khadgar"}) + encodedAuth, err := c.MakeEncodedAuthConfigFromRemote(tag) + if err != nil { + ch <- makeErrorBuildImageMessage(err) + return + } + pushOptions := enginetypes.ImagePushOptions{RegistryAuth: encodedAuth} + rc, err := node.Engine.ImagePush(ctx, tag, pushOptions) if err != nil { ch <- makeErrorBuildImageMessage(err) return @@ -199,7 +205,9 @@ func (c *calcium) BuildImage(ctx context.Context, opts *types.BuildOptions) (cha if err == io.EOF { break } - log.Errorf("[BuildImage] Decode push image message failed %v", err) + malformed := []byte{} + _, _ = decoder2.Buffered().Read(malformed) + log.Errorf("[BuildImage] Decode push image message failed %v, buffered: %v", err, malformed) return } ch <- message diff --git a/cluster/calcium/helper.go b/cluster/calcium/helper.go index 159d96908..8961d0418 100644 --- a/cluster/calcium/helper.go +++ b/cluster/calcium/helper.go @@ -1,15 +1,19 @@ package calcium import ( + "encoding/base64" + "encoding/json" "fmt" "io" "io/ioutil" "os" "strings" + "github.com/docker/distribution/reference" enginetypes "github.com/docker/docker/api/types" enginecontainer "github.com/docker/docker/api/types/container" engineapi "github.com/docker/docker/client" + "github.com/docker/docker/registry" "github.com/projecteru2/core/lock" "github.com/projecteru2/core/types" "github.com/projecteru2/core/utils" @@ -165,3 +169,37 @@ func execuateInside(client *engineapi.Client, ID, cmd, user string, env []string } return b, nil } + +// MakeAuthConfigFromRemote Calculate encoded AuthConfig from registry and eru-core config +// See https://github.com/docker/cli/blob/16cccc30f95c8163f0749eba5a2e80b807041342/cli/command/registry.go#L67 +func (c *calcium) MakeEncodedAuthConfigFromRemote(remote string) (string, error) { + ref, err := reference.ParseNormalizedNamed(remote) + if err != nil { + return "", err + } + + // Resolve the Repository name from fqn to RepositoryInfo + repoInfo, err := registry.ParseRepositoryInfo(ref) + if err != nil { + return "", err + } + + serverAddress := repoInfo.Index.Name + if authConfig, exists := c.config.Docker.AuthConfigs[serverAddress]; exists { + if encodedAuth, err := EncodeAuthToBase64(authConfig); err == nil { + return encodedAuth, nil + } + return "", err + } + return "dummy", nil +} + +// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload +// See https://github.com/docker/cli/blob/master/cli/command/registry.go#L41 +func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) { + buf, err := json.Marshal(authConfig) + if err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(buf), nil +} diff --git a/cluster/calcium/helper_test.go b/cluster/calcium/helper_test.go index 3b5fefaf7..9c6f2de2a 100644 --- a/cluster/calcium/helper_test.go +++ b/cluster/calcium/helper_test.go @@ -1,6 +1,8 @@ package calcium import ( + "encoding/base64" + "encoding/json" "testing" "github.com/projecteru2/core/types" @@ -17,3 +19,12 @@ func TestMakeMountPaths(t *testing.T) { assert.Equal(t, binds, []string{"/foo-data:/foo-data:rw", "/proc/sys:/writable-proc/sys:rw", "/sys/kernel/mm/transparent_hugepage:/writable-sys/kernel/mm/transparent_hugepage:rw"}) assert.Equal(t, volumes, map[string]struct{}{"/foo-data": struct{}{}, "/writable-proc/sys": struct{}{}, "/writable-sys/kernel/mm/transparent_hugepage": struct{}{}}) } + +func TestRegistryAuth(t *testing.T) { + tag := "docker.io/projecteru2/core" + encodedAuth, _ := mockc.MakeEncodedAuthConfigFromRemote(tag) + decodedAuth, _ := base64.StdEncoding.DecodeString(encodedAuth) + authConfig := types.AuthConfig{} + json.Unmarshal(decodedAuth, &authConfig) + assert.Equal(t, authConfig.Password, mockc.config.Docker.AuthConfigs["docker.io"].Password) +} diff --git a/cluster/calcium/mock_test.go b/cluster/calcium/mock_test.go index de74c51af..1637fd05f 100644 --- a/cluster/calcium/mock_test.go +++ b/cluster/calcium/mock_test.go @@ -60,8 +60,14 @@ var ( }, Git: coretypes.GitConfig{SCMType: "gitlab"}, Docker: coretypes.DockerConfig{ - Hub: "hub.testhub.com", + Hub: "docker.io", Namespace: "apps", + AuthConfigs: map[string]coretypes.AuthConfig{ + "docker.io": coretypes.AuthConfig{ + Username: "timfeirg", + Password: "secret", + }, + }, }, Scheduler: coretypes.SchedConfig{ ShareBase: 10, diff --git a/glide.lock b/glide.lock index 2c0b1bd9f..87c2ebe28 100644 --- a/glide.lock +++ b/glide.lock @@ -67,6 +67,12 @@ imports: - pkg/stdcopy - pkg/system - pkg/tlsconfig +- name: github.com/spf13/pflag + version: 2300d0f8576fe575f71aaa5b9bbe4e1b0dc2eb51 +- name: github.com/gorilla/mux + version: 5bbbb5b2b5729b132181cc7f4aa3b3c973e9a0ed +- name: github.com/Nvveen/Gotty + version: cd527374f1e5bff4938207604a14f2e38a9cf512 - name: github.com/docker/go-connections version: 3ede32e2033de7505e6500d6c868c2b9ed9f169d subpackages: diff --git a/types/config.go b/types/config.go index d45cb4f17..7ef53234a 100644 --- a/types/config.go +++ b/types/config.go @@ -1,6 +1,8 @@ package types -import "time" +import ( + "time" +) // Config holds eru-core config type Config struct { @@ -37,14 +39,15 @@ type GitConfig struct { // DockerConfig holds eru-core docker config type DockerConfig struct { - APIVersion string `yaml:"version"` // docker API version - LogDriver string `yaml:"log_driver"` // docker log driver, can be "json-file", "none" - NetworkMode string `yaml:"network_mode"` // docker network mode - CertPath string `yaml:"cert_path"` // docker cert files path - Hub string `yaml:"hub"` // docker hub address - Namespace string `yaml:"namespace"` // docker hub prefix, will be set to $Hub/$HubPrefix/$appname - BuildPod string `yaml:"build_pod"` // podname used to build - UseLocalDNS bool `yaml:"local_dns"` // use node IP as dns + APIVersion string `yaml:"version"` // docker API version + LogDriver string `yaml:"log_driver"` // docker log driver, can be "json-file", "none" + NetworkMode string `yaml:"network_mode"` // docker network mode + CertPath string `yaml:"cert_path"` // docker cert files path + Hub string `yaml:"hub"` // docker hub address + Namespace string `yaml:"namespace"` // docker hub prefix, will be set to $Hub/$HubPrefix/$appname + BuildPod string `yaml:"build_pod"` // podname used to build + UseLocalDNS bool `yaml:"local_dns"` // use node IP as dns + AuthConfigs map[string]AuthConfig `yaml:"auths"` // docker registry credentials } // SchedConfig holds scheduler config @@ -59,3 +62,11 @@ type SyslogConfig struct { Facility string `yaml:"facility"` Format string `yaml:"format"` } + +// AuthConfig contains authorization information for connecting to a Registry +// Basically copied from https://github.com/moby/moby/blob/16a1736b9b93e44c898f95d670bbaf20a558103d/api/types/auth.go#L4 +// But use yaml instead of json +type AuthConfig struct { + Username string `yaml:"username,omitempty" json:"username,omitempty"` + Password string `yaml:"password,omitempty" json:"password,omitempty"` +}