contrib: Use return instead of os.Exit(1) in analyze-local-images

In order to cleanup properly the allocated resources (`defer`).

Fixes #117
This commit is contained in:
Quentin Machu 2016-03-23 15:02:26 -04:00
parent 6883cb9b01
commit 1040dbbff9

View File

@ -45,10 +45,10 @@ const (
) )
var ( var (
endpoint = flag.String("endpoint", "http://127.0.0.1:6060", "Address to Clair API") flagEndpoint = 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") flagMyAddress = 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)") flagMinimumSeverity = 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)") flagColorMode = flag.String("color", "auto", "Colorize the output (always, auto, never)")
) )
type vulnerabilityInfo struct { type vulnerabilityInfo struct {
@ -98,25 +98,31 @@ func main() {
} }
imageName := flag.Args()[0] imageName := flag.Args()[0]
minSeverity := types.Priority(*minimumSeverity) minSeverity := types.Priority(*flagMinimumSeverity)
if !minSeverity.IsValid() { if !minSeverity.IsValid() {
flag.Usage() flag.Usage()
os.Exit(1) os.Exit(1)
} }
if *colorMode == "never" { if *flagColorMode == "never" {
color.NoColor = true color.NoColor = true
} else if *colorMode == "always" { } else if *flagColorMode == "always" {
color.NoColor = false 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. // Save image.
log.Printf("Saving %s to local disk (this may take some time)", imageName) log.Printf("Saving %s to local disk (this may take some time)", imageName)
path, err := save(imageName) path, err := save(imageName)
defer os.RemoveAll(path) defer os.RemoveAll(path)
if err != nil { if err != nil {
fmt.Printf("Could not save image: %s\n", err) return fmt.Errorf("Could not save image: %s", err)
os.Exit(1)
} }
// Retrieve history. // Retrieve history.
@ -126,22 +132,29 @@ func main() {
layerIDs, err = historyFromCommand(imageName) layerIDs, err = historyFromCommand(imageName)
} }
if err != nil || len(layerIDs) == 0 { if err != nil || len(layerIDs) == 0 {
log.Printf("Could not get image's history: %s\n", err) return fmt.Errorf("Could not get image's history: %s", err)
os.Exit(1)
} }
// Setup a simple HTTP server if Clair is not local. // Setup a simple HTTP server if Clair is not local.
if !strings.Contains(*endpoint, "127.0.0.1") && !strings.Contains(*endpoint, "localhost") { if !strings.Contains(endpoint, "127.0.0.1") && !strings.Contains(endpoint, "localhost") {
allowedHost := strings.TrimPrefix(*endpoint, "http://") allowedHost := strings.TrimPrefix(endpoint, "http://")
portIndex := strings.Index(allowedHost, ":") portIndex := strings.Index(allowedHost, ":")
if portIndex >= 0 { if portIndex >= 0 {
allowedHost = allowedHost[:portIndex] allowedHost = allowedHost[:portIndex]
} }
go listenHTTP(path, allowedHost) log.Printf("Setting up HTTP server (allowing: %s)\n", allowedHost)
path = "http://" + *myAddress + ":" + strconv.Itoa(httpPort) ch := make(chan error)
time.Sleep(200 * time.Millisecond) 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)
} }
// Analyze layers. // Analyze layers.
@ -151,22 +164,20 @@ func main() {
var err error var err error
if i > 0 { 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 { } else {
err = analyzeLayer(*endpoint, path+"/"+layerIDs[i]+"/layer.tar", layerIDs[i], "") err = analyzeLayer(endpoint, path+"/"+layerIDs[i]+"/layer.tar", layerIDs[i], "")
} }
if err != nil { if err != nil {
log.Printf("Could not analyze layer: %s\n", err) return fmt.Errorf("Could not analyze layer: %s", err)
os.Exit(1)
} }
} }
// Get vulnerabilities. // Get vulnerabilities.
log.Println("Retrieving image's 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 { if err != nil {
log.Printf("Could not get layer information: %s\n", err) return fmt.Errorf("Could not get layer information: %s", err)
os.Exit(1)
} }
// Print report. // Print report.
@ -174,7 +185,7 @@ func main() {
if len(layer.Features) == 0 { 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:")) 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 isSafe := true
@ -231,11 +242,11 @@ func main() {
if isSafe { if isSafe {
fmt.Printf("%s No vulnerabilities were detected in your image\n", color.GreenString("Success!")) fmt.Printf("%s No vulnerabilities were detected in your image\n", color.GreenString("Success!"))
os.Exit(0)
} else if !hasVisibleVulnerabilities { } else if !hasVisibleVulnerabilities {
fmt.Printf("%s No vulnerabilities matching the minimum severity level were detected in your image\n", color.YellowString("NOTE:")) 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) { func save(imageName string) (string, error) {
@ -330,9 +341,7 @@ func historyFromCommand(imageName string) ([]string, error) {
return layers, nil return layers, nil
} }
func listenHTTP(path, allowedHost string) { func listenHTTP(path, allowedHost string, ch chan error) {
log.Printf("Setting up HTTP server (allowing: %s)\n", allowedHost)
restrictedFileServer := func(path, allowedHost string) http.Handler { restrictedFileServer := func(path, allowedHost string) http.Handler {
fc := func(w http.ResponseWriter, r *http.Request) { fc := func(w http.ResponseWriter, r *http.Request) {
host, _, err := net.SplitHostPort(r.RemoteAddr) host, _, err := net.SplitHostPort(r.RemoteAddr)
@ -345,11 +354,7 @@ func listenHTTP(path, allowedHost string) {
return http.HandlerFunc(fc) return http.HandlerFunc(fc)
} }
err := http.ListenAndServe(":"+strconv.Itoa(httpPort), restrictedFileServer(path, allowedHost)) ch <- 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)
}
} }
func analyzeLayer(endpoint, path, layerName, parentLayerName string) error { func analyzeLayer(endpoint, path, layerName, parentLayerName string) error {