From f3a9b00ec86f334702381edf842f03b3a9243a0a Mon Sep 17 00:00:00 2001 From: Jess Frazelle Date: Sun, 17 Jun 2018 15:41:58 -0400 Subject: [PATCH] refactor how the domain for the images is used Signed-off-by: Jess Frazelle --- README.md | 17 ++++++----- delete.go | 27 ++++++++++++++---- delete_test.go | 30 +++++++------------- digest.go | 15 +++++++--- layer.go | 20 ++++++++++--- layer_test.go | 33 ++++++++++++++++++++++ list.go | 61 ++++++++++++++++++++++++---------------- list_test.go | 15 +++++----- main.go | 63 ++++++++++++++++++----------------------- main_test.go | 7 ++++- manifest.go | 14 ++++++--- manifest_test.go | 31 ++++++++++++++++++++ registry/delete.go | 18 ++---------- registry/digest.go | 19 ++++++++----- registry/image.go | 67 ++++++++++++++++++++++++++++++++++++++++++++ registry/manifest.go | 6 ++-- tags.go | 16 ++++++++++- tags_test.go | 8 ++++-- vulns.go | 12 ++++++-- vulns_test.go | 7 +++-- 20 files changed, 337 insertions(+), 149 deletions(-) create mode 100644 layer_test.go create mode 100644 manifest_test.go create mode 100644 registry/image.go diff --git a/README.md b/README.md index 83661892..b0cd14fa 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,6 @@ GLOBAL OPTIONS: --force-non-ssl, -f force allow use of non-ssl --username value, -u value username for the registry --password value, -p value password for the registry - --registry value, -r value URL to the private registry (ex. r.j3ss.co) (default: "https://registry-1.docker.io") [$REG_REGISTRY] --timeout value timeout for HTTP requests (default: "1m") --skip-ping skip pinging the registry while establishing connection --help, -h show help @@ -87,7 +86,7 @@ not present, you can pass through flags directly. ```console # this command might take a while if you have hundreds of images like I do -$ reg -r r.j3ss.co ls +$ reg ls r.j3ss.co Repositories for r.j3ss.co REPO TAGS awscli latest @@ -100,7 +99,7 @@ chrome beta, latest, stable **Tags** ```console -$ reg tags tor-browser +$ reg tags r.j3ss.co/tor-browser alpha hardened latest @@ -110,7 +109,7 @@ stable ### Get a Manifest ```console -$ reg manifest htop +$ reg manifest r.j3ss.co/htop { "schemaVersion": 1, "name": "htop", @@ -130,30 +129,30 @@ $ reg manifest htop ### Get the Digest ```console -$ reg digest htop +$ reg digest r.j3ss.co/htop sha256:791158756cc0f5b27ef8c5c546284568fc9b7f4cf1429fb736aff3ee2d2e340f ``` ### Download a Layer ```console -$ reg layer -o chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4 +$ reg layer -o r.j3ss.co/chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4 OR -$ reg layer chrome@sha256:a3ed95caeb0.. > layer.tar +$ reg layer r.j3ss.co/chrome@sha256:a3ed95caeb0.. > layer.tar ``` ### Delete an Image ```console -$ reg rm chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4 +$ reg rm r.j3ss.co/chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4 Deleted chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4 ``` ### Vulnerability Reports ```console -$ reg vulns --clair https://clair.j3ss.co chrome +$ reg vulns --clair https://clair.j3ss.co r.j3ss.co/chrome Found 32 vulnerabilities CVE-2015-5180: [Low] diff --git a/delete.go b/delete.go index 6f82b22e..e9d97ba4 100644 --- a/delete.go +++ b/delete.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/genuinetools/reg/repoutils" + "github.com/genuinetools/reg/registry" "github.com/urfave/cli" ) @@ -16,15 +16,32 @@ var deleteCommand = cli.Command{ return fmt.Errorf("pass the name of the repository") } - repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0]) + image, err := registry.ParseImage(c.Args().First()) if err != nil { return err } - if err := r.Delete(repo, ref); err != nil { - return fmt.Errorf("Delete %s@%s failed: %v", repo, ref, err) + // Create the registry client. + r, err := createRegistryClient(c, image.Domain) + if err != nil { + return err } - fmt.Printf("Deleted %s@%s\n", repo, ref) + + // Get the digest. + digest, err := r.Digest(image) + if err != nil { + return err + } + + if err := image.WithDigest(digest); err != nil { + return err + } + + // Delete the reference. + if err := r.Delete(image.Path, digest); err != nil { + return err + } + fmt.Printf("Deleted %s\n", image.String()) return nil }, diff --git a/delete_test.go b/delete_test.go index b523f973..39d41d4b 100644 --- a/delete_test.go +++ b/delete_test.go @@ -1,37 +1,27 @@ package main import ( + "fmt" "strings" "testing" ) func TestDelete(t *testing.T) { // Make sure we have busybox in list. - out, err := run("ls") + out, err := run("ls", domain) if err != nil { - t.Fatalf("output: %s, error: %v", string(out), err) + t.Fatalf("output: %s, error: %v", out, err) } - expected := []string{"alpine latest", "busybox glibc, musl, latest"} - for _, e := range expected { - if !strings.Contains(out, e) { - t.Logf("expected to contain: %s\ngot: %s", e, out) - } + expected := `REPO TAGS +alpine 3.5, latest +busybox glibc, latest, musl` + if !strings.HasSuffix(strings.TrimSpace(out), expected) { + t.Fatalf("expected to contain: %s\ngot: %s", expected, out) } // Remove busybox image. - if out, err := run("rm", "busybox"); err != nil { - t.Fatalf("output: %s, error: %v", string(out), err) + if out, err := run("rm", fmt.Sprintf("%s/busybox:glibc", domain)); err != nil { + t.Fatalf("output: %s, error: %v", out, err) } - // Make sure there is no busybox in list. - out, err = run("ls") - if err != nil { - t.Fatalf("output: %s, error: %v", string(out), err) - } - expected = []string{"alpine latest", "busybox glibc, musl\n"} - for _, e := range expected { - if !strings.Contains(out, e) { - t.Logf("expected to contain: %s\ngot: %s", e, out) - } - } } diff --git a/digest.go b/digest.go index 6595d963..2a91c38c 100644 --- a/digest.go +++ b/digest.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/genuinetools/reg/repoutils" + "github.com/genuinetools/reg/registry" "github.com/urfave/cli" ) @@ -15,17 +15,24 @@ var digestCommand = cli.Command{ return fmt.Errorf("pass the name of the repository") } - repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0]) + image, err := registry.ParseImage(c.Args().First()) if err != nil { return err } - digest, err := r.Digest(repo, ref) + // Create the registry client. + r, err := createRegistryClient(c, image.Domain) if err != nil { return err } - fmt.Println(digest) + // Get the digest. + digest, err := r.Digest(image) + if err != nil { + return err + } + + fmt.Println(digest.String()) return nil }, diff --git a/layer.go b/layer.go index 13c0a2b4..4531c313 100644 --- a/layer.go +++ b/layer.go @@ -5,8 +5,7 @@ import ( "io/ioutil" "os" - "github.com/genuinetools/reg/repoutils" - digest "github.com/opencontainers/go-digest" + "github.com/genuinetools/reg/registry" "github.com/urfave/cli" ) @@ -25,12 +24,25 @@ var layerCommand = cli.Command{ return fmt.Errorf("pass the name of the repository") } - repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0]) + image, err := registry.ParseImage(c.Args().First()) if err != nil { return err } - layer, err := r.DownloadLayer(repo, digest.FromString(ref)) + // Create the registry client. + r, err := createRegistryClient(c, image.Domain) + if err != nil { + return err + } + + // Get the digest. + digest, err := r.Digest(image) + if err != nil { + return err + } + + // Download the layer. + layer, err := r.DownloadLayer(image.Path, digest) if err != nil { return err } diff --git a/layer_test.go b/layer_test.go new file mode 100644 index 00000000..82fae8bb --- /dev/null +++ b/layer_test.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestLayer(t *testing.T) { + // Get the digest. + out, err := run("digest", fmt.Sprintf("%s/busybox", domain)) + if err != nil { + t.Fatalf("output: %s, error: %v", out, err) + } + + tmpf := filepath.Join(os.TempDir(), "download-layer.tar") + defer os.RemoveAll(tmpf) + + // Download the layer. + lines := strings.Split(strings.TrimSpace(out), "\n") + layer := fmt.Sprintf("%s/busybox@%s", domain, strings.TrimSpace(lines[len(lines)-1])) + out, err = run("layer", "-o", tmpf, layer) + if err != nil { + t.Fatalf("output: %s, error: %v", out, err) + } + + // Make sure the file exists + if _, err := os.Stat(tmpf); os.IsNotExist(err) { + t.Fatalf("%s should exist after downloading the layer but it didn't", tmpf) + } +} diff --git a/list.go b/list.go index de79796c..178f316a 100644 --- a/list.go +++ b/list.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "sort" "strings" "sync" "text/tabwriter" @@ -15,13 +16,46 @@ var listCommand = cli.Command{ Aliases: []string{"ls"}, Usage: "list all repositories", Action: func(c *cli.Context) error { + if len(c.Args()) < 1 { + return fmt.Errorf("pass the domain of the registry") + } + + // Create the registry client. + r, err := createRegistryClient(c, c.Args().First()) + if err != nil { + return err + } + // Get the repositories via catalog. repos, err := r.Catalog("") if err != nil { return err } + sort.Strings(repos) - fmt.Printf("Repositories for %s\n", auth.ServerAddress) + fmt.Printf("Repositories for %s\n", r.Domain) + + var ( + wg sync.WaitGroup + repoTags = map[string][]string{} + ) + + wg.Add(len(repos)) + for _, repo := range repos { + go func(repo string) { + // Get the tags. + tags, err := r.Tags(repo) + if err != nil { + fmt.Printf("Get tags of [%s] error: %s", repo, err) + } + // Sort the tags + sort.Strings(tags) + repoTags[repo] = tags + + wg.Done() + }(repo) + } + wg.Wait() // Setup the tab writer. w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) @@ -29,31 +63,10 @@ var listCommand = cli.Command{ // Print header. fmt.Fprintln(w, "REPO\tTAGS") - var ( - l sync.Mutex - wg sync.WaitGroup - ) - - wg.Add(len(repos)) + // Sort the repos. for _, repo := range repos { - go func(repo string) { - // Get the tags and print to stdout. - tags, err := r.Tags(repo) - if err != nil { - fmt.Printf("Get tags of [%s] error: %s", repo, err) - } - out := fmt.Sprintf("%s\t%s\n", repo, strings.Join(tags, ", ")) - - // Lock around the tabwriter to prevent garbled output. - // See: https://github.com/genuinetools/reg/issues/54 - l.Lock() - w.Write([]byte(out)) - l.Unlock() - - wg.Done() - }(repo) + w.Write([]byte(fmt.Sprintf("%s\t%s\n", repo, strings.Join(repoTags[repo], ", ")))) } - wg.Wait() w.Flush() diff --git a/list_test.go b/list_test.go index f0674cfe..e13c4ca0 100644 --- a/list_test.go +++ b/list_test.go @@ -6,14 +6,15 @@ import ( ) func TestList(t *testing.T) { - out, err := run("ls") + out, err := run("ls", domain) if err != nil { - t.Fatalf("output: %s, error: %v", string(out), err) + t.Fatalf("output: %s, error: %v", out, err) } - expected := []string{"alpine latest", "busybox glibc, musl"} - for _, e := range expected { - if !strings.Contains(out, e) { - t.Logf("expected to contain: %s\ngot: %s", e, out) - } + + expected := `REPO TAGS +alpine 3.5, latest +busybox glibc, latest, musl` + if !strings.HasSuffix(strings.TrimSpace(out), expected) { + t.Fatalf("expected to contain: %s\ngot: %s", expected, out) } } diff --git a/main.go b/main.go index 2ac726f2..6fefd5e7 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,6 @@ import ( "strings" "time" - "github.com/docker/docker/api/types" "github.com/genuinetools/reg/registry" "github.com/genuinetools/reg/repoutils" "github.com/genuinetools/reg/version" @@ -14,11 +13,6 @@ import ( "github.com/urfave/cli" ) -var ( - auth types.AuthConfig - r *registry.Registry -) - func main() { app := cli.NewApp() app.Name = "reg" @@ -48,12 +42,6 @@ func main() { Name: "password, p", Usage: "password for the registry", }, - cli.StringFlag{ - Name: "registry, r", - Usage: "URL to the private registry (ex. r.j3ss.co)", - Value: repoutils.DefaultDockerRegistry, - EnvVar: "REG_REGISTRY", - }, cli.StringFlag{ Name: "timeout", Value: "1m", @@ -90,33 +78,36 @@ func main() { return } - auth, err = repoutils.GetAuthConfig(c.GlobalString("username"), c.GlobalString("password"), c.GlobalString("registry")) - if err != nil { - return err - } - - // Prevent non-ssl unless explicitly forced - if !c.GlobalBool("force-non-ssl") && strings.HasPrefix(auth.ServerAddress, "http:") { - return fmt.Errorf("Attempt to use insecure protocol! Use non-ssl option to force") - } - - // Parse the timeout. - timeout, err := time.ParseDuration(c.GlobalString("timeout")) - if err != nil { - return fmt.Errorf("parsing %s as duration failed: %v", c.GlobalString("timeout"), err) - } - - // Create the registry client. - r, err = registry.New(auth, registry.Opt{ - Insecure: c.GlobalBool("insecure"), - Debug: c.GlobalBool("debug"), - SkipPing: c.GlobalBool("skip-ping"), - Timeout: timeout, - }) - return err + return } if err := app.Run(os.Args); err != nil { logrus.Fatal(err) } } + +func createRegistryClient(c *cli.Context, domain string) (*registry.Registry, error) { + auth, err := repoutils.GetAuthConfig(c.GlobalString("username"), c.GlobalString("password"), domain) + if err != nil { + return nil, err + } + + // Prevent non-ssl unless explicitly forced + if !c.GlobalBool("force-non-ssl") && strings.HasPrefix(auth.ServerAddress, "http:") { + return nil, fmt.Errorf("Attempt to use insecure protocol! Use non-ssl option to force") + } + + // Parse the timeout. + timeout, err := time.ParseDuration(c.GlobalString("timeout")) + if err != nil { + return nil, fmt.Errorf("parsing %s as duration failed: %v", c.GlobalString("timeout"), err) + } + + // Create the registry client. + return registry.New(auth, registry.Opt{ + Insecure: c.GlobalBool("insecure"), + Debug: c.GlobalBool("debug"), + SkipPing: c.GlobalBool("skip-ping"), + Timeout: timeout, + }) +} diff --git a/main_test.go b/main_test.go index 97ffde28..84502d97 100644 --- a/main_test.go +++ b/main_test.go @@ -13,6 +13,10 @@ import ( "github.com/genuinetools/reg/testutils" ) +const ( + domain = "localhost:5000" +) + var ( exeSuffix string // ".exe" on Windows @@ -103,8 +107,9 @@ func TestMain(m *testing.M) { func run(args ...string) (string, error) { prog := "./testreg" + exeSuffix // always add trust insecure, and the registry - newargs := append([]string{"-d", "-k", "-r", "localhost:5000"}, args...) + newargs := append([]string{"-d", "-k"}, args...) cmd := exec.Command(prog, newargs...) + cmd.Env = []string{"REG_REGISTRY=localhost:5000"} out, err := cmd.CombinedOutput() return string(out), err } diff --git a/manifest.go b/manifest.go index 172b0cc6..46f50560 100644 --- a/manifest.go +++ b/manifest.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/genuinetools/reg/repoutils" + "github.com/genuinetools/reg/registry" "github.com/urfave/cli" ) @@ -22,7 +22,13 @@ var manifestCommand = cli.Command{ return fmt.Errorf("pass the name of the repository") } - repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0]) + image, err := registry.ParseImage(c.Args().First()) + if err != nil { + return err + } + + // Create the registry client. + r, err := createRegistryClient(c, image.Domain) if err != nil { return err } @@ -30,13 +36,13 @@ var manifestCommand = cli.Command{ var manifest interface{} if c.Bool("v1") { // Get the v1 manifest if it was explicitly asked for. - manifest, err = r.ManifestV1(repo, ref) + manifest, err = r.ManifestV1(image.Path, image.Reference()) if err != nil { return err } } else { // Get the v2 manifest. - manifest, err = r.Manifest(repo, ref) + manifest, err = r.Manifest(image.Path, image.Reference()) if err != nil { return err } diff --git a/manifest_test.go b/manifest_test.go new file mode 100644 index 00000000..3f06263c --- /dev/null +++ b/manifest_test.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "strings" + "testing" +) + +func TestManifestV2(t *testing.T) { + out, err := run("manifest", fmt.Sprintf("%s/busybox", domain)) + if err != nil { + t.Fatalf("output: %s, error: %v", out, err) + } + + expected := `"schemaVersion": 2,` + if !strings.Contains(out, expected) { + t.Fatalf("expected: %s\ngot: %s", expected, out) + } +} + +func TestManifestV1(t *testing.T) { + out, err := run("manifest", "--v1", fmt.Sprintf("%s/busybox", domain)) + if err != nil { + t.Fatalf("output: %s, error: %v", out, err) + } + + expected := `"schemaVersion": 1,` + if !strings.Contains(out, expected) { + t.Fatalf("expected: %s\ngot: %s", expected, out) + } +} diff --git a/registry/delete.go b/registry/delete.go index 210ef90c..838044d8 100644 --- a/registry/delete.go +++ b/registry/delete.go @@ -5,24 +5,12 @@ import ( "net/http" "github.com/docker/distribution/manifest/schema2" - ocd "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" ) -// Delete removes a repository digest or reference from the registry. +// Delete removes a repository digest from the registry. // https://docs.docker.com/registry/spec/api/#deleting-an-image -func (r *Registry) Delete(repository, digest string) error { - // If digest is not valid try resolving it as a reference - if _, err := ocd.Parse(digest); err != nil { - digest, err = r.Digest(repository, digest) - if err != nil { - return err - } - if digest == "" { - return nil - } - } - - // Delete the image. +func (r *Registry) Delete(repository string, digest digest.Digest) (err error) { url := r.url("/v2/%s/manifests/%s", repository, digest) r.Logf("registry.manifests.delete url=%s repository=%s digest=%s", url, repository, digest) diff --git a/registry/digest.go b/registry/digest.go index 4905d959..08bc9618 100644 --- a/registry/digest.go +++ b/registry/digest.go @@ -5,20 +5,26 @@ import ( "net/http" "github.com/docker/distribution/manifest/schema2" + digest "github.com/opencontainers/go-digest" ) -// Digest returns the digest for a repository and reference. -func (r *Registry) Digest(repository, ref string) (string, error) { - url := r.url("/v2/%s/manifests/%s", repository, ref) +// Digest returns the digest for an image. +func (r *Registry) Digest(image Image) (digest.Digest, error) { + if len(image.Digest) > 1 { + // return early if we already have an image digest. + return image.Digest, nil + } + + url := r.url("/v2/%s/manifests/%s", image.Path, image.Tag) r.Logf("registry.manifests.get url=%s repository=%s ref=%s", - url, repository, ref) + url, image.Path, image.Tag) req, err := http.NewRequest("GET", url, nil) if err != nil { return "", err } - req.Header.Set("Accept", schema2.MediaTypeManifest) + req.Header.Set("Accept", schema2.MediaTypeManifest) resp, err := r.Client.Do(req) if err != nil { return "", err @@ -29,6 +35,5 @@ func (r *Registry) Digest(repository, ref string) (string, error) { return "", fmt.Errorf("Got status code: %d", resp.StatusCode) } - digest := resp.Header.Get("Docker-Content-Digest") - return digest, nil + return digest.FromString(resp.Header.Get("Docker-Content-Digest")), nil } diff --git a/registry/image.go b/registry/image.go new file mode 100644 index 00000000..9e1573f7 --- /dev/null +++ b/registry/image.go @@ -0,0 +1,67 @@ +package registry + +import ( + "fmt" + + "github.com/docker/distribution/reference" + digest "github.com/opencontainers/go-digest" +) + +// Image holds information about an image. +type Image struct { + Domain string + Path string + Tag string + Digest digest.Digest + named reference.Named +} + +// String returns the string representation of an image. +func (i Image) String() string { + return i.named.String() +} + +// Reference returns either the digest if it is non-empty or the tag for the image. +func (i Image) Reference() string { + if len(i.Digest.String()) > 1 { + return i.Digest.String() + } + + return i.Tag +} + +// WithDigest sets the digest for an image. +func (i *Image) WithDigest(digest digest.Digest) (err error) { + i.Digest = digest + i.named, err = reference.WithDigest(i.named, digest) + return err +} + +// ParseImage returns an Image struct with all the values filled in for a given image. +func ParseImage(image string) (Image, error) { + // Parse the image name and tag. + named, err := reference.ParseNormalizedNamed(image) + if err != nil { + return Image{}, fmt.Errorf("parsing image %q failed: %v", image, err) + } + // Add the latest lag if they did not provide one. + named = reference.TagNameOnly(named) + + i := Image{ + named: named, + Domain: reference.Domain(named), + Path: reference.Path(named), + } + + // Add the tag if there was one. + if tagged, ok := named.(reference.Tagged); ok { + i.Tag = tagged.Tag() + } + + // Add the digest if there was one. + if canonical, ok := named.(reference.Canonical); ok { + i.Digest = canonical.Digest() + } + + return i, nil +} diff --git a/registry/manifest.go b/registry/manifest.go index 11630d3c..176f10df 100644 --- a/registry/manifest.go +++ b/registry/manifest.go @@ -87,17 +87,17 @@ func (r *Registry) ManifestV1(repository, ref string) (schema1.SignedManifest, e return m, nil } +// PutManifest calls a PUT for the specific manifest for an image. func (r *Registry) PutManifest(repository, ref string, manifest distribution.Manifest) error { url := r.url("/v2/%s/manifests/%s", repository, ref) r.Logf("registry.manifest.put url=%s repository=%s reference=%s", url, repository, ref) - manifestJson, err := json.Marshal(manifest) + b, err := json.Marshal(manifest) if err != nil { return err } - buffer := bytes.NewBuffer(manifestJson) - req, err := http.NewRequest("PUT", url, buffer) + req, err := http.NewRequest("PUT", url, bytes.NewBuffer(b)) if err != nil { return err } diff --git a/tags.go b/tags.go index 889cabf5..463636a7 100644 --- a/tags.go +++ b/tags.go @@ -2,8 +2,10 @@ package main import ( "fmt" + "sort" "strings" + "github.com/genuinetools/reg/registry" "github.com/urfave/cli" ) @@ -15,11 +17,23 @@ var tagsCommand = cli.Command{ return fmt.Errorf("pass the name of the repository") } - tags, err := r.Tags(c.Args()[0]) + image, err := registry.ParseImage(c.Args().First()) if err != nil { return err } + // Create the registry client. + r, err := createRegistryClient(c, image.Domain) + if err != nil { + return err + } + + tags, err := r.Tags(image.Path) + if err != nil { + return err + } + sort.Strings(tags) + // Print the tags. fmt.Println(strings.Join(tags, "\n")) diff --git a/tags_test.go b/tags_test.go index def0e5f2..53b4d469 100644 --- a/tags_test.go +++ b/tags_test.go @@ -1,19 +1,21 @@ package main import ( + "fmt" "strings" "testing" ) func TestTags(t *testing.T) { - out, err := run("tags", "busybox") + out, err := run("tags", fmt.Sprintf("%s/busybox", domain)) if err != nil { - t.Fatalf("output: %s, error: %v", string(out), err) + t.Fatalf("output: %s, error: %v", out, err) } expected := `glibc +latest musl ` if !strings.HasSuffix(out, expected) { - t.Logf("expected: %s\ngot: %s", expected, out) + t.Fatalf("expected: %s\ngot: %s", expected, out) } } diff --git a/vulns.go b/vulns.go index ff67e62e..ac1db619 100644 --- a/vulns.go +++ b/vulns.go @@ -6,7 +6,7 @@ import ( "time" "github.com/genuinetools/reg/clair" - "github.com/genuinetools/reg/repoutils" + "github.com/genuinetools/reg/registry" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -38,7 +38,13 @@ var vulnsCommand = cli.Command{ return fmt.Errorf("pass the name of the repository") } - repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0]) + image, err := registry.ParseImage(c.Args().First()) + if err != nil { + return err + } + + // Create the registry client. + r, err := createRegistryClient(c, image.Domain) if err != nil { return err } @@ -60,7 +66,7 @@ var vulnsCommand = cli.Command{ } // Get the vulnerability report. - report, err := cr.Vulnerabilities(r, repo, ref) + report, err := cr.Vulnerabilities(r, image.Path, image.Reference()) if err != nil { return err } diff --git a/vulns_test.go b/vulns_test.go index 613c30e0..22b1c6de 100644 --- a/vulns_test.go +++ b/vulns_test.go @@ -1,18 +1,19 @@ package main import ( + "fmt" "strings" "testing" ) func TestVulns(t *testing.T) { - out, err := run("vulns", "--clair", "http://localhost:6060", "alpine:3.5") + out, err := run("vulns", "--clair", "http://localhost:6060", fmt.Sprintf("%s/alpine:3.5", domain)) if err != nil { - t.Fatalf("output: %s, error: %v", string(out), err) + t.Fatalf("output: %s, error: %v", out, err) } expected := `clair.clair resp.Status=200 OK` if !strings.HasSuffix(strings.TrimSpace(out), expected) { - t.Logf("expected: %s\ngot: %s", expected, out) + t.Fatalf("expected: %s\ngot: %s", expected, out) } }