1

I'm trying to use the govmomi library (https://github.com/vmware/govmomi) to make some automated changes to a VM but I can't seem to get around the nil pointer exceptions on line 134 and 136 for the last few hours. I got it to work at one point but then I'm not sure what I'm doing wrong now as I've tried so many combos but nothing seems to work now...

package main

import (
    "context"
    "flag"
    "github.com/vmware/govmomi"
    "github.com/vmware/govmomi/vim25"
    "github.com/vmware/govmomi/vim25/soap"
    "net/url"
    "os"
    "strings"

    "fmt"

    "github.com/vmware/govmomi/govc/flags"

    "github.com/vmware/govmomi/vim25/types"
    "log"
)


type change struct {
    *flags.VirtualMachineFlag
}

const (
    envURL      = "GOVC_URL"
    envUserName = "GOVC_USERNAME"
    envPassword = "GOVC_PASSWORD"
    envInsecure = "GOVC_INSECURE"
)

// getEnvString returns string from environment variable.
func getEnvString(v string, def string) string {
    r := os.Getenv(v)
    if r == "" {
        return def
    }

    return r
}

// getEnvBool returns boolean from environment variable.
func getEnvBool(v string, def bool) bool {
    r := os.Getenv(v)
    if r == "" {
        return def
    }

    switch strings.ToLower(r[0:1]) {
    case "t", "y", "1":
        return true
    }

    return false
}

var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", getEnvString(envURL, "https://username:password@host"+vim25.Path), urlDescription)

var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", getEnvBool(envInsecure, false), insecureDescription)

func processOverride(u *url.URL) {
    envUsername := os.Getenv(envUserName)
    envPassword := os.Getenv(envPassword)

    // Override username if provided
    if envUsername != "" {
        var password string
        var ok bool

        if u.User != nil {
            password, ok = u.User.Password()
        }

        if ok {
            u.User = url.UserPassword(envUsername, password)
        } else {
            u.User = url.User(envUsername)
        }
    }

    // Override password if provided
    if envPassword != "" {
        var username string

        if u.User != nil {
            username = u.User.Username()
        }

        u.User = url.UserPassword(username, envPassword)
    }
}

func NewClient(ctx context.Context) (*govmomi.Client, error) {
    flag.Parse()

    // Parse URL from string
    u, err := soap.ParseURL(*urlFlag)
    if err != nil {
        return nil, err
    }

    // Override username and/or password as required
    processOverride(u)

    // Connect and log in to ESX or vCenter
    return govmomi.NewClient(ctx, u, *insecureFlag)
}

func main() {

    ctx := context.Background()

    // Connect and login to ESX or vCenter
    c, err := NewClient(ctx)
    if err != nil {
        log.Fatal(err)
    }

    defer c.Logout(ctx)

    var spec *types.VirtualMachineConfigSpec

    var cmd *change

    var flip bool

    flip = false

    spec.VPMCEnabled = &flip

    vm, err := cmd.VirtualMachine()

    task, err := vm.Reconfigure(ctx, *spec)
    if err != nil {
        println(err)
    }

    fmt.Println(task.Wait(ctx))
}

the 2 lines that are throwing errors are:

spec.VPMCEnabled = &flip

and

vm, err := cmd.VirtualMachine()

both seem to throw the same panic: runtime error: invalid memory address or nil pointer dereference. If I comment out the first one throwing the error, the second one throws it then.

I think both are unrelated but I can't quite figure how how to do the dereferencing correctly.

----UPDATE EDIT 1--------------

I made some changes to this with the below response but still can't get around the error on at task, err := vm.Reconfigure(ctx, *spec)...

package main

import (
    "context"
    "flag"
    "github.com/vmware/govmomi"
    "github.com/vmware/govmomi/object"
    "github.com/vmware/govmomi/vim25"
    "github.com/vmware/govmomi/vim25/soap"
    "net/url"
    "os"
    "strings"

    "fmt"

    "github.com/vmware/govmomi/vim25/types"
    "log"
)


const (
    envURL      = "GOVC_URL"
    envUserName = "GOVC_USERNAME"
    envPassword = "GOVC_PASSWORD"
    envInsecure = "GOVC_INSECURE"
)

// getEnvString returns string from environment variable.
func getEnvString(v string, def string) string {
    r := os.Getenv(v)
    if r == "" {
        return def
    }

    return r
}

// getEnvBool returns boolean from environment variable.
func getEnvBool(v string, def bool) bool {
    r := os.Getenv(v)
    if r == "" {
        return def
    }

    switch strings.ToLower(r[0:1]) {
    case "t", "y", "1":
        return true
    }

    return false
}

var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", getEnvString(envURL, "https://username:password@host"+vim25.Path), urlDescription)

var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", getEnvBool(envInsecure, false), insecureDescription)

func processOverride(u *url.URL) {
    envUsername := os.Getenv(envUserName)
    envPassword := os.Getenv(envPassword)

    // Override username if provided
    if envUsername != "" {
        var password string
        var ok bool

        if u.User != nil {
            password, ok = u.User.Password()
        }

        if ok {
            u.User = url.UserPassword(envUsername, password)
        } else {
            u.User = url.User(envUsername)
        }
    }

    // Override password if provided
    if envPassword != "" {
        var username string

        if u.User != nil {
            username = u.User.Username()
        }

        u.User = url.UserPassword(username, envPassword)
    }
}

func NewClient(ctx context.Context) (*govmomi.Client, error) {
    flag.Parse()

    // Parse URL from string
    u, err := soap.ParseURL(*urlFlag)
    if err != nil {
        return nil, err
    }

    // Override username and/or password as required
    processOverride(u)

    // Connect and log in to ESX or vCenter
    return govmomi.NewClient(ctx, u, *insecureFlag)
}

func main() {

    ctx := context.Background()

    // Connect and login to ESX or vCenter
    c, err := NewClient(ctx)
    if err != nil {
        log.Fatal(err)
    }

    defer c.Logout(ctx)

    var spec *types.VirtualMachineConfigSpec
    spec = new(types.VirtualMachineConfigSpec)


    var flip bool

    flip = false

    spec.VPMCEnabled = &flip

    var vm *object.VirtualMachine
    vm = new(object.VirtualMachine)


    task, err := vm.Reconfigure(ctx, *spec)
    if err != nil {
        println(err)
    }

    fmt.Println(task.Wait(ctx))

}

the full error I'm getting is this:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x280 pc=0x14e18f6]

goroutine 1 [running]:
github.com/vmware/govmomi/vim25.(*Client).RoundTrip(0x0, 0x1886f00, 0xc000016088, 0x1884140, 0xc000298720, 0x1884140, 0xc000298740, 0x300, 0x16e8ac0)
        /Users/ronakpatel/go/src/github.com/vmware/govmomi/vim25/client.go:89 +0x26
github.com/vmware/govmomi/vim25/methods.ReconfigVM_Task(0x1886f00, 0xc000016088, 0x1884060, 0x0, 0xc0002d0000, 0xc000288000, 0xc000080400, 0x0)
        /Users/ronakpatel/go/src/github.com/vmware/govmomi/vim25/methods/methods.go:10879 +0xb8
github.com/vmware/govmomi/object.VirtualMachine.Reconfigure(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1886f00, 0xc000016088, 0x0, ...)
        /Users/ronakpatel/go/src/github.com/vmware/govmomi/object/virtual_machine.go:207 +0x19b
main.main()
        /Users/ronakpatel/go/src/awesomeProject1/main.go:143 +0x1ec
exit status 2

----UPDATE EDIT 2----------

I changed somethings around and used the answer provided below but now I'm getting the error again but at this part: task, err := vm.Reconfigure(ctx, spec1)

Error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x153d05a]

goroutine 1 [running]:
main.main()
        /Users/ronakpatel/go/src/awesomeProject1/main.go:134 +0x19a
exit status 2

Code:

spec1 := types.VirtualMachineConfigSpec{
    VPMCEnabled: &[]bool{false}[0],
}

var vm *object.VirtualMachine

var vmf *flags.VirtualMachineFlag

vmf, ctx = flags.NewVirtualMachineFlag(ctx)

vm, _ = vmf.VirtualMachine()

task, err := vm.Reconfigure(ctx, spec1)
if err != nil {
    println(err)
}

fmt.Println(task.Wait(ctx))
lightweight
  • 3,227
  • 14
  • 79
  • 142
  • `vm, err := cmd.VirtualMachine()` Check the error before you use `vm`. – mkopriva Mar 28 '19 at 06:49
  • it doesn't get that far though cause of point exception.... – lightweight Mar 28 '19 at 06:52
  • Well then where does it get to? Saying "134 and 136" when the line i highlighted is on 133 in the given example code (+1 line for the missing package statement) doesn't help. Just provide accurate info if you want accurate help. – mkopriva Mar 28 '19 at 06:55
  • You should also provide the relevant part of the error that you're getting – mkopriva Mar 28 '19 at 06:56
  • ah didn't see that....just made an edit to it to correct it on the lines....it stops at 134 and when i comment that out...it stops at 136....both with these errors: `panic: runtime error: invalid memory address or nil pointer dereference` – lightweight Mar 28 '19 at 06:58
  • still that doesn't compute, there's nothing on line 134 and line 136 cannot cause that kind of error. See line numbers here (https://play.golang.org/p/HV8yJewz8mM) – mkopriva Mar 28 '19 at 06:59
  • That is not the relevant part of the error, show the stack that lists the files, lines, and functions that cause the error. And please don't put that into a comment but update the question instead. – mkopriva Mar 28 '19 at 07:00
  • It is highly probable that the package you're using provides proper constructors for the types you keep initializing with `new`. Look at that package's documentation and especially at the documentation for the types you need and see if there aren't any `NewXxx` functions to support those types. Like for example `NewVirtualMachine`, `NewVirtualMachineConfigSpec` etc, if there are then use them instead of `new`. Also a lot of packages provide examples in the docs on how to properly initialize the types that those packages declare. – mkopriva Mar 28 '19 at 07:40
  • ... https://godoc.org/github.com/vmware/govmomi/object#NewVirtualMachine – mkopriva Mar 28 '19 at 07:45

1 Answers1

3
    var spec *types.VirtualMachineConfigSpec // error: spec is nil pointer
    spec = new(types.VirtualMachineConfigSpec) // fix: spec is not nil

    var cmd *change

    var flip bool

    flip = false

    // used here
    spec.VPMCEnabled = &flip

    vm, err := cmd.VirtualMachine()
beiping96
  • 644
  • 3
  • 12
  • thanks! got my closer but this part is still throwing the same error: `vm, err := cmd.VirtualMachine()` – lightweight Mar 28 '19 at 07:12
  • 1
    @lightweight when you declare a pointer variable, without initializing it, it will be nil, calling a method on such a variable will almost always get you that error. Whatever you use in your code, just make sure it's initialized. – mkopriva Mar 28 '19 at 07:33
  • @lightweight ok, the `*flags.VirtualMachineFlag` in `*change` is still nil pointer – beiping96 Mar 28 '19 at 09:48
  • @lightweight https://godoc.org/github.com/vmware/govmomi/govc/flags#NewVirtualMachineFlag you can declare `*flags.VirtualMachineFlag` follow the doc – beiping96 Mar 28 '19 at 09:52
  • ah thanks! so I made some changes you stated and also cleaned up some stuff (I think), but now I'm getting that same error at this now `task, err := vm.Reconfigure(ctx, spec1)` :-( ....also, thanks for being patience.... – lightweight Mar 28 '19 at 14:55
  • @lightweight guess check the error in `vm, _ = vmf.VirtualMachine()` will be helpful – beiping96 Apr 01 '19 at 01:25