agola/internal/services/runservice/executor/registry/registry.go

140 lines
4.0 KiB
Go

// Copyright 2019 Sorint.lab
//
// 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 registry
import (
"encoding/base64"
"fmt"
"strings"
"github.com/pkg/errors"
"github.com/sorintlab/agola/internal/services/runservice/types"
"github.com/google/go-containerregistry/pkg/name"
)
//func registryAuthToken(auth *types.DockerRegistryAuth) (string, error) {
// if auth == nil {
// return "", nil
// }
//
// switch auth.Type {
// case types.DockerRegistryAuthTypeBasic:
// authConfig := dtypes.AuthConfig{
// Username: auth.Username,
// Password: auth.Password,
// }
// authConfigj, err := json.Marshal(authConfig)
// if err != nil {
// panic(err)
// }
// return base64.URLEncoding.EncodeToString(authConfigj), nil
//
// default:
// return "", errors.Errorf("unsupported registry auth type %q", auth.Type)
// }
//}
// Docker config represents the docker config.json format. We only consider the "auths" part
type DockerConfig struct {
Auths map[string]DockerConfigAuth `json:"auths,omitempty"`
}
// Docker config represents the docker config.json auth part. We only consider the "auth" token part
type DockerConfigAuth struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Auth string `json:"auth,omitempty"`
}
// There are a variety of ways a domain may get qualified within the Docker credential file.
// We enumerate them here as format strings.
var (
domainForms = []string{
// Allow naked domains
"%s",
// Allow scheme-prefixed.
"https://%s",
"http://%s",
// Allow scheme-prefixes with version in url path.
"https://%s/v1/",
"http://%s/v1/",
"https://%s/v2/",
"http://%s/v2/",
}
)
func GetRegistry(image string) (string, error) {
ref, err := name.ParseReference(image, name.WeakValidation)
if err != nil {
return "", err
}
regName := ref.Context().RegistryStr()
return regName, nil
}
// ResolveAuth resolves the auth username and password for the provided registry name
func ResolveAuth(auths map[string]types.DockerRegistryAuth, regname string) (string, string, error) {
if auths != nil {
for _, form := range domainForms {
if auth, ok := auths[fmt.Sprintf(form, regname)]; ok {
switch auth.Type {
case types.DockerRegistryAuthTypeEncodedAuth:
decoded, err := base64.StdEncoding.DecodeString(auth.Auth)
if err != nil {
return "", "", errors.Wrapf(err, "failed to decode docker auth")
}
parts := strings.Split(string(decoded), ":")
if len(parts) != 2 {
return "", "", errors.Wrapf(err, "wrong docker auth")
}
return parts[0], parts[1], nil
case types.DockerRegistryAuthTypeBasic:
return auth.Username, auth.Password, nil
default:
return "", "", fmt.Errorf("unsupported auth type %q", auth.Type)
}
}
}
}
return "", "", nil
}
func GenDockerConfig(auths map[string]types.DockerRegistryAuth, images []string) (*DockerConfig, error) {
dockerConfig := &DockerConfig{Auths: make(map[string]DockerConfigAuth)}
for _, image := range images {
ref, err := name.ParseReference(image, name.WeakValidation)
if err != nil {
return nil, err
}
regName := ref.Context().RegistryStr()
if _, ok := dockerConfig.Auths[regName]; ok {
continue
}
username, password, err := ResolveAuth(auths, regName)
if err != nil {
return nil, errors.Wrapf(err, "failed to resolve auth")
}
delimited := fmt.Sprintf("%s:%s", username, password)
auth := base64.StdEncoding.EncodeToString([]byte(delimited))
dockerConfig.Auths[regName] = DockerConfigAuth{Username: username, Password: password, Auth: auth}
}
return dockerConfig, nil
}