Go: panique: erreur d'exécution: adresse mémoire invalide ou déréférence de pointeur nul

95

Lors de l'exécution de mon programme Go, il panique et renvoie ce qui suit:

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

J'ai regardé les réponses que d'autres ont eues à la même exception, mais je ne vois rien de simple (c'est-à-dire une erreur non gérée).

Je l'exécute sur une machine qui n'a pas accès aux serveurs d'API répertoriés dans le code, mais j'espérais qu'elle renverrait une erreur appropriée (car j'ai tenté de détecter des erreurs de ce type).

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
}
élithrar
la source

Réponses:

118

Selon la documentation pour func (*Client) Do:

"Une erreur est renvoyée si elle est causée par la stratégie client (telle que CheckRedirect) ou s'il y a eu une erreur de protocole HTTP. Une réponse non-2xx ne provoque pas d'erreur.

Quand err est nul, resp contient toujours un resp.Body non nul. "

Puis en regardant ce code:

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

if err != nil {
    return nil, err
}

Je suppose que errnon nil. Vous accédez à la .Close()méthode res.Bodyavant de vérifier le fichier err.

Le deferseul reporte l'appel de fonction. Le champ et la méthode sont accessibles immédiatement.


Essayez donc de vérifier l'erreur immédiatement.

res, err := client.Do(req)

if err != nil {
    return nil, err
}
defer res.Body.Close()
utilisateur1106925
la source
1
Parfait! déplacer différer après la vérification d'erreur l'a résolu.
Melvin
si err! = nil, res.Body = nil, pourquoi res.Body.Close () peut être appelé?
oohcode
2
@oohcode if err! = nil alors res.Body.Close () n'est jamais appelé, car il y a une instruction return dans le bloc if.
giraffe.guru
12

Le déréférencement du pointeur nul est à la ligne 65 qui est le différé dans

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

if err != nil {
    return nil, err
}

Si err! = Nil alors res == nil et res.Body panique. Gérez l'erreur avant de différer le res.Body.Close ().

Volker
la source
3

Puisque je suis arrivé ici avec mon problème, j'ajouterai cette réponse bien qu'elle ne soit pas exactement pertinente par rapport à la question initiale. Lorsque vous implémentez une interface, assurez-vous de ne pas oublier d'ajouter le pointeur de type sur vos déclarations de fonction membre. Exemple:

type AnimalSounder interface {
    MakeNoise()
}

type Dog struct {
    Name string
    mean bool
    BarkStrength int
}

func (dog *Dog) MakeNoise() {
    //implementation
}

J'ai oublié la partie * (chien chien) , je ne le recommande pas. Ensuite, vous rencontrez de vilains problèmes lors de l'appel de MakeNoice sur une variable d'interface AnimalSounder de type Dog.

Bjarki B
la source
-15

pour moi, une solution à ce problème était d'ajouter sql.Open ... sslmode = disable

Kaxa
la source