2013-04-29 15 views
44

Quando si esegue il mio programma Go, si lascia prendere dal panico e restituisce il seguente:Go: il panico: Errore di runtime: indirizzo di memoria non valida o un puntatore nullo dereference

panic: runtime error: invalid memory address or nil pointer dereference 
[signal 0xb code=0x1 addr=0x38 pc=0x26df] 

goroutine 1 [running]: 
main.getBody(0x1cdcd4, 0xf800000004, 0x1f2b44, 0x23, 0xf84005c800, ...) 
     /Users/matt/Dropbox/code/go/scripts/cron/fido.go:65 +0x2bb 
main.getToken(0xf84005c7e0, 0x10) 
     /Users/matt/Dropbox/code/go/scripts/cron/fido.go:140 +0x156 
main.main() 
     /Users/matt/Dropbox/code/go/scripts/cron/fido.go:178 +0x61 

goroutine 2 [syscall]: 
created by runtime.main 
     /usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:221 

goroutine 3 [syscall]: 
syscall.Syscall6() 
     /usr/local/Cellar/go/1.0.3/src/pkg/syscall/asm_darwin_amd64.s:38 +0x5 
syscall.kevent(0x6, 0x0, 0x0, 0xf840085188, 0xa, ...) 
     /usr/local/Cellar/go/1.0.3/src/pkg/syscall/zsyscall_darwin_amd64.go:199 +0x88 
syscall.Kevent(0xf800000006, 0x0, 0x0, 0xf840085188, 0xa0000000a, ...) 
     /usr/local/Cellar/go/1.0.3/src/pkg/syscall/syscall_bsd.go:546 +0xa4 
net.(*pollster).WaitFD(0xf840085180, 0xf840059040, 0x0, 0x0, 0x0, ...) 
     /usr/local/Cellar/go/1.0.3/src/pkg/net/fd_darwin.go:96 +0x185 
net.(*pollServer).Run(0xf840059040, 0x0) 
     /usr/local/Cellar/go/1.0.3/src/pkg/net/fd.go:236 +0xe4 
created by net.newPollServer 
     /usr/local/Cellar/go/1.0.3/src/pkg/net/newpollserver.go:35 +0x382 

Ho guardato le risposte che altri hanno avuto lo stesso eccezione, ma non può vedere nulla di semplice (cioè un errore non gestito).

Lo sto eseguendo su una macchina che non ha accesso ai server API elencati nel codice, ma speravo che avrebbe restituito un errore appropriato (come ho tentato di rilevare errori di questo tipo).

package main 

/* 
Fido fetches the list of public images from the Glance server, captures the IDs of images with 'status': 'active' and then queues the images for pre-fetching with the Glance CLI utility `glance-cache-manage`. Once the images are added to the queue, `glance-cache-prefetcher` is called to actively fetch the queued images into the local compute nodes' image cache. 

See http://docs.openstack.org/developer/glance/cache.html for further details on the Glance image cache. 
*/ 

import (
    "bytes" 
    "encoding/json" 
    "fmt" 
    "io/ioutil" 
    /* 
     "log" 
     "log/syslog" 
    */ 
    "net/http" 
    "os" 
    "os/exec" 
) 

func prefetchImages() error { 

    cmd := exec.Command("glance-cache-prefetcher") 
    err := cmd.Run() 

    if err != nil { 
     return fmt.Errorf("glance-cache-prefetcher failed to execute properly: %v", err) 
    } 

    return nil 
} 

func queueImages(hostname string, imageList []string) error { 

    for _, image := range imageList { 
     cmd := exec.Command("glance-cache-manage", "--host=", hostname, "queue-image", image) 
     err := cmd.Run() 

     if err != nil { 
      return fmt.Errorf("glance-cache-manage failed to execute properly: %v", err) 
     } else { 
      fmt.Printf("Image %s queued", image) 
     } 
    } 

    return nil 
} 

func getBody(method string, url string, headers map[string]string, body []byte) ([]byte, error) { 

    client := &http.Client{} 
    req, err := http.NewRequest(method, url, bytes.NewReader(body)) 

    if err != nil { 
     return nil, err 
    } 

    for key, value := range headers { 
     req.Header.Add(key, value) 
    } 

    res, err := client.Do(req) 
    defer res.Body.Close() 

    if err != nil { 
     return nil, err 
    } 

    var bodyBytes []byte 

    if res.StatusCode == 200 { 
     bodyBytes, err = ioutil.ReadAll(res.Body) 
    } else if err != nil { 
     return nil, err 
    } else { 
     return nil, fmt.Errorf("The remote end did not return a HTTP 200 (OK) response.") 
    } 

    return bodyBytes, nil 

} 

func getImages(authToken string) ([]string, error) { 

    type GlanceDetailResponse struct { 
     Images []struct { 
      Name string `json:"name"` 
      Status string `json:"status"` 
      ID  string `json:"id"` 
     } 
    } 

    method := "GET" 
    url := "http://192.168.1.2:9292/v1.1/images/detail" 
    headers := map[string]string{"X-Auth-Token": authToken} 

    bodyBytes, err := getBody(method, url, headers, nil) 

    if err != nil { 
     return nil, fmt.Errorf("unable to retrieve the response body from the Glance API server: %v", err) 
    } 

    var glance GlanceDetailResponse 
    err = json.Unmarshal(bodyBytes, &glance) 

    if err != nil { 
     return nil, fmt.Errorf("unable to parse the JSON response:", err) 
    } 

    imageList := make([]string, 10) 

    for _, image := range glance.Images { 
     if image.Status == "active" { 
      imageList = append(imageList, image.ID) 
     } 
    } 

    return imageList, nil 

} 

func getToken() (string, error) { 

    type TokenResponse struct { 
     Auth []struct { 
      Token struct { 
       Expires string `json:"expires"` 
       ID  string `json:"id"` 
      } 
     } 
    } 

    method := "POST" 
    url := "http://192.168.1.2:5000/v2.0/tokens" 
    headers := map[string]string{"Content-type": "application/json"} 
    creds := []byte(`{"auth":{"passwordCredentials":{"username": "glance", "password":"<password>"}, "tenantId":"<tenantkeygoeshere>"}}`) 

    bodyBytes, err := getBody(method, url, headers, creds) 

    if err != nil { 
     return "", err 
    } 

    var keystone TokenResponse 
    err = json.Unmarshal(bodyBytes, &keystone) 

    if err != nil { 
     return "", err 
    } 

    authToken := string((keystone.Auth[0].Token.ID)) 

    return authToken, nil 
} 

func main() { 

    /* 
     slog, err := syslog.New(syslog.LOG_ERR, "[fido]") 

     if err != nil { 
      log.Fatalf("unable to connect to syslog: %v", err) 
      os.Exit(1) 
     } else { 
      defer slog.Close() 
     } 
    */ 

    hostname, err := os.Hostname() 

    if err != nil { 
     // slog.Err("Hostname not captured") 
     os.Exit(1) 
    } 

    authToken, err := getToken() 

    if err != nil { 
     // slog.Err("The authentication token from the Glance API server was not retrieved") 
     os.Exit(1) 
    } 

    imageList, err := getImages(authToken) 

    err = queueImages(hostname, imageList) 

    if err != nil { 
     // slog.Err("Could not queue the images for pre-fetching") 
     os.Exit(1) 
    } 

    err = prefetchImages() 

    if err != nil { 
     // slog.Err("Could not queue the images for pre-fetching") 
     os.Exit(1) 
    } 

    return 
} 

risposta

57

Secondo la documentazione per func (*Client) Do:

"An error is returned if caused by client policy (such as CheckRedirect), or if there was an HTTP protocol error. A non-2xx response doesn't cause an error.

When err is nil, resp always contains a non-nil resp.Body."

Poi guardando questo codice:

res, err := client.Do(req) 
defer res.Body.Close() 

if err != nil { 
    return nil, err 
} 

Sto indovinando che non è errnil. Stai per accedere al metodo .Close() su res.Body prima di verificare lo err.

Il defer solleva solo la chiamata di funzione. Il campo e il metodo sono accessibili immediatamente.


Quindi, provare a verificare l'errore immediatamente.

res, err := client.Do(req) 

if err != nil { 
    return nil, err 
} 
defer res.Body.Close() 
+0

Grazie - che corresse. – elithrar

+0

Perfetto! spostando il differimento dopo il controllo degli errori risolto. – Melvin

+0

se err! = Nil, res.Body = nil, perché res.Body.Close() può essere chiamato? – oohcode

4

Il nil puntatore dereferenziamento è in linea 65 che è la rinviare in

res, err := client.Do(req) 
defer res.Body.Close() 

if err != nil { 
    return nil, err 
} 

Se err! = Nil poi res == nil e panico res.Body. Gestire err prima di posticipare il res.Body.Close().

-1

per me uno soluzione per questo problema è stato quello di aggiungere in sql.Open ... sslmode = disabilitare

Problemi correlati