Go: panic: runtime error: invalid memory address or nil pointer dereference
go
Solution
According to the docs for `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."
Then looking at this code:
res, err := client.Do(req)
defer res.Body.Close()
if err != nil {
return nil, err
}
I'm guessing that `err` is not `nil`. You're accessing the `.Close()` method on `res.Body` before you check for the `err`.
The `defer` only defers the function call. The field and method are accessed immediately.
So instead, try checking the error immediately.
res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
Problem
When running my Go program, it panics and returns the following: ``` 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 ``` I've looked at the responses others have had to the same exception, but can't see anything simple (i.e. an unhandled error). I am running it on a machine that does not have access to the API servers listed in the code, but I was hoping it'd return an appropriate error (as I've attempted to catch errors of that kind). ``` 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 } ```