From 8a400d0c4f91bcd1fef8da38dfab00d4c85603e3 Mon Sep 17 00:00:00 2001 From: Laura Brehm Date: Fri, 25 Aug 2023 20:15:42 +0700 Subject: [PATCH] scout hint: tell user logging in is required (#2262) The `docker scout quickview` hint, displayed after a user pulls or builds an image, only works if the user is logged in to Hub. Check if user isn't logged in, and make hint more explicit in this case. Signed-off-by: Laura Brehm --- cli/mobycli/scout_suggest.go | 39 ++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/cli/mobycli/scout_suggest.go b/cli/mobycli/scout_suggest.go index b6f32eb07..6e798b7da 100644 --- a/cli/mobycli/scout_suggest.go +++ b/cli/mobycli/scout_suggest.go @@ -20,8 +20,11 @@ import ( "fmt" "os" "strings" + "time" + "github.com/docker/cli/cli/config" "github.com/docker/compose/v2/pkg/utils" + "github.com/docker/docker/registry" "github.com/fatih/color" ) @@ -53,10 +56,14 @@ func displayScoutQuickViewSuggestMsg(image string) { } out := os.Stderr b := color.New(color.Bold) - _, _ = fmt.Fprintln(out) - _, _ = b.Fprintln(out, "What's Next?") - _, _ = fmt.Fprintf(out, " View summary of image vulnerabilities and recommendations → %s", color.CyanString("docker scout quickview%s", image)) - _, _ = fmt.Fprintln(out) + + _, _ = b.Fprintln(out, "\nWhat's Next?") + if !hubLoggedIn() { + _, _ = fmt.Fprintln(out, " 1. Sign in to your Docker account → "+color.CyanString("docker login")) + _, _ = fmt.Fprintln(out, " 2. View a summary of image vulnerabilities and recommendations → "+color.CyanString("docker scout quickview"+image)) + } else { + _, _ = fmt.Fprintln(out, " View a summary of image vulnerabilities and recommendations → "+color.CyanString("docker scout quickview"+image)) + } } func pulledImageFromArgs(args []string) string { @@ -74,3 +81,27 @@ func pulledImageFromArgs(args []string) string { } return image } + +// hubLoggedIn checks whether the user has credentials configured +// for Docker Hub. If it fails to get a result within 100ms, it +// short-circuits and returns `true`. +// This can be an expensive operation, so use it mindfully. +func hubLoggedIn() bool { + result := make(chan bool) + go func() { + creds, err := config.LoadDefaultConfigFile(nil).GetAllCredentials() + if err != nil { + // preserve original behaviour if we fail to fetch creds + result <- true + } + _, ok := creds[registry.IndexServer] + result <- ok + }() + select { + case loggedIn := <-result: + return loggedIn + case <-time.After(100 * time.Millisecond): + // preserve original behaviour if we time out + return true + } +}