mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-13 21:56:38 +01:00
refactor: extract code from the container package
This commit is contained in:
parent
4130b110c6
commit
d1abce889a
15 changed files with 253 additions and 185 deletions
33
pkg/registry/registry.go
Normal file
33
pkg/registry/registry.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// GetPullOptions creates a struct with all options needed for pulling images from a registry
|
||||
func GetPullOptions(imageName string) (types.ImagePullOptions, error) {
|
||||
auth, err := EncodedAuth(imageName)
|
||||
log.Debugf("Got image name: %s", imageName)
|
||||
if err != nil {
|
||||
return types.ImagePullOptions{}, err
|
||||
}
|
||||
|
||||
log.Debugf("Got auth value: %s", auth)
|
||||
if auth == "" {
|
||||
return types.ImagePullOptions{}, nil
|
||||
}
|
||||
|
||||
return types.ImagePullOptions{
|
||||
RegistryAuth: auth,
|
||||
PrivilegeFunc: DefaultAuthHandler,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DefaultAuthHandler will be invoked if an AuthConfig is rejected
|
||||
// It could be used to return a new value for the "X-Registry-Auth" authentication header,
|
||||
// but there's no point trying again with the same value as used in AuthConfig
|
||||
func DefaultAuthHandler() (string, error) {
|
||||
log.Debug("Authentication request was rejected. Trying again without authentication")
|
||||
return "", nil
|
||||
}
|
||||
99
pkg/registry/trust.go
Normal file
99
pkg/registry/trust.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
cliconfig "github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/config/credentials"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// EncodedAuth returns an encoded auth config for the given registry
|
||||
// loaded from environment variables or docker config
|
||||
// as available in that order
|
||||
func EncodedAuth(ref string) (string, error) {
|
||||
auth, err := EncodedEnvAuth(ref)
|
||||
if err != nil {
|
||||
auth, err = EncodedConfigAuth(ref)
|
||||
}
|
||||
return auth, err
|
||||
}
|
||||
|
||||
// EncodedEnvAuth returns an encoded auth config for the given registry
|
||||
// loaded from environment variables
|
||||
// Returns an error if authentication environment variables have not been set
|
||||
func EncodedEnvAuth(ref string) (string, error) {
|
||||
username := os.Getenv("REPO_USER")
|
||||
password := os.Getenv("REPO_PASS")
|
||||
if username != "" && password != "" {
|
||||
auth := types.AuthConfig{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
log.Debugf("Loaded auth credentials %s for %s", auth, ref)
|
||||
return EncodeAuth(auth)
|
||||
}
|
||||
return "", errors.New("Registry auth environment variables (REPO_USER, REPO_PASS) not set")
|
||||
}
|
||||
|
||||
// EncodedConfigAuth returns an encoded auth config for the given registry
|
||||
// loaded from the docker config
|
||||
// Returns an empty string if credentials cannot be found for the referenced server
|
||||
// The docker config must be mounted on the container
|
||||
func EncodedConfigAuth(ref string) (string, error) {
|
||||
server, err := ParseServerAddress(ref)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to parse the image ref %s", err)
|
||||
return "", err
|
||||
}
|
||||
configDir := os.Getenv("DOCKER_CONFIG")
|
||||
if configDir == "" {
|
||||
configDir = "/"
|
||||
}
|
||||
configFile, err := cliconfig.Load(configDir)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to find default config file %s", err)
|
||||
return "", err
|
||||
}
|
||||
credStore := CredentialsStore(*configFile)
|
||||
auth, _ := credStore.Get(server) // returns (types.AuthConfig{}) if server not in credStore
|
||||
|
||||
if auth == (types.AuthConfig{}) {
|
||||
log.Debugf("No credentials for %s in %s", server, configFile.Filename)
|
||||
return "", nil
|
||||
}
|
||||
log.Debugf("Loaded auth credentials %s from %s", auth, configFile.Filename)
|
||||
return EncodeAuth(auth)
|
||||
}
|
||||
|
||||
// ParseServerAddress extracts the server part from a container image ref
|
||||
func ParseServerAddress(ref string) (string, error) {
|
||||
|
||||
parsedRef, err := reference.Parse(ref)
|
||||
if err != nil {
|
||||
return ref, err
|
||||
}
|
||||
|
||||
parts := strings.Split(parsedRef.String(), "/")
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
// CredentialsStore returns a new credentials store based
|
||||
// on the settings provided in the configuration file.
|
||||
func CredentialsStore(configFile configfile.ConfigFile) credentials.Store {
|
||||
if configFile.CredentialsStore != "" {
|
||||
return credentials.NewNativeStore(&configFile, configFile.CredentialsStore)
|
||||
}
|
||||
return credentials.NewFileStore(&configFile)
|
||||
}
|
||||
|
||||
// EncodeAuth Base64 encode an AuthConfig struct for transmission over HTTP
|
||||
func EncodeAuth(auth types.AuthConfig) (string, error) {
|
||||
return command.EncodeAuthToBase64(auth)
|
||||
}
|
||||
59
pkg/registry/trust_test.go
Normal file
59
pkg/registry/trust_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncodedEnvAuth_ShouldReturnAnErrorIfRepoEnvsAreUnset(t *testing.T) {
|
||||
os.Unsetenv("REPO_USER")
|
||||
os.Unsetenv("REPO_PASS")
|
||||
_, err := EncodedEnvAuth("")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
func TestEncodedEnvAuth_ShouldReturnAuthHashIfRepoEnvsAreSet(t *testing.T) {
|
||||
expectedHash := "eyJ1c2VybmFtZSI6ImNvbnRhaW5ycnItdXNlciIsInBhc3N3b3JkIjoiY29udGFpbnJyci1wYXNzIn0="
|
||||
|
||||
os.Setenv("REPO_USER", "containrrr-user")
|
||||
os.Setenv("REPO_PASS", "containrrr-pass")
|
||||
config, _ := EncodedEnvAuth("")
|
||||
|
||||
assert.Equal(t, config, expectedHash)
|
||||
}
|
||||
|
||||
func TestEncodedConfigAuth_ShouldReturnAnErrorIfFileIsNotPresent(t *testing.T) {
|
||||
os.Setenv("DOCKER_CONFIG", "/dev/null/should-fail")
|
||||
_, err := EncodedConfigAuth("")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* This part only confirms that it still works in the same way as it did
|
||||
* with the old version of the docker api client sdk. I'd say that
|
||||
* ParseServerAddress likely needs to be elaborated a bit to default to
|
||||
* dockerhub in case no server address was provided.
|
||||
*
|
||||
* ++ @simskij, 2019-04-04
|
||||
*/
|
||||
|
||||
func TestParseServerAddress_ShouldReturnErrorIfPassedEmptyString(t *testing.T) {
|
||||
_, err := ParseServerAddress("")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestParseServerAddress_ShouldReturnTheRepoNameIfPassedAFullyQualifiedImageName(t *testing.T) {
|
||||
val, _ := ParseServerAddress("github.com/containrrrr/config")
|
||||
assert.Equal(t, val, "github.com")
|
||||
}
|
||||
|
||||
func TestParseServerAddress_ShouldReturnTheOrganizationPartIfPassedAnImageNameMissingServerName(t *testing.T) {
|
||||
val, _ := ParseServerAddress("containrrr/config")
|
||||
assert.Equal(t, val, "containrrr")
|
||||
}
|
||||
|
||||
func TestParseServerAddress_ShouldReturnTheServerNameIfPassedAFullyQualifiedImageName(t *testing.T) {
|
||||
val, _ := ParseServerAddress("github.com/containrrrr/config")
|
||||
assert.Equal(t, val, "github.com")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue