Skip to content

Commit

Permalink
refactor: refactor docker auth and move to client pkg (#1386)
Browse files Browse the repository at this point in the history
  • Loading branch information
kakaZhou719 authored May 11, 2022
1 parent 6abf596 commit 8f856bf
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 162 deletions.
9 changes: 8 additions & 1 deletion pkg/checker/registry_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"context"
"fmt"

"github.com/sealerio/sealer/pkg/client/docker/auth"

"github.com/sealerio/sealer/common"
"github.com/sealerio/sealer/pkg/image/distributionutil"
v2 "github.com/sealerio/sealer/types/api/v2"
Expand All @@ -39,7 +41,12 @@ func (r *RegistryChecker) Check(cluster *v2.Cluster, phase string) error {
return fmt.Errorf("registry auth info not found,please run 'sealer login' first")
}
// try to log in with auth info
authConfig, err := utils.GetDockerAuthInfoFromDocker(r.RegistryDomain)
svc, err := auth.NewDockerAuthService()
if err != nil {
return fmt.Errorf("failed to read default auth file %s: %v", authFile, err)
}

authConfig, err := svc.GetAuthByDomain(r.RegistryDomain)
if err != nil {
return fmt.Errorf("failed to get auth info, err: %s", err)
}
Expand Down
128 changes: 128 additions & 0 deletions pkg/client/docker/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright © 2022 Alibaba Group Holding Ltd.
//
// Licensed 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.

package auth

import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"

"github.com/docker/docker/api/types"
"github.com/sealerio/sealer/common"
"github.com/sealerio/sealer/utils"
)

type Item struct {
Auth string `json:"auth"`
}

type DockerAuth struct {
Auths map[string]Item `json:"auths"`
}

func (d *DockerAuth) Get(domain string) (string, string, error) {
auth := d.Auths[domain].Auth
if auth == "" {
return "", "", fmt.Errorf("auth for %s doesn't exist", domain)
}

decode, err := base64.StdEncoding.DecodeString(auth)
if err != nil {
return "", "", err
}
i := bytes.IndexRune(decode, ':')

if i == -1 {
return "", "", fmt.Errorf("auth base64 has problem of format")
}

return string(decode[:i]), string(decode[i+1:]), nil
}

func (d *DockerAuth) Set(hostname, username, password string) {
authEncode := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
d.Auths[hostname] = Item{Auth: authEncode}
}

type DockerAuthService struct {
FilePath string
AuthContent DockerAuth `json:"auths"`
}

func (s *DockerAuthService) SetAuthInfo(hostname, username, password string) error {
if !utils.IsFileExist(s.FilePath) {
if err := os.MkdirAll(filepath.Dir(s.FilePath), common.FileMode0755); err != nil {
return err
}
}

s.AuthContent.Set(hostname, username, password)
data, err := json.MarshalIndent(s.AuthContent, "", "\t")
if err != nil {
return err
}

if err := ioutil.WriteFile(s.FilePath, data, common.FileMode0644); err != nil {
return fmt.Errorf("write %s failed,%s", s.FilePath, err)
}
return nil
}

func (s *DockerAuthService) GetAuthInfoByDomain(domain string) (string, string, error) {
return s.AuthContent.Get(domain)
}

func (s *DockerAuthService) GetAuthByDomain(domain string) (types.AuthConfig, error) {
defaultAuthConfig := types.AuthConfig{ServerAddress: domain}

user, passwd, err := s.AuthContent.Get(domain)
if err != nil {
return defaultAuthConfig, err
}

return types.AuthConfig{
Username: user,
Password: passwd,
ServerAddress: domain,
}, nil
}

func NewDockerAuthService() (DockerAuthService, error) {
var (
authFile = common.DefaultRegistryAuthConfigDir()
ac = DockerAuth{Auths: map[string]Item{}}
das = DockerAuthService{FilePath: authFile, AuthContent: ac}
)

if !utils.IsFileExist(authFile) {
return das, nil
}

content, err := ioutil.ReadFile(filepath.Clean(authFile))
if err != nil {
return das, err
}

err = json.Unmarshal(content, &ac)
if err != nil {
return das, err
}
das.AuthContent = ac
return das, nil
}
10 changes: 7 additions & 3 deletions pkg/client/docker/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import (
"encoding/base64"
"encoding/json"

"github.com/sealerio/sealer/pkg/client/docker/auth"

"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
dockerregistry "github.com/docker/docker/registry"

"github.com/sealerio/sealer/logger"
normalreference "github.com/sealerio/sealer/pkg/image/reference"
"github.com/sealerio/sealer/utils"
)

func GetCanonicalImageName(rawImageName string) (reference.Named, error) {
Expand All @@ -46,7 +47,6 @@ func GetCanonicalImagePullOptions(canonicalImageName string) types.ImagePullOpti
)

named, err := normalreference.ParseToNamed(canonicalImageName)

