Skip to content

Commit

Permalink
improve registry ref processing, logging & test files
Browse files Browse the repository at this point in the history
  • Loading branch information
Pwuts committed Jan 18, 2023
1 parent cba8af0 commit 0671ee8
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 82 deletions.
4 changes: 0 additions & 4 deletions pkg/registry/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,6 @@ func GetScopeFromImageName(imageRef ref.Named, svc string) string {
func GetChallengeURL(imageRef ref.Named) (url.URL, error) {
domain := ref.Domain(imageRef)

if len(domain) == 0 {
return url.URL{}, errors.New("image ref has no domain")
}

return url.URL{
Scheme: "https",
Host: domain,
Expand Down
1 change: 0 additions & 1 deletion pkg/registry/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (

// BuildManifestURL from raw image data
func BuildManifestURL(container types.Container) (string, error) {

normalizedRef, err := ref.ParseDockerRef(container.ImageName())
if err != nil {
return "", err
Expand Down
35 changes: 25 additions & 10 deletions pkg/registry/manifest/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var _ = Describe("the manifest module", func() {
mockName := "mock-container"
mockCreated := time.Now()

When("building a manifest url", func() {
Describe("BuildManifestURL", func() {
It("should return a valid url given a fully qualified image", func() {
expected := "https://ghcr.io/v2/containrrr/watchtower/manifests/latest"
imageInfo := apiTypes.ImageInspect{
Expand Down Expand Up @@ -62,15 +62,6 @@ var _ = Describe("the manifest module", func() {
Expect(err).NotTo(HaveOccurred())
Expect(res).To(Equal(expected))
})
It("should combine the tag name and digest pinning into one digest, given multiple colons", func() {
in := "containrrr/watchtower:latest@sha256:daf7034c5c89775afe3008393ae033529913548243b84926931d7c84398ecda7"
image, tag := "containrrr/watchtower", "latest@sha256:daf7034c5c89775afe3008393ae033529913548243b84926931d7c84398ecda7"

imageOut, tagOut := manifest.ExtractImageAndTag(in)

Expect(imageOut).To(Equal(image))
Expect(tagOut).To(Equal(tag))
})
It("should not prepend library/ for single-part container names in registries other than Docker Hub", func() {
expected := "https://docker-registry.domain/v2/imagename/manifests/latest"
imageRef := "docker-registry.domain/imagename:latest"
Expand All @@ -84,6 +75,30 @@ var _ = Describe("the manifest module", func() {
Expect(err).NotTo(HaveOccurred())
Expect(res).To(Equal(expected))
})
It("should throw an error on pinned images", func() {
imageRef := "docker-registry.domain/imagename@sha256:daf7034c5c89775afe3008393ae033529913548243b84926931d7c84398ecda7"
imageInfo := apiTypes.ImageInspect{
RepoTags: []string{
imageRef,
},
}
mock := mocks.CreateMockContainerWithImageInfo(mockId, mockName, imageRef, mockCreated, imageInfo)

URL, err := manifest.BuildManifestURL(mock)
Expect(err).To(HaveOccurred())
Expect(URL).To(BeEmpty())
})
})

Describe("ExtractImageAndTag", func() {
It("should combine the tag name and digest pinning into one digest, given multiple colons", func() {
in := "containrrr/watchtower:latest@sha256:daf7034c5c89775afe3008393ae033529913548243b84926931d7c84398ecda7"
image, tag := "containrrr/watchtower", "latest@sha256:daf7034c5c89775afe3008393ae033529913548243b84926931d7c84398ecda7"

imageOut, tagOut := manifest.ExtractImageAndTag(in)

Expect(imageOut).To(Equal(image))
Expect(tagOut).To(Equal(tag))
})
})
})
7 changes: 6 additions & 1 deletion pkg/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import (
log "github.com/sirupsen/logrus"
)

const (
DefaultRegistryDomain = "docker.io"
LegacyDefaultRegistryDomain = "index.docker.io"
)

// 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)
Expand Down Expand Up @@ -47,7 +52,7 @@ func WarnOnAPIConsumption(container watchtowerTypes.Container) bool {

containerHost := ref.Domain(normalizedName)

if containerHost == "docker.io" || containerHost == "ghcr.io" {
if containerHost == DefaultRegistryDomain || containerHost == "ghcr.io" {
return true
}

Expand Down
47 changes: 26 additions & 21 deletions pkg/registry/trust.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,38 @@ import (
"encoding/json"
"errors"
"os"
"strings"

cliconfig "github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/credentials"
"github.com/docker/cli/cli/config/types"
"github.com/docker/distribution/reference"
ref "github.com/docker/distribution/reference"
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)
func EncodedAuth(imageRef string) (string, error) {
auth, err := EncodedEnvAuth()
if err != nil {
auth, err = EncodedConfigAuth(ref)
auth, err = EncodedConfigAuth(imageRef)
}
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) {
func EncodedEnvAuth() (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 for user %s on registry %s", auth.Username, ref)
log.Debugf("Loaded auth credentials for registry user %s from environment", auth.Username)
log.Tracef("Using auth password %s", auth.Password)
return EncodeAuth(auth)
}
Expand All @@ -48,43 +47,49 @@ func EncodedEnvAuth(ref string) (string, error) {
// 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)
func EncodedConfigAuth(imageRef string) (string, error) {
registry, err := GetRegistryAddress(imageRef)
if err != nil {
log.Errorf("Unable to parse the image ref %s", err)
log.Errorf("Could not get registry from image ref %s: %s", imageRef, 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)
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
auth, _ := credStore.Get(registry) // returns (types.AuthConfig{}) if server not in credStore

if auth == (types.AuthConfig{}) {
log.WithField("config_file", configFile.Filename).Debugf("No credentials for %s found", server)
log.WithField("config_file", configFile.Filename).Debugf("No credentials for %s found", registry)
return "", nil
}
log.Debugf("Loaded auth credentials for user %s, on registry %s, from file %s", auth.Username, ref, configFile.Filename)
log.Debugf("Loaded auth credentials for user %s, on registry %s, from file %s", auth.Username, registry, configFile.Filename)
log.Tracef("Using auth password %s", auth.Password)
return EncodeAuth(auth)
}

// ParseServerAddress extracts the server part from a container image ref
func ParseServerAddress(ref string) (string, error) {

parsedRef, err := reference.Parse(ref)
// GetRegistryAddress extracts the server part from a container image ref,
// returning docker.io for single-part image names without an explicit domain
func GetRegistryAddress(imageRef string) (string, error) {
parsedRef, err := ref.Parse(imageRef)
if err != nil {
return ref, err
return "", err
}

parts := strings.Split(parsedRef.String(), "/")
return parts[0], nil
var registry string
if namedRef, ok := parsedRef.(ref.Named); ok && len(ref.Domain(namedRef)) > 0 {
registry = ref.Domain(namedRef)
} else {
registry = DefaultRegistryDomain
}
return registry, nil
}

// CredentialsStore returns a new credentials store based
Expand Down
90 changes: 45 additions & 45 deletions pkg/registry/trust_test.go
Original file line number Diff line number Diff line change
@@ -1,65 +1,65 @@
package registry

import (
"os"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"os"
)

var _ = Describe("Testing with Ginkgo", func() {
It("encoded env auth_ should return an error if repo envs are unset", func() {
_ = os.Unsetenv("REPO_USER")
_ = os.Unsetenv("REPO_PASS")

_, err := EncodedEnvAuth("")
Expect(err).To(HaveOccurred())
})
It("encoded env auth_ should return auth hash if repo envs are set", func() {
var err error
expectedHash := "eyJ1c2VybmFtZSI6ImNvbnRhaW5ycnItdXNlciIsInBhc3N3b3JkIjoiY29udGFpbnJyci1wYXNzIn0="
var _ = Describe("Registry credential helpers", func() {
Describe("EncodedAuth", func() {
It("should return repo credentials from env when set", func() {
var err error
expected := "eyJ1c2VybmFtZSI6ImNvbnRhaW5ycnItdXNlciIsInBhc3N3b3JkIjoiY29udGFpbnJyci1wYXNzIn0="

err = os.Setenv("REPO_USER", "containrrr-user")
Expect(err).NotTo(HaveOccurred())
err = os.Setenv("REPO_USER", "containrrr-user")
Expect(err).NotTo(HaveOccurred())

err = os.Setenv("REPO_PASS", "containrrr-pass")
Expect(err).NotTo(HaveOccurred())
err = os.Setenv("REPO_PASS", "containrrr-pass")
Expect(err).NotTo(HaveOccurred())

config, err := EncodedEnvAuth("")
Expect(config).To(Equal(expectedHash))
Expect(err).NotTo(HaveOccurred())
config, err := EncodedEnvAuth()
Expect(config).To(Equal(expected))
Expect(err).NotTo(HaveOccurred())
})
})
It("encoded config auth_ should return an error if file is not present", func() {
var err error

err = os.Setenv("DOCKER_CONFIG", "/dev/null/should-fail")
Expect(err).NotTo(HaveOccurred())

_, err = EncodedConfigAuth("")
Expect(err).To(HaveOccurred())
Describe("EncodedEnvAuth", func() {
It("should return an error if repo envs are unset", func() {
_ = os.Unsetenv("REPO_USER")
_ = os.Unsetenv("REPO_PASS")

_, err := EncodedEnvAuth()
Expect(err).To(HaveOccurred())
})
})
/*
* 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
*/
It("parse server address_ should return error if passed empty string", func() {

_, err := ParseServerAddress("")
Expect(err).To(HaveOccurred())
})
It("parse server address_ should return the organization part if passed an image name missing server name", func() {
Describe("EncodedConfigAuth", func() {
It("should return an error if file is not present", func() {
var err error

err = os.Setenv("DOCKER_CONFIG", "/dev/null/should-fail")
Expect(err).NotTo(HaveOccurred())

val, _ := ParseServerAddress("containrrr/config")
Expect(val).To(Equal("containrrr"))
_, err = EncodedConfigAuth("")
Expect(err).To(HaveOccurred())
})
})
It("parse server address_ should return the server name if passed a fully qualified image name", func() {

val, _ := ParseServerAddress("github.com/containrrrr/config")
Expect(val).To(Equal("github.com"))
Describe("GetRegistryAddress", func() {
It("should return error if passed empty string", func() {
_, err := GetRegistryAddress("")
Expect(err).To(HaveOccurred())
})
It("should return docker.io if passed a single-part image name with no explicit domain", func() {
Expect(GetRegistryAddress("containrrr")).To(Equal(DefaultRegistryDomain))
})
It("should return the organization part if passed a multi-part image name with no explicit domain", func() {
Expect(GetRegistryAddress("containrrr/config")).To(Equal("containrrr"))
})
It("should return the server name if passed a fully qualified image name", func() {
Expect(GetRegistryAddress("github.com/containrrr/config")).To(Equal("github.com"))
})
})
})

0 comments on commit 0671ee8

Please sign in to comment.