Skip to content

Commit

Permalink
Merge pull request #13 from aquasecurity/registry
Browse files Browse the repository at this point in the history
Support specifying the registry name
  • Loading branch information
lizrice committed Aug 4, 2017
2 parents 201c148 + e5be551 commit 2e887c2
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 28 deletions.
54 changes: 43 additions & 11 deletions cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,43 @@ func imageNameForManifest(imageName string) string {
return imageName + ":_manifesto"
}

func repoAndTaggedNames(name string) (repoName string, imageName string, tagName string) {
nameSlice := strings.Split(name, ":")
repoName = nameSlice[0]
// registryName - dockerHub if omitted
// repoName - hostname/org/repo - hostname is omitted if it's dockerHub
// imageName - hostname/org/repo:tag - latest is used for the tag if not specified; hostname omitted if it's dockerHub
// repoNameNoHost - org/repo - hostname always omitted
func getNameComponents(name string) (registryName string, repoName string, imageName string, repoNameNoHost string, tagName string, digestName string) {

// reference := name [ ":" tag ] [ "@" digest ]
nameSlice := strings.Split(name, "@")
if len(nameSlice) > 1 {
digestName = nameSlice[1]
name = nameSlice[0]
}

// name := [ hostname "/" ] component [ "/" component ]*
// name can include : as part of the host name, so we look for hostname and components before
// looking for the tag
nameSlice = strings.Split(name, "/")
registryName = dockerHub
if len(nameSlice) > 2 {
name = strings.Join(nameSlice[1:], "/")
registryName = nameSlice[0]

// Include registry name in repo name if it's not Docker Hub
repoName = registryName + "/"
}

// Now look for a tag
nameSlice = strings.Split(name, ":")
repoName += nameSlice[0]
repoNameNoHost = nameSlice[0]
tagName = "latest"
if len(nameSlice) > 1 {
tagName = nameSlice[1]
}

imageName = repoName + ":" + tagName
return repoName, imageName, tagName
return registryName, repoName, imageName, repoNameNoHost, tagName, digestName
}

// getCmd gets manifesto data
Expand All @@ -110,6 +138,8 @@ var getCmd = &cobra.Command{
Short: "Show metadata for the container image",
Long: `Display metadata information about the container image.`,
Run: func(cmd *cobra.Command, args []string) {
var err error

if len(args) < 2 {
cmd.Help()
return
Expand All @@ -118,14 +148,16 @@ var getCmd = &cobra.Command{
name := args[0]
metadata := args[1]

repoName, imageName, _ := repoAndTaggedNames(name)
registryURL, repoName, imageName, repoNameNoHost, _, imageDigest := getNameComponents(name)
metadataImageName := imageNameForManifest(repoName)

// Get the digest for the image
imageDigest, err := dockerGetDigest(imageName)
if err != nil {
fmt.Printf("Image '%s' not found\n", imageName)
os.Exit(1)
if imageDigest == "" {
imageDigest, err = dockerGetDigest(imageName)
if err != nil {
fmt.Printf("Image '%s' not found\n", imageName)
os.Exit(1)
}
}

log.Debugf("Image has digest %s", imageDigest)
Expand All @@ -142,7 +174,7 @@ var getCmd = &cobra.Command{

// We'll need the registry API from here on
ensureRegistryCredentials()
r, err := registry.New(dockerHub, username, password)
r, err := registry.New(registryURL, username, password)
if err != nil {
fmt.Fprintf(os.Stderr, "Error connecting to registry: %v\n", err)
os.Exit(1)
Expand All @@ -155,7 +187,7 @@ var getCmd = &cobra.Command{
for _, m := range v.MetadataManifesto {
if m.Type == metadata {
log.Debugf("'%s' metadata identified", metadata)
contents, err := r.GetBlob(repoName, m.Digest)
contents, err := r.GetBlob(repoNameNoHost, m.Digest)
if err != nil {
// Maybe this metadata was stored as an image by a previous version of manifesto
// so try getting it that way
Expand Down
67 changes: 67 additions & 0 deletions cmd/get_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package cmd

import "testing"

func TestGetNameComponents(t *testing.T) {
cases := []struct {
input string
reg string
repo string
img string
repoNoHost string
tag string
digest string
}{
{"lizrice/imagetest", "registry-1.docker.io", "lizrice/imagetest", "lizrice/imagetest:latest", "lizrice/imagetest", "latest", ""},
{"lizrice/imagetest:v1.0", "registry-1.docker.io", "lizrice/imagetest", "lizrice/imagetest:v1.0", "lizrice/imagetest", "v1.0", ""},
{"quay.io/lizrice/imagetest:v1.0", "quay.io", "quay.io/lizrice/imagetest", "quay.io/lizrice/imagetest:v1.0", "lizrice/imagetest", "v1.0", ""},
{"localhost:5000/lizrice/imagetest:v1.0", "localhost:5000", "localhost:5000/lizrice/imagetest", "localhost:5000/lizrice/imagetest:v1.0", "lizrice/imagetest", "v1.0", ""},
{"lizrice/imagetest@12345", "registry-1.docker.io", "lizrice/imagetest", "lizrice/imagetest:latest", "lizrice/imagetest", "latest", "12345"},
}

for _, c := range cases {
t.Run(c.input, func(t *testing.T) {
reg, repo, img, repoNoHost, tag, digest := getNameComponents(c.input)
if reg != c.reg {
t.Fatalf("registry name: got %s expected %s", reg, c.reg)
}
if repo != c.repo {
t.Fatalf("repo name: got %s expected %s", repo, c.repo)
}
if img != c.img {
t.Fatalf("image name: got %s expected %s", img, c.img)
}
if repoNoHost != c.repoNoHost {
t.Fatalf("repo name without host: got %s expected %s", repoNoHost, c.repoNoHost)
}
if tag != c.tag {
t.Fatalf("tag name: got %s expected %s", tag, c.tag)
}
if digest != c.digest {
t.Fatalf("digest: got %s expected %s", digest, c.digest)
}
})
}
}

func TestImageNameForManifest(t *testing.T) {
cases := []struct {
input string
img string
}{
{"lizrice/imagetest", "lizrice/imagetest:_manifesto"},
{"lizrice/imagetest:v1.0", "lizrice/imagetest:_manifesto"},
{"quay.io/lizrice/imagetest:v1.0", "quay.io/lizrice/imagetest:_manifesto"},
{"localhost:5000/lizrice/imagetest", "localhost:5000/lizrice/imagetest:_manifesto"},
}

for _, c := range cases {
t.Run(c.input, func(t *testing.T) {
_, repo, _, _, _, _ := getNameComponents(c.input)
img := imageNameForManifest(repo)
if img != c.img {
t.Fatalf("manifesto image name: got %s expected %s", img, c.img)
}
})
}
}
13 changes: 8 additions & 5 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,23 @@ var listCmd = &cobra.Command{
Short: "List currently stored metadata for the container image",
Long: `Display a list of the metadata stored for the specified container image.`,
Run: func(cmd *cobra.Command, args []string) {
var err error
if len(args) < 1 {
cmd.Help()
return
}

name := args[0]
repoName, imageName, _ := repoAndTaggedNames(name)
_, repoName, imageName, _, _, imageDigest := getNameComponents(name)
metadataImageName := imageNameForManifest(repoName)

// Get the digest for the image
imageDigest, err := dockerGetDigest(imageName)
if err != nil {
fmt.Printf("Image '%s' not found\n", imageName)
os.Exit(1)
if imageDigest == "" {
imageDigest, err = dockerGetDigest(imageName)
if err != nil {
fmt.Printf("Image '%s' not found\n", imageName)
os.Exit(1)
}
}

log.Debugf("Image has digest %s", imageDigest)
Expand Down
20 changes: 12 additions & 8 deletions cmd/put.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"github.com/spf13/cobra"
)

const dockerHub = "https://registry-1.docker.io"
const dockerHub = "registry-1.docker.io"

type Stream struct {
Stream string `json:"stream"`
Expand Down Expand Up @@ -101,6 +101,8 @@ var putCmd = &cobra.Command{
Short: "Put metadata for the container image",
Long: `Store datafile as metadata associated with the image`,
Run: func(cmd *cobra.Command, args []string) {
var err error

if len(args) < 3 {
cmd.Help()
return
Expand All @@ -110,16 +112,18 @@ var putCmd = &cobra.Command{
metadataName := args[1]
datafile := args[2]

repoName, imageName, _ := repoAndTaggedNames(name)
registryName, repoName, imageName, repoNameNoHost, _, imageDigest := getNameComponents(name)
metadataImageName := imageNameForManifest(repoName)

fmt.Printf("Storing metadata '%s' for image '%s'\n", metadataName, imageName)

// Get the digest for this image
imageDigest, err := dockerGetDigest(imageName)
if err != nil {
fmt.Printf("Image '%s' not found\n", imageName)
os.Exit(1)
if imageDigest == "" {
imageDigest, err = dockerGetDigest(imageName)
if err != nil {
fmt.Printf("Image '%s' not found\n", imageName)
os.Exit(1)
}
}

log.Debugf("Image has digest %s", imageDigest)
Expand All @@ -128,7 +132,7 @@ var putCmd = &cobra.Command{

// We'll need the registry API from here on
ensureRegistryCredentials()
r, err := registry.New(dockerHub, username, password)
r, err := registry.New(registryName, username, password)
if err != nil {
fmt.Fprintf(os.Stderr, "Error connecting to registry: %v\n", err)
os.Exit(1)
Expand All @@ -139,7 +143,7 @@ var putCmd = &cobra.Command{
fmt.Fprintf(os.Stderr, "Error opening file %s: %v\n", datafile, err)
}

digest, err := r.UploadBlob(repoName, f)
digest, err := r.UploadBlob(repoNameNoHost, f)
if err != nil {
fmt.Printf("Error uploading metadata to registry: %v\n", err)
os.Exit(1)
Expand Down
7 changes: 4 additions & 3 deletions registry/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,18 @@ func computeDigest(data []byte) string {
func (r *V2) UploadBlob(repoName string, data io.Reader) (string, error) {

// Post to get the location / UUID for this upload
res, err := r.call("POST", "/v2/"+repoName+"/blobs/uploads/", []byte{}, "")
URL := "/v2/" + repoName + "/blobs/uploads/"
res, err := r.call("POST", URL, []byte{}, "")
if err != nil {
return "", fmt.Errorf("post to blobs/upload failed: %v", err)
return "", fmt.Errorf("post to %s failed: %v", URL, err)
}

// We don't need the body at all so discard and close it now
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()

if res.StatusCode != http.StatusAccepted {
return "", fmt.Errorf("post to blobs/upload not accepted: %s", res.Status)
return "", fmt.Errorf("post to %s not accepted: %s", URL, res.Status)
}

// The post gives us the location for the blob upload
Expand Down
4 changes: 4 additions & 0 deletions registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ func New(URL, username, password string) (*V2, error) {
return nil, errors.New("The registry URL must be provided")
}

if !strings.HasPrefix(URL, "http") {
URL = "https://" + URL
}

r := &V2{
URL: URL,
Client: &http.Client{
Expand Down
2 changes: 1 addition & 1 deletion registry/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestNew(t *testing.T) {
t.Fatalf("Failed to create registry: %v", err)
}

if r.URL != "example.com" {
if r.URL != "https://example.com" {
t.Fatalf("Unexpected registry URL %s", r.URL)
}

Expand Down

0 comments on commit 2e887c2

Please sign in to comment.