diff --git a/contrib/analyze-local-images/main.go b/contrib/analyze-local-images/main.go index b8f609b1..167a8d26 100644 --- a/contrib/analyze-local-images/main.go +++ b/contrib/analyze-local-images/main.go @@ -45,10 +45,10 @@ const ( ) var ( - endpoint = flag.String("endpoint", "http://127.0.0.1:6060", "Address to Clair API") - myAddress = flag.String("my-address", "127.0.0.1", "Address from the point of view of Clair") - minimumSeverity = flag.String("minimum-severity", "Negligible", "Minimum severity of vulnerabilities to show (Unknown, Negligible, Low, Medium, High, Critical, Defcon1)") - colorMode = flag.String("color", "auto", "Colorize the output (always, auto, never)") + flagEndpoint = flag.String("endpoint", "http://127.0.0.1:6060", "Address to Clair API") + flagMyAddress = flag.String("my-address", "127.0.0.1", "Address from the point of view of Clair") + flagMinimumSeverity = flag.String("minimum-severity", "Negligible", "Minimum severity of vulnerabilities to show (Unknown, Negligible, Low, Medium, High, Critical, Defcon1)") + flagColorMode = flag.String("color", "auto", "Colorize the output (always, auto, never)") ) type vulnerabilityInfo struct { @@ -98,25 +98,31 @@ func main() { } imageName := flag.Args()[0] - minSeverity := types.Priority(*minimumSeverity) + minSeverity := types.Priority(*flagMinimumSeverity) if !minSeverity.IsValid() { flag.Usage() os.Exit(1) } - if *colorMode == "never" { + if *flagColorMode == "never" { color.NoColor = true - } else if *colorMode == "always" { + } else if *flagColorMode == "always" { color.NoColor = false } + err := AnalyzeLocalImage(imageName, minSeverity, *flagEndpoint, *flagMyAddress) + if err != nil { + log.Fatal(err) + } +} + +func AnalyzeLocalImage(imageName string, minSeverity types.Priority, endpoint, myAddress string) error { // Save image. log.Printf("Saving %s to local disk (this may take some time)", imageName) path, err := save(imageName) defer os.RemoveAll(path) if err != nil { - fmt.Printf("Could not save image: %s\n", err) - os.Exit(1) + return fmt.Errorf("Could not save image: %s", err) } // Retrieve history. @@ -126,22 +132,29 @@ func main() { layerIDs, err = historyFromCommand(imageName) } if err != nil || len(layerIDs) == 0 { - log.Printf("Could not get image's history: %s\n", err) - os.Exit(1) + return fmt.Errorf("Could not get image's history: %s", err) } // Setup a simple HTTP server if Clair is not local. - if !strings.Contains(*endpoint, "127.0.0.1") && !strings.Contains(*endpoint, "localhost") { - allowedHost := strings.TrimPrefix(*endpoint, "http://") + if !strings.Contains(endpoint, "127.0.0.1") && !strings.Contains(endpoint, "localhost") { + allowedHost := strings.TrimPrefix(endpoint, "http://") portIndex := strings.Index(allowedHost, ":") if portIndex >= 0 { allowedHost = allowedHost[:portIndex] } - go listenHTTP(path, allowedHost) + log.Printf("Setting up HTTP server (allowing: %s)\n", allowedHost) + + ch := make(chan error) + go listenHTTP(path, allowedHost, ch) + select { + case err := <-ch: + return fmt.Errorf("An error occured when starting HTTP server: %s", err) + case <-time.After(100 * time.Millisecond): + break + } - path = "http://" + *myAddress + ":" + strconv.Itoa(httpPort) - time.Sleep(200 * time.Millisecond) + path = "http://" + myAddress + ":" + strconv.Itoa(httpPort) } // Analyze layers. @@ -151,22 +164,20 @@ func main() { var err error if i > 0 { - err = analyzeLayer(*endpoint, path+"/"+layerIDs[i]+"/layer.tar", layerIDs[i], layerIDs[i-1]) + err = analyzeLayer(endpoint, path+"/"+layerIDs[i]+"/layer.tar", layerIDs[i], layerIDs[i-1]) } else { - err = analyzeLayer(*endpoint, path+"/"+layerIDs[i]+"/layer.tar", layerIDs[i], "") + err = analyzeLayer(endpoint, path+"/"+layerIDs[i]+"/layer.tar", layerIDs[i], "") } if err != nil { - log.Printf("Could not analyze layer: %s\n", err) - os.Exit(1) + return fmt.Errorf("Could not analyze layer: %s", err) } } // Get vulnerabilities. log.Println("Retrieving image's vulnerabilities") - layer, err := getLayer(*endpoint, layerIDs[len(layerIDs)-1]) + layer, err := getLayer(endpoint, layerIDs[len(layerIDs)-1]) if err != nil { - log.Printf("Could not get layer information: %s\n", err) - os.Exit(1) + return fmt.Errorf("Could not get layer information: %s", err) } // Print report. @@ -174,7 +185,7 @@ func main() { if len(layer.Features) == 0 { fmt.Printf("%s No features have been detected in the image. This usually means that the image isn't supported by Clair.\n", color.YellowString("NOTE:")) - os.Exit(0) + return nil } isSafe := true @@ -231,11 +242,11 @@ func main() { if isSafe { fmt.Printf("%s No vulnerabilities were detected in your image\n", color.GreenString("Success!")) - os.Exit(0) } else if !hasVisibleVulnerabilities { fmt.Printf("%s No vulnerabilities matching the minimum severity level were detected in your image\n", color.YellowString("NOTE:")) - os.Exit(0) } + + return nil } func save(imageName string) (string, error) { @@ -330,9 +341,7 @@ func historyFromCommand(imageName string) ([]string, error) { return layers, nil } -func listenHTTP(path, allowedHost string) { - log.Printf("Setting up HTTP server (allowing: %s)\n", allowedHost) - +func listenHTTP(path, allowedHost string, ch chan error) { restrictedFileServer := func(path, allowedHost string) http.Handler { fc := func(w http.ResponseWriter, r *http.Request) { host, _, err := net.SplitHostPort(r.RemoteAddr) @@ -345,11 +354,7 @@ func listenHTTP(path, allowedHost string) { return http.HandlerFunc(fc) } - err := http.ListenAndServe(":"+strconv.Itoa(httpPort), restrictedFileServer(path, allowedHost)) - if err != nil { - log.Printf("An error occurs with the HTTP server: %s\n", err) - os.Exit(1) - } + ch <- http.ListenAndServe(":"+strconv.Itoa(httpPort), restrictedFileServer(path, allowedHost)) } func analyzeLayer(endpoint, path, layerName, parentLayerName string) error {