if err != nil {
logger.Warn("parse canonical ImageName failed: %v", err)
return opts
Expand All @@ -57,8 +57,12 @@ func GetCanonicalImagePullOptions(canonicalImageName string) types.ImagePullOpti
if registryAddr == dockerregistry.IndexName {
registryAddr = dockerregistry.IndexServer
}
svc, err := auth.NewDockerAuthService()
if err != nil {
return opts
}

authConfig, err = utils.GetDockerAuthInfoFromDocker(registryAddr)
authConfig, err = svc.GetAuthByDomain(registryAddr)
if err == nil {
encodedJSON, err = json.Marshal(authConfig)
if err != nil {
Expand Down
10 changes: 9 additions & 1 deletion pkg/image/default_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"os"
"path/filepath"

"github.com/sealerio/sealer/pkg/client/docker/auth"

"github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/manifest/schema2"
dockerstreams "github.com/docker/cli/cli/streams"
Expand Down Expand Up @@ -230,7 +232,13 @@ func (d DefaultImageService) Login(RegistryURL, RegistryUsername, RegistryPasswd
if err != nil {
return fmt.Errorf("failed to authenticate %s: %v", RegistryURL, err)
}
if err := utils.SetDockerConfig(RegistryURL, RegistryUsername, RegistryPasswd); err != nil {

svc, err := auth.NewDockerAuthService()
if err != nil {
return fmt.Errorf("failed to read default auth file: %v", err)
}

if err := svc.SetAuthInfo(RegistryURL, RegistryUsername, RegistryPasswd); err != nil {
return err
}
logger.Info("%s login %s success", RegistryUsername, RegistryURL)
Expand Down
25 changes: 18 additions & 7 deletions pkg/image/distributionutil/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,33 @@ package distributionutil

import (
"context"
"fmt"

"github.com/sealerio/sealer/pkg/client/docker/auth"

"github.com/distribution/distribution/v3"
"github.com/docker/docker/api/types"

"github.com/sealerio/sealer/logger"
"github.com/sealerio/sealer/pkg/image/reference"
"github.com/sealerio/sealer/utils"
)

func NewV2Repository(named reference.Named, actions ...string) (distribution.Repository, error) {
authConfig, authErr := utils.GetDockerAuthInfoFromDocker(named.Domain())
repo, err := getV2Repository(authConfig, named, actions...)
if err != nil && authErr != nil {
logger.Debug("failed to get auth info, err: %s", authErr)
var (
domain = named.Domain()
defaultAuth = types.AuthConfig{ServerAddress: domain}
)

svc, err := auth.NewDockerAuthService()
if err != nil {
return nil, fmt.Errorf("failed to read default auth file: %v", err)
}
return repo, err

authConfig, err := svc.GetAuthByDomain(domain)
if err != nil && authConfig != defaultAuth {
return nil, fmt.Errorf("failed to get auth info for domain%s: %v", domain, err)
}

return getV2Repository(authConfig, named, actions...)
}

func getV2Repository(authConfig types.AuthConfig, named reference.Named, actions ...string) (distribution.Repository, error) {
Expand Down
20 changes: 13 additions & 7 deletions pkg/image/save/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ import (
"io"
"strings"

"github.com/docker/docker/api/types"

"github.com/sealerio/sealer/pkg/client/docker/auth"

"github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/configuration"
"github.com/distribution/distribution/v3/reference"
"github.com/distribution/distribution/v3/registry/storage"
"github.com/distribution/distribution/v3/registry/storage/driver/factory"
dockerstreams "github.com/docker/cli/cli/streams"
"github.com/docker/docker/api/types"
dockerjsonmessage "github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter"
Expand All @@ -39,7 +42,6 @@ import (
"github.com/sealerio/sealer/pkg/image/distributionutil"
"github.com/sealerio/sealer/pkg/image/save/distributionpkg/proxy"
v1 "github.com/sealerio/sealer/types/api/v1"
"github.com/sealerio/sealer/utils"
)

const (
Expand Down Expand Up @@ -395,20 +397,24 @@ func NewProxyRegistry(ctx context.Context, rootdir, domain string) (distribution
proxyURL = defaultProxyURL
}

var defaultAuth = types.AuthConfig{ServerAddress: domain}
auth, err := utils.GetDockerAuthInfoFromDocker(domain)
svc, err := auth.NewDockerAuthService()
if err != nil {
return nil, fmt.Errorf("failed to read default auth file: %v", err)
}
defaultAuth := types.AuthConfig{ServerAddress: domain}
authConfig, err := svc.GetAuthByDomain(domain)
//ignore err when is there is no username and password.
//regard it as a public registry
//only report parse error
if err != nil && auth != defaultAuth {
if err != nil && authConfig != defaultAuth {
return nil, fmt.Errorf("get authentication info error: %v", err)
}

config := configuration.Configuration{
Proxy: configuration.Proxy{
RemoteURL: proxyURL,
Username: auth.Username,
Password: auth.Password,
Username: authConfig.Username,
Password: authConfig.Password,
},
Storage: configuration.Storage{
driverName: configuration.Parameters{configRootDir: rootdir},
Expand Down
Loading

0 comments on commit 8f856bf

Please sign in to comment.