From e23a9dee3d431e02ae22e609a875c15abb959749 Mon Sep 17 00:00:00 2001 From: Johann Queuniet Date: Wed, 25 Oct 2023 17:45:33 +0200 Subject: [PATCH] Add context support --- README.md | 10 +- access.go | 101 ++++++++--------- access_test.go | 76 ++++++++----- cluster.go | 13 ++- cluster_firewall.go | 33 +++--- cluster_test.go | 18 +-- containers.go | 25 +++-- nodes.go | 111 +++++++++--------- nodes_network.go | 33 +++--- nodes_network_test.go | 13 ++- nodes_test.go | 11 +- pools.go | 21 ++-- pools_test.go | 17 +-- proxmox.go | 35 +++--- proxmox_test.go | 14 ++- storage.go | 43 +++---- tasks.go | 41 +++---- tests/integration/cluster_test.go | 17 +-- tests/integration/proxmox_test.go | 16 ++- types.go | 2 + virtual_machine.go | 179 +++++++++++++++--------------- virtual_machine_test.go | 7 +- 22 files changed, 452 insertions(+), 384 deletions(-) diff --git a/README.md b/README.md index d61f5dd..719c9d2 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,9 @@ Create a client and use the public methods to access Proxmox resources. package main import ( + "context" "fmt" + "github.com/luthermonson/go-proxmox" ) @@ -51,7 +53,7 @@ func main() { proxmox.WithCredentials(&credentials), ) - version, err := client.Version() + version, err := client.Version(context.Background()) if err != nil { panic(err) } @@ -64,7 +66,11 @@ func main() { package main import ( + "context" + "crypto/tls" "fmt" + "net/http" + "github.com/luthermonson/go-proxmox" ) @@ -84,7 +90,7 @@ func main() { proxmox.WithAPIToken(tokenID, secret), ) - version, err := client.Version() + version, err := client.Version(context.Background()) if err != nil { panic(err) } diff --git a/access.go b/access.go index 11c1ad2..2225548 100644 --- a/access.go +++ b/access.go @@ -1,14 +1,15 @@ package proxmox import ( + "context" "errors" "fmt" "net/url" ) // Deprecated: Use WithCredentials Option -func (c *Client) Login(username, password string) error { - _, err := c.Ticket(&Credentials{ +func (c *Client) Login(ctx context.Context, username, password string) error { + _, err := c.Ticket(ctx, &Credentials{ Username: username, Password: password, }) @@ -21,20 +22,20 @@ func (c *Client) APIToken(tokenID, secret string) { c.token = fmt.Sprintf("%s=%s", tokenID, secret) } -func (c *Client) Ticket(credentials *Credentials) (*Session, error) { - return c.session, c.Post("/access/ticket", credentials, &c.session) +func (c *Client) Ticket(ctx context.Context, credentials *Credentials) (*Session, error) { + return c.session, c.Post(ctx, "/access/ticket", credentials, &c.session) } -func (c *Client) ACL() (acl ACLs, err error) { - return acl, c.Get("/access/acl", &acl) +func (c *Client) ACL(ctx context.Context) (acl ACLs, err error) { + return acl, c.Get(ctx, "/access/acl", &acl) } -func (c *Client) UpdateACL(acl ACL) error { - return c.Put("/access/acl", &acl, nil) +func (c *Client) UpdateACL(ctx context.Context, acl ACL) error { + return c.Put(ctx, "/access/acl", &acl, nil) } // Permissions get permissions for the current user for the client which passes no params, use Permission -func (c *Client) Permissions(o *PermissionsOptions) (permissions Permissions, err error) { +func (c *Client) Permissions(ctx context.Context, o *PermissionsOptions) (permissions Permissions, err error) { u := url.URL{Path: "/access/permissions"} if o != nil { // params are optional @@ -48,26 +49,26 @@ func (c *Client) Permissions(o *PermissionsOptions) (permissions Permissions, er u.RawQuery = params.Encode() } - return permissions, c.Get(u.String(), &permissions) + return permissions, c.Get(ctx, u.String(), &permissions) } -func (c *Client) Password(userid, password string) error { - return c.Post("/access/password", map[string]string{ +func (c *Client) Password(ctx context.Context, userid, password string) error { + return c.Post(ctx, "/access/password", map[string]string{ "userid": userid, "password": password, }, nil) } // NewDomain create a new domain with the required two parameters pull it and use domain.Update to configure -func (c *Client) NewDomain(realm string, domainType DomainType) error { - return c.Post("/access/domains", map[string]string{ +func (c *Client) NewDomain(ctx context.Context, realm string, domainType DomainType) error { + return c.Post(ctx, "/access/domains", map[string]string{ "realm": realm, "type": string(domainType), }, nil) } -func (c *Client) Domain(realm string) (domain *Domain, err error) { - err = c.Get(fmt.Sprintf("/access/domains/%s", realm), &domain) +func (c *Client) Domain(ctx context.Context, realm string) (domain *Domain, err error) { + err = c.Get(ctx, fmt.Sprintf("/access/domains/%s", realm), &domain) if nil == err { domain.Realm = realm domain.client = c @@ -75,8 +76,8 @@ func (c *Client) Domain(realm string) (domain *Domain, err error) { return } -func (c *Client) Domains() (domains Domains, err error) { - err = c.Get("/access/domains", &domains) +func (c *Client) Domains(ctx context.Context) (domains Domains, err error) { + err = c.Get(ctx, "/access/domains", &domains) if nil == err { for _, d := range domains { d.client = c @@ -85,37 +86,37 @@ func (c *Client) Domains() (domains Domains, err error) { return } -func (d *Domain) Update() error { +func (d *Domain) Update(ctx context.Context) error { if d.Realm == "" { return errors.New("realm can not be empty") } - return d.client.Put(fmt.Sprintf("/access/domains/%s", d.Realm), d, nil) + return d.client.Put(ctx, fmt.Sprintf("/access/domains/%s", d.Realm), d, nil) } -func (d *Domain) Delete() error { +func (d *Domain) Delete(ctx context.Context) error { if d.Realm == "" { return errors.New("realm can not be empty") } - return d.client.Delete(fmt.Sprintf("/access/domains/%s", d.Realm), nil) + return d.client.Delete(ctx, fmt.Sprintf("/access/domains/%s", d.Realm), nil) } -func (d *Domain) Sync(options DomainSyncOptions) error { +func (d *Domain) Sync(ctx context.Context, options DomainSyncOptions) error { if d.Realm == "" { return errors.New("realm can not be empty") } - return d.client.Post(fmt.Sprintf("/access/domains/%s", d.Realm), options, nil) + return d.client.Post(ctx, fmt.Sprintf("/access/domains/%s", d.Realm), options, nil) } // NewGroup makes a new group, comment is option and can be left empty -func (c *Client) NewGroup(groupid, comment string) error { - return c.Post("/access/groups", map[string]string{ +func (c *Client) NewGroup(ctx context.Context, groupid, comment string) error { + return c.Post(ctx, "/access/groups", map[string]string{ "groupid": groupid, "comment": comment, }, nil) } -func (c *Client) Group(groupid string) (group *Group, err error) { - err = c.Get(fmt.Sprintf("/access/groups/%s", groupid), &group) +func (c *Client) Group(ctx context.Context, groupid string) (group *Group, err error) { + err = c.Get(ctx, fmt.Sprintf("/access/groups/%s", groupid), &group) if nil == err { group.GroupID = groupid group.client = c @@ -123,8 +124,8 @@ func (c *Client) Group(groupid string) (group *Group, err error) { return } -func (c *Client) Groups() (groups Groups, err error) { - err = c.Get("/access/groups", &groups) +func (c *Client) Groups(ctx context.Context) (groups Groups, err error) { + err = c.Get(ctx, "/access/groups", &groups) if nil == err { for _, g := range groups { g.client = c @@ -133,16 +134,16 @@ func (c *Client) Groups() (groups Groups, err error) { return } -func (g *Group) Update() error { - return g.client.Put(fmt.Sprintf("/access/groups/%s", g.GroupID), g, nil) +func (g *Group) Update(ctx context.Context) error { + return g.client.Put(ctx, fmt.Sprintf("/access/groups/%s", g.GroupID), g, nil) } -func (g *Group) Delete() error { - return g.client.Delete(fmt.Sprintf("/access/groups/%s", g.GroupID), nil) +func (g *Group) Delete(ctx context.Context) error { + return g.client.Delete(ctx, fmt.Sprintf("/access/groups/%s", g.GroupID), nil) } -func (c *Client) User(userid string) (user *User, err error) { - err = c.Get(fmt.Sprintf("/access/users/%s", userid), &user) +func (c *Client) User(ctx context.Context, userid string) (user *User, err error) { + err = c.Get(ctx, fmt.Sprintf("/access/users/%s", userid), &user) if nil == err { user.UserID = userid user.client = c @@ -150,8 +151,8 @@ func (c *Client) User(userid string) (user *User, err error) { return } -func (c *Client) Users() (users Users, err error) { - err = c.Get("/access/users", &users) +func (c *Client) Users(ctx context.Context) (users Users, err error) { + err = c.Get(ctx, "/access/users", &users) if nil == err { for _, g := range users { g.client = c @@ -160,21 +161,21 @@ func (c *Client) Users() (users Users, err error) { return } -func (u *User) Update() error { - return u.client.Put(fmt.Sprintf("/access/users/%s", u.UserID), u, nil) +func (u *User) Update(ctx context.Context) error { + return u.client.Put(ctx, fmt.Sprintf("/access/users/%s", u.UserID), u, nil) } -func (u *User) Delete() error { - return u.client.Delete(fmt.Sprintf("/access/users/%s", u.UserID), nil) +func (u *User) Delete(ctx context.Context) error { + return u.client.Delete(ctx, fmt.Sprintf("/access/users/%s", u.UserID), nil) } -func (c *Client) Role(roleid string) (role Permission, err error) { - err = c.Get(fmt.Sprintf("/access/roles/%s", roleid), &role) +func (c *Client) Role(ctx context.Context, roleid string) (role Permission, err error) { + err = c.Get(ctx, fmt.Sprintf("/access/roles/%s", roleid), &role) return } -func (c *Client) Roles() (roles Roles, err error) { - err = c.Get("/access/roles", &roles) +func (c *Client) Roles(ctx context.Context) (roles Roles, err error) { + err = c.Get(ctx, "/access/roles", &roles) if nil == err { for _, g := range roles { g.client = c @@ -183,10 +184,10 @@ func (c *Client) Roles() (roles Roles, err error) { return } -func (r *Role) Update() error { - return r.client.Put(fmt.Sprintf("/access/roles/%s", r.RoleID), r, nil) +func (r *Role) Update(ctx context.Context) error { + return r.client.Put(ctx, fmt.Sprintf("/access/roles/%s", r.RoleID), r, nil) } -func (r *Role) Delete() error { - return r.client.Delete(fmt.Sprintf("/access/roles/%s", r.RoleID), nil) +func (r *Role) Delete(ctx context.Context) error { + return r.client.Delete(ctx, fmt.Sprintf("/access/roles/%s", r.RoleID), nil) } diff --git a/access_test.go b/access_test.go index 023bc0f..e068be2 100644 --- a/access_test.go +++ b/access_test.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "testing" "github.com/luthermonson/go-proxmox/tests/mocks" @@ -17,8 +18,9 @@ func TestTicket(t *testing.T) { Username: "root@pam", Password: "1234", })) + ctx := context.Background() - session, err := client.Ticket(client.credentials) + session, err := client.Ticket(ctx, client.credentials) assert.Nil(t, err) assert.Equal(t, "root@pam", session.Username) assert.Equal(t, "pve-cluster", session.ClusterName) @@ -28,28 +30,29 @@ func TestPermissions(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - perms, err := client.Permissions(nil) + perms, err := client.Permissions(ctx, nil) assert.Nil(t, err) assert.Equal(t, 8, len(perms)) assert.Equal(t, IntOrBool(true), perms["/"]["Datastore.Allocate"]) // test path option - perms, err = client.Permissions(&PermissionsOptions{ + perms, err = client.Permissions(ctx, &PermissionsOptions{ Path: "path", }) assert.Nil(t, err) assert.Equal(t, IntOrBool(true), perms["path"]["permission"]) // test userid - perms, err = client.Permissions(&PermissionsOptions{ + perms, err = client.Permissions(ctx, &PermissionsOptions{ UserID: "userid", }) assert.Nil(t, err) assert.Equal(t, IntOrBool(true), perms["path"]["permission"]) // test both path and userid - perms, err = client.Permissions(&PermissionsOptions{ + perms, err = client.Permissions(ctx, &PermissionsOptions{ UserID: "userid", Path: "path", }) @@ -61,16 +64,18 @@ func TestPassword(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - assert.Nil(t, client.Password("userid", "password")) + assert.Nil(t, client.Password(ctx, "userid", "password")) } func TestDomains(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - ds, err := client.Domains() + ds, err := client.Domains(ctx) assert.Nil(t, err) assert.Equal(t, 3, len(ds)) } @@ -79,8 +84,9 @@ func TestDomain(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - d, err := client.Domain("test") + d, err := client.Domain(ctx, "test") assert.Nil(t, err) assert.Equal(t, d.Realm, "test") assert.False(t, bool(d.AutoCreate)) @@ -90,16 +96,18 @@ func TestNewGroup(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - assert.Nil(t, client.NewGroup("groupid", "comment")) + assert.Nil(t, client.NewGroup(ctx, "groupid", "comment")) } func TestGroup(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - g, err := client.Group("test") + g, err := client.Group(ctx, "test") assert.Nil(t, err) assert.Equal(t, g.GroupID, "test") assert.Len(t, g.Members, 2) @@ -109,8 +117,9 @@ func TestGroups(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - gs, err := client.Groups() + gs, err := client.Groups(ctx) assert.Nil(t, err) assert.Len(t, gs, 2) for _, g := range gs { @@ -123,33 +132,36 @@ func TestGroup_Update(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() group := Group{ client: client, } - assert.Error(t, group.Update()) // no groupid + assert.Error(t, group.Update(ctx)) // no groupid group.GroupID = "groupid" - assert.Nil(t, group.Update()) + assert.Nil(t, group.Update(ctx)) } func TestGroup_Delete(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() group := Group{ client: client, } - assert.Error(t, group.Delete()) + assert.Error(t, group.Delete(ctx)) group.GroupID = "groupid" - assert.Nil(t, group.Delete()) + assert.Nil(t, group.Delete(ctx)) } func TestUser(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - u, err := client.User("root@pam") + u, err := client.User(ctx, "root@pam") assert.Nil(t, err) assert.Equal(t, u.UserID, "root@pam") } @@ -158,8 +170,9 @@ func TestUsers(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - users, err := client.Users() + users, err := client.Users(ctx) assert.Nil(t, err) assert.Len(t, users, 4) } @@ -168,13 +181,14 @@ func TestRole(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - u, err := client.Role("Administrator") + u, err := client.Role(ctx, "Administrator") assert.Nil(t, err) assert.Contains(t, u, "SDN.Allocate") assert.Len(t, u, 38) - u, err = client.Role("NoAccess") + u, err = client.Role(ctx, "NoAccess") assert.Nil(t, err) assert.Len(t, u, 0) } @@ -183,8 +197,9 @@ func TestRoles(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - roles, err := client.Roles() + roles, err := client.Roles(ctx) assert.Nil(t, err) assert.Len(t, roles, 16) } @@ -193,8 +208,9 @@ func TestACL(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - acls, err := client.ACL() + acls, err := client.ACL(ctx) assert.Nil(t, err) assert.Len(t, acls, 1) } @@ -203,51 +219,55 @@ func TestNewDomain(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - assert.Nil(t, client.NewDomain("test", "t")) + assert.Nil(t, client.NewDomain(ctx, "test", "t")) } func TestDomain_Update(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() // no realm name domain := Domain{ client: client, } - assert.Error(t, domain.Update()) + assert.Error(t, domain.Update(ctx)) domain.Realm = "test" - assert.Nil(t, domain.Update()) + assert.Nil(t, domain.Update(ctx)) } func TestDomain_Delete(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() // no realm name domain := Domain{ client: client, } - assert.Error(t, domain.Delete()) + assert.Error(t, domain.Delete(ctx)) domain.Realm = "test" - assert.Nil(t, domain.Delete()) + assert.Nil(t, domain.Delete(ctx)) } func TestDomain_Sync(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() // no realm name domain := Domain{ client: client, } - assert.Error(t, domain.Sync(DomainSyncOptions{})) + assert.Error(t, domain.Sync(ctx, DomainSyncOptions{})) domain.Realm = "test" - assert.Nil(t, domain.Sync(DomainSyncOptions{})) + assert.Nil(t, domain.Sync(ctx, DomainSyncOptions{})) } diff --git a/cluster.go b/cluster.go index 31fbdd4..8fa3f2a 100644 --- a/cluster.go +++ b/cluster.go @@ -1,21 +1,22 @@ package proxmox import ( + "context" "net/url" "strconv" "strings" ) -func (c *Client) Cluster() (*Cluster, error) { +func (c *Client) Cluster(ctx context.Context) (*Cluster, error) { cluster := &Cluster{ client: c, } - return cluster, c.Get("/cluster/status", cluster) + return cluster, c.Get(ctx, "/cluster/status", cluster) } -func (cl *Cluster) NextID() (int, error) { +func (cl *Cluster) NextID(ctx context.Context) (int, error) { var ret string - if err := cl.client.Get("/cluster/nextid", &ret); err != nil { + if err := cl.client.Get(ctx, "/cluster/nextid", &ret); err != nil { return 0, err } return strconv.Atoi(ret) @@ -25,7 +26,7 @@ func (cl *Cluster) NextID() (int, error) { // It calls /cluster/resources api v2 endpoint with an optional "type" parameter // to filter searched values. // It returns a list of ClusterResources. -func (cl *Cluster) Resources(filters ...string) (rs ClusterResources, err error) { +func (cl *Cluster) Resources(ctx context.Context, filters ...string) (rs ClusterResources, err error) { u := url.URL{Path: "/cluster/resources"} // filters are variadic because they're optional, munging everything passed into one big string to make @@ -36,5 +37,5 @@ func (cl *Cluster) Resources(filters ...string) (rs ClusterResources, err error) u.RawQuery = params.Encode() } - return rs, cl.client.Get(u.String(), &rs) + return rs, cl.client.Get(ctx, u.String(), &rs) } diff --git a/cluster_firewall.go b/cluster_firewall.go index 5807217..87ce838 100644 --- a/cluster_firewall.go +++ b/cluster_firewall.go @@ -1,11 +1,12 @@ package proxmox import ( + "context" "fmt" ) -func (cl *Cluster) FWGroups() (groups []*FirewallSecurityGroup, err error) { - err = cl.client.Get("/cluster/firewall/groups", &groups) +func (cl *Cluster) FWGroups(ctx context.Context) (groups []*FirewallSecurityGroup, err error) { + err = cl.client.Get(ctx, "/cluster/firewall/groups", &groups) if nil == err { for _, g := range groups { @@ -15,9 +16,9 @@ func (cl *Cluster) FWGroups() (groups []*FirewallSecurityGroup, err error) { return } -func (cl *Cluster) FWGroup(name string) (group *FirewallSecurityGroup, err error) { +func (cl *Cluster) FWGroup(ctx context.Context, name string) (group *FirewallSecurityGroup, err error) { group = &FirewallSecurityGroup{} - err = cl.client.Get(fmt.Sprintf("/cluster/firewall/groups/%s", name), &group.Rules) + err = cl.client.Get(ctx, fmt.Sprintf("/cluster/firewall/groups/%s", name), &group.Rules) if nil == err { group.Group = name group.client = cl.client @@ -25,26 +26,26 @@ func (cl *Cluster) FWGroup(name string) (group *FirewallSecurityGroup, err error return } -func (cl *Cluster) NewFWGroup(group *FirewallSecurityGroup) error { - return cl.client.Post(fmt.Sprintf("/cluster/firewall/groups"), group, &group) +func (cl *Cluster) NewFWGroup(ctx context.Context, group *FirewallSecurityGroup) error { + return cl.client.Post(ctx, fmt.Sprintf("/cluster/firewall/groups"), group, &group) } -func (g *FirewallSecurityGroup) GetRules() ([]*FirewallRule, error) { - return g.Rules, g.client.Get(fmt.Sprintf("/cluster/firewall/groups/%s", g.Group), &g.Rules) +func (g *FirewallSecurityGroup) GetRules(ctx context.Context) ([]*FirewallRule, error) { + return g.Rules, g.client.Get(ctx, fmt.Sprintf("/cluster/firewall/groups/%s", g.Group), &g.Rules) } -func (g *FirewallSecurityGroup) Delete() error { - return g.client.Delete(fmt.Sprintf("/cluster/firewall/groups/%s", g.Group), nil) +func (g *FirewallSecurityGroup) Delete(ctx context.Context) error { + return g.client.Delete(ctx, fmt.Sprintf("/cluster/firewall/groups/%s", g.Group), nil) } -func (g *FirewallSecurityGroup) RuleCreate(rule *FirewallRule) error { - return g.client.Post(fmt.Sprintf("/cluster/firewall/groups/%s", g.Group), rule, nil) +func (g *FirewallSecurityGroup) RuleCreate(ctx context.Context, rule *FirewallRule) error { + return g.client.Post(ctx, fmt.Sprintf("/cluster/firewall/groups/%s", g.Group), rule, nil) } -func (g *FirewallSecurityGroup) RuleUpdate(rule *FirewallRule) error { - return g.client.Put(fmt.Sprintf("/cluster/firewall/groups/%s/%d", g.Group, rule.Pos), rule, nil) +func (g *FirewallSecurityGroup) RuleUpdate(ctx context.Context, rule *FirewallRule) error { + return g.client.Put(ctx, fmt.Sprintf("/cluster/firewall/groups/%s/%d", g.Group, rule.Pos), rule, nil) } -func (g *FirewallSecurityGroup) RuleDelete(rulePos int) error { - return g.client.Delete(fmt.Sprintf("/cluster/firewall/groups/%s/%d", g.Group, rulePos), nil) +func (g *FirewallSecurityGroup) RuleDelete(ctx context.Context, rulePos int) error { + return g.client.Delete(ctx, fmt.Sprintf("/cluster/firewall/groups/%s/%d", g.Group, rulePos), nil) } diff --git a/cluster_test.go b/cluster_test.go index 1f2c97a..717eda3 100644 --- a/cluster_test.go +++ b/cluster_test.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "testing" "github.com/luthermonson/go-proxmox/tests/mocks" @@ -11,8 +12,9 @@ func TestCluster(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - cluster, err := client.Cluster() + cluster, err := client.Cluster(ctx) assert.Nil(t, err) assert.Equal(t, 4, cluster.Version) assert.Equal(t, "cluster", cluster.ID) @@ -26,10 +28,11 @@ func TestNextID(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - cluster, err := client.Cluster() + cluster, err := client.Cluster(ctx) assert.Nil(t, err) - nextid, err := cluster.NextID() + nextid, err := cluster.NextID(ctx) assert.Nil(t, err) assert.Equal(t, 100, nextid) } @@ -38,15 +41,16 @@ func TestCluster_Resources(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - cluster, err := client.Cluster() + cluster, err := client.Cluster(ctx) assert.Nil(t, err) - // json unmarshall tests - rs, err := cluster.Resources() + // json unmarshaling tests + rs, err := cluster.Resources(ctx) assert.Equal(t, 20, len(rs)) // type param test - rs, err = cluster.Resources("node") + rs, err = cluster.Resources(ctx, "node") assert.Equal(t, 1, len(rs)) } diff --git a/containers.go b/containers.go index ca61903..1524b61 100644 --- a/containers.go +++ b/containers.go @@ -1,32 +1,33 @@ package proxmox import ( + "context" "fmt" "net/url" ) -func (c *Container) Start() (status string, err error) { - return status, c.client.Post(fmt.Sprintf("/nodes/%s/lxc/%d/status/start", c.Node, c.VMID), nil, &status) +func (c *Container) Start(ctx context.Context) (status string, err error) { + return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/start", c.Node, c.VMID), nil, &status) } -func (c *Container) Stop() (status *ContainerStatus, err error) { - return status, c.client.Post(fmt.Sprintf("/nodes/%s/lxc/%d/status/stop", c.Node, c.VMID), nil, &status) +func (c *Container) Stop(ctx context.Context) (status *ContainerStatus, err error) { + return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/stop", c.Node, c.VMID), nil, &status) } -func (c *Container) Suspend() (status *ContainerStatus, err error) { - return status, c.client.Post(fmt.Sprintf("/nodes/%s/lxc/%d/status/suspend", c.Node, c.VMID), nil, &status) +func (c *Container) Suspend(ctx context.Context) (status *ContainerStatus, err error) { + return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/suspend", c.Node, c.VMID), nil, &status) } -func (c *Container) Reboot() (status *ContainerStatus, err error) { - return status, c.client.Post(fmt.Sprintf("/nodes/%s/lxc/%d/status/reboot", c.Node, c.VMID), nil, &status) +func (c *Container) Reboot(ctx context.Context) (status *ContainerStatus, err error) { + return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/reboot", c.Node, c.VMID), nil, &status) } -func (c *Container) Resume() (status *ContainerStatus, err error) { - return status, c.client.Post(fmt.Sprintf("/nodes/%s/lxc/%d/status/resume", c.Node, c.VMID), nil, &status) +func (c *Container) Resume(ctx context.Context) (status *ContainerStatus, err error) { + return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/resume", c.Node, c.VMID), nil, &status) } -func (c *Container) TermProxy() (vnc *VNC, err error) { - return vnc, c.client.Post(fmt.Sprintf("/nodes/%s/lxk/%d/termproxy", c.Node, c.VMID), nil, &vnc) +func (c *Container) TermProxy(ctx context.Context) (vnc *VNC, err error) { + return vnc, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxk/%d/termproxy", c.Node, c.VMID), nil, &vnc) } func (c *Container) VNCWebSocket(vnc *VNC) (chan string, chan string, chan error, func() error, error) { diff --git a/nodes.go b/nodes.go index fddb51b..f5e668c 100644 --- a/nodes.go +++ b/nodes.go @@ -1,17 +1,18 @@ package proxmox import ( + "context" "fmt" "net/url" "strings" ) -func (c *Client) Nodes() (ns NodeStatuses, err error) { - return ns, c.Get("/nodes", &ns) +func (c *Client) Nodes(ctx context.Context) (ns NodeStatuses, err error) { + return ns, c.Get(ctx, "/nodes", &ns) } -func (c *Client) Node(name string) (node *Node, err error) { - if err := c.Get(fmt.Sprintf("/nodes/%s/status", name), &node); err != nil { +func (c *Client) Node(ctx context.Context, name string) (node *Node, err error) { + if err = c.Get(ctx, fmt.Sprintf("/nodes/%s/status", name), &node); err != nil { return nil, err } node.Name = name @@ -20,12 +21,12 @@ func (c *Client) Node(name string) (node *Node, err error) { return } -func (n *Node) Version() (version *Version, err error) { - return version, n.client.Get(fmt.Sprintf("/nodes/%s/version", n.Name), &version) +func (n *Node) Version(ctx context.Context) (version *Version, err error) { + return version, n.client.Get(ctx, fmt.Sprintf("/nodes/%s/version", n.Name), &version) } -func (n *Node) TermProxy() (vnc *VNC, err error) { - return vnc, n.client.Post(fmt.Sprintf("/nodes/%s/termproxy", n.Name), nil, &vnc) +func (n *Node) TermProxy(ctx context.Context) (vnc *VNC, err error) { + return vnc, n.client.Post(ctx, fmt.Sprintf("/nodes/%s/termproxy", n.Name), nil, &vnc) } // VNCWebSocket send, recv, errors, closer, error @@ -36,8 +37,8 @@ func (n *Node) VNCWebSocket(vnc *VNC) (chan string, chan string, chan error, fun return n.client.VNCWebSocket(p, vnc) } -func (n *Node) VirtualMachines() (vms VirtualMachines, err error) { - if err := n.client.Get(fmt.Sprintf("/nodes/%s/qemu", n.Name), &vms); err != nil { +func (n *Node) VirtualMachines(ctx context.Context) (vms VirtualMachines, err error) { + if err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu", n.Name), &vms); err != nil { return nil, err } @@ -49,7 +50,7 @@ func (n *Node) VirtualMachines() (vms VirtualMachines, err error) { return vms, nil } -func (n *Node) NewVirtualMachine(vmid int, options ...VirtualMachineOption) (*Task, error) { +func (n *Node) NewVirtualMachine(ctx context.Context, vmid int, options ...VirtualMachineOption) (*Task, error) { var upid UPID data := make(map[string]interface{}) data["vmid"] = vmid @@ -58,29 +59,29 @@ func (n *Node) NewVirtualMachine(vmid int, options ...VirtualMachineOption) (*Ta data[option.Name] = option.Value } - err := n.client.Post(fmt.Sprintf("/nodes/%s/qemu", n.Name), data, &upid) + err := n.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu", n.Name), data, &upid) return NewTask(upid, n.client), err } -func (n *Node) VirtualMachine(vmid int) (*VirtualMachine, error) { +func (n *Node) VirtualMachine(ctx context.Context, vmid int) (*VirtualMachine, error) { vm := &VirtualMachine{ client: n.client, Node: n.Name, } - if err := n.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/status/current", n.Name, vmid), &vm); nil != err { + if err := n.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/current", n.Name, vmid), &vm); nil != err { return nil, err } - if err := n.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/config", n.Name, vmid), &vm.VirtualMachineConfig); err != nil { + if err := n.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/config", n.Name, vmid), &vm.VirtualMachineConfig); err != nil { return nil, err } return vm, nil } -func (n *Node) Containers() (c Containers, err error) { - if err = n.client.Get(fmt.Sprintf("/nodes/%s/lxc", n.Name), &c); err != nil { +func (n *Node) Containers(ctx context.Context) (c Containers, err error) { + if err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc", n.Name), &c); err != nil { return } @@ -92,9 +93,9 @@ func (n *Node) Containers() (c Containers, err error) { return } -func (n *Node) Container(vmid int) (*Container, error) { +func (n *Node) Container(ctx context.Context, vmid int) (*Container, error) { var c Container - if err := n.client.Get(fmt.Sprintf("/nodes/%s/lxc/%d/status/current", n.Name, vmid), &c); err != nil { + if err := n.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/current", n.Name, vmid), &c); err != nil { return nil, err } c.client = n.client @@ -103,8 +104,8 @@ func (n *Node) Container(vmid int) (*Container, error) { return &c, nil } -func (n *Node) Appliances() (appliances Appliances, err error) { - err = n.client.Get(fmt.Sprintf("/nodes/%s/aplinfo", n.Name), &appliances) +func (n *Node) Appliances(ctx context.Context) (appliances Appliances, err error) { + err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/aplinfo", n.Name), &appliances) if err != nil { return appliances, err } @@ -117,19 +118,19 @@ func (n *Node) Appliances() (appliances Appliances, err error) { return appliances, nil } -func (n *Node) DownloadAppliance(template, storage string) (ret string, err error) { - return ret, n.client.Post(fmt.Sprintf("/nodes/%s/aplinfo", n.Name), map[string]string{ +func (n *Node) DownloadAppliance(ctx context.Context, template, storage string) (ret string, err error) { + return ret, n.client.Post(ctx, fmt.Sprintf("/nodes/%s/aplinfo", n.Name), map[string]string{ "template": template, "storage": storage, }, &ret) } -func (n *Node) VzTmpls(storage string) (templates VzTmpls, err error) { - return templates, n.client.Get(fmt.Sprintf("/nodes/%s/storage/%s/content?content=vztmpl", n.Name, storage), &templates) +func (n *Node) VzTmpls(ctx context.Context, storage string) (templates VzTmpls, err error) { + return templates, n.client.Get(ctx, fmt.Sprintf("/nodes/%s/storage/%s/content?content=vztmpl", n.Name, storage), &templates) } -func (n *Node) VzTmpl(template, storage string) (*VzTmpl, error) { - templates, err := n.VzTmpls(storage) +func (n *Node) VzTmpl(ctx context.Context, template, storage string) (*VzTmpl, error) { + templates, err := n.VzTmpls(ctx, storage) if err != nil { return nil, err } @@ -144,8 +145,8 @@ func (n *Node) VzTmpl(template, storage string) (*VzTmpl, error) { return nil, fmt.Errorf("could not find vztmpl: %s", template) } -func (n *Node) Storages() (storages Storages, err error) { - err = n.client.Get(fmt.Sprintf("/nodes/%s/storage", n.Name), &storages) +func (n *Node) Storages(ctx context.Context) (storages Storages, err error) { + err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/storage", n.Name), &storages) if err != nil { return } @@ -158,8 +159,8 @@ func (n *Node) Storages() (storages Storages, err error) { return } -func (n *Node) Storage(name string) (storage *Storage, err error) { - err = n.client.Get(fmt.Sprintf("/nodes/%s/storage/%s/status", n.Name, name), &storage) +func (n *Node) Storage(ctx context.Context, name string) (storage *Storage, err error) { + err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/storage/%s/status", n.Name, name), &storage) if err != nil { return } @@ -171,29 +172,29 @@ func (n *Node) Storage(name string) (storage *Storage, err error) { return } -func (n *Node) StorageISO() (*Storage, error) { - return n.findStorageByContent("iso") +func (n *Node) StorageISO(ctx context.Context) (*Storage, error) { + return n.findStorageByContent(ctx, "iso") } -func (n *Node) StorageVZTmpl() (*Storage, error) { - return n.findStorageByContent("vztmpl") +func (n *Node) StorageVZTmpl(ctx context.Context) (*Storage, error) { + return n.findStorageByContent(ctx, "vztmpl") } -func (n *Node) StorageBackup() (*Storage, error) { - return n.findStorageByContent("backup") +func (n *Node) StorageBackup(ctx context.Context) (*Storage, error) { + return n.findStorageByContent(ctx, "backup") } -func (n *Node) StorageRootDir() (*Storage, error) { - return n.findStorageByContent("rootdir") +func (n *Node) StorageRootDir(ctx context.Context) (*Storage, error) { + return n.findStorageByContent(ctx, "rootdir") } -func (n *Node) StorageImages() (*Storage, error) { - return n.findStorageByContent("images") +func (n *Node) StorageImages(ctx context.Context) (*Storage, error) { + return n.findStorageByContent(ctx, "images") } // findStorageByContent takes iso/backup/vztmpl/rootdir/images and returns the storage that type of content should be on -func (n *Node) findStorageByContent(content string) (storage *Storage, err error) { - storages, err := n.Storages() +func (n *Node) findStorageByContent(ctx context.Context, content string) (storage *Storage, err error) { + storages, err := n.Storages(ctx) if err != nil { return nil, err } @@ -209,28 +210,28 @@ func (n *Node) findStorageByContent(content string) (storage *Storage, err error return nil, ErrNotFound } -func (n *Node) FirewallOptionGet() (firewallOption *FirewallNodeOption, err error) { - err = n.client.Get(fmt.Sprintf("/nodes/%s/firewall/options", n.Name), firewallOption) +func (n *Node) FirewallOptionGet(ctx context.Context) (firewallOption *FirewallNodeOption, err error) { + err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/firewall/options", n.Name), firewallOption) return } -func (n *Node) FirewallOptionSet(firewallOption *FirewallNodeOption) error { - return n.client.Put(fmt.Sprintf("/nodes/%s/firewall/options", n.Name), firewallOption, nil) +func (n *Node) FirewallOptionSet(ctx context.Context, firewallOption *FirewallNodeOption) error { + return n.client.Put(ctx, fmt.Sprintf("/nodes/%s/firewall/options", n.Name), firewallOption, nil) } -func (n *Node) FirewallGetRules() (rules []*FirewallRule, err error) { - err = n.client.Get(fmt.Sprintf("/nodes/%s/firewall/rules", n.Name), &rules) +func (n *Node) FirewallGetRules(ctx context.Context) (rules []*FirewallRule, err error) { + err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/firewall/rules", n.Name), &rules) return } -func (n *Node) FirewallRulesCreate(rule *FirewallRule) error { - return n.client.Post(fmt.Sprintf("/nodes/%s/firewall/rules", n.Name), rule, nil) +func (n *Node) FirewallRulesCreate(ctx context.Context, rule *FirewallRule) error { + return n.client.Post(ctx, fmt.Sprintf("/nodes/%s/firewall/rules", n.Name), rule, nil) } -func (n *Node) FirewallRulesUpdate(rule *FirewallRule) error { - return n.client.Put(fmt.Sprintf("/nodes/%s/firewall/rules/%d", n.Name, rule.Pos), rule, nil) +func (n *Node) FirewallRulesUpdate(ctx context.Context, rule *FirewallRule) error { + return n.client.Put(ctx, fmt.Sprintf("/nodes/%s/firewall/rules/%d", n.Name, rule.Pos), rule, nil) } -func (n *Node) FirewallRulesDelete(rulePos int) error { - return n.client.Delete(fmt.Sprintf("/nodes/%s/firewall/rules/%d", n.Name, rulePos), nil) +func (n *Node) FirewallRulesDelete(ctx context.Context, rulePos int) error { + return n.client.Delete(ctx, fmt.Sprintf("/nodes/%s/firewall/rules/%d", n.Name, rulePos), nil) } diff --git a/nodes_network.go b/nodes_network.go index a2411ac..c50d98e 100644 --- a/nodes_network.go +++ b/nodes_network.go @@ -1,9 +1,12 @@ package proxmox -import "fmt" +import ( + "context" + "fmt" +) -func (n *Node) NewNetwork(network *NodeNetwork) (task *Task, err error) { - err = n.client.Post(fmt.Sprintf("/nodes/%s/network", n.Name), network, network) +func (n *Node) NewNetwork(ctx context.Context, network *NodeNetwork) (task *Task, err error) { + err = n.client.Post(ctx, fmt.Sprintf("/nodes/%s/network", n.Name), network, network) if nil != err { return } @@ -11,11 +14,11 @@ func (n *Node) NewNetwork(network *NodeNetwork) (task *Task, err error) { network.client = n.client network.Node = n.Name network.NodeAPI = n - return n.NetworkReload() + return n.NetworkReload(ctx) } -func (n *Node) Network(iface string) (network *NodeNetwork, err error) { - err = n.client.Get(fmt.Sprintf("/nodes/%s/network/%s", n.Name, iface), &network) +func (n *Node) Network(ctx context.Context, iface string) (network *NodeNetwork, err error) { + err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/network/%s", n.Name, iface), &network) if err != nil { return } @@ -30,8 +33,8 @@ func (n *Node) Network(iface string) (network *NodeNetwork, err error) { return } -func (n *Node) Networks() (networks NodeNetworks, err error) { - err = n.client.Get(fmt.Sprintf("/nodes/%s/network", n.Name), &networks) +func (n *Node) Networks(ctx context.Context) (networks NodeNetworks, err error) { + err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/network", n.Name), &networks) if err != nil { return nil, err } @@ -45,9 +48,9 @@ func (n *Node) Networks() (networks NodeNetworks, err error) { return } -func (n *Node) NetworkReload() (*Task, error) { +func (n *Node) NetworkReload(ctx context.Context) (*Task, error) { var upid UPID - err := n.client.Put(fmt.Sprintf("/nodes/%s/network", n.Name), nil, &upid) + err := n.client.Put(ctx, fmt.Sprintf("/nodes/%s/network", n.Name), nil, &upid) if err != nil { return nil, err } @@ -55,22 +58,22 @@ func (n *Node) NetworkReload() (*Task, error) { return NewTask(upid, n.client), nil } -func (nw *NodeNetwork) Update() error { +func (nw *NodeNetwork) Update(ctx context.Context) error { if "" == nw.Iface { return nil } - return nw.client.Put(fmt.Sprintf("/nodes/%s/network/%s", nw.Node, nw.Iface), nw, nil) + return nw.client.Put(ctx, fmt.Sprintf("/nodes/%s/network/%s", nw.Node, nw.Iface), nw, nil) } -func (nw *NodeNetwork) Delete() (task *Task, err error) { +func (nw *NodeNetwork) Delete(ctx context.Context) (task *Task, err error) { var upid UPID if "" == nw.Iface { return } - err = nw.client.Delete(fmt.Sprintf("/nodes/%s/network/%s", nw.Node, nw.Iface), &upid) + err = nw.client.Delete(ctx, fmt.Sprintf("/nodes/%s/network/%s", nw.Node, nw.Iface), &upid) if err != nil { return } - return nw.NodeAPI.NetworkReload() + return nw.NodeAPI.NetworkReload(ctx) } diff --git a/nodes_network_test.go b/nodes_network_test.go index f8660b5..20f1511 100644 --- a/nodes_network_test.go +++ b/nodes_network_test.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "testing" "github.com/luthermonson/go-proxmox/tests/mocks" @@ -11,12 +12,13 @@ func TestNetwork(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() node := Node{ client: client, Name: "node1", } - network, err := node.Network("vmbr0") + network, err := node.Network(ctx, "vmbr0") assert.Nil(t, err) assert.Equal(t, network.Iface, "vmbr0") } @@ -25,12 +27,13 @@ func TestNode1Networks(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() node := Node{ client: client, Name: "node1", } - networks, err := node.Networks() + networks, err := node.Networks(ctx) assert.Nil(t, err) assert.Len(t, networks, 2) } @@ -39,12 +42,13 @@ func TestNode2Networks(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() node := Node{ client: client, Name: "node2", } - networks, err := node.Networks() + networks, err := node.Networks(ctx) assert.Nil(t, err) assert.Len(t, networks, 2) } @@ -53,12 +57,13 @@ func TestNetworksPve8(t *testing.T) { mocks.ProxmoxVE8x(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() node := Node{ client: client, Name: "node1", } - networks, err := node.Networks() + networks, err := node.Networks(ctx) assert.Nil(t, err) assert.Len(t, networks, 5) } diff --git a/nodes_test.go b/nodes_test.go index 5c70b1b..fc3ae43 100644 --- a/nodes_test.go +++ b/nodes_test.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "testing" "github.com/luthermonson/go-proxmox/tests/mocks" @@ -11,8 +12,9 @@ func TestClient_Nodes(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - nodes, err := client.Nodes() + nodes, err := client.Nodes(ctx) assert.Nil(t, err) for _, n := range nodes { assert.Contains(t, n.Node, "node") @@ -25,17 +27,18 @@ func TestClient_Node(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - node, err := client.Node("node1") + node, err := client.Node(ctx, "node1") assert.Nil(t, err) assert.Equal(t, "node1", node.Name) assert.NotNil(t, node.client) - v, err := node.Version() + v, err := node.Version(ctx) assert.Nil(t, err) assert.Equal(t, "7.4", v.Release) - node, err = client.Node("doesntexist") + node, err = client.Node(ctx, "doesntexist") assert.NotNil(t, err) assert.Nil(t, node) } diff --git a/pools.go b/pools.go index 2caad79..96a49f3 100644 --- a/pools.go +++ b/pools.go @@ -1,20 +1,21 @@ package proxmox import ( + "context" "fmt" "net/url" "strings" ) -func (c *Client) NewPool(poolid, comment string) error { - return c.Post("/pools", map[string]string{ +func (c *Client) NewPool(ctx context.Context, poolid, comment string) error { + return c.Post(ctx, "/pools", map[string]string{ "poolid": poolid, "comment": comment, }, nil) } -func (c *Client) Pools() (pools Pools, err error) { - err = c.Get("/pools", &pools) +func (c *Client) Pools(ctx context.Context) (pools Pools, err error) { + err = c.Get(ctx, "/pools", &pools) for _, pool := range pools { pool.client = c } @@ -22,7 +23,7 @@ func (c *Client) Pools() (pools Pools, err error) { } // Pool optional filter of cluster resources by type, enum can be "qemu", "lxc", "storage". -func (c *Client) Pool(poolid string, filters ...string) (pool *Pool, err error) { +func (c *Client) Pool(ctx context.Context, poolid string, filters ...string) (pool *Pool, err error) { u := url.URL{Path: fmt.Sprintf("/pools/%s", poolid)} // filters are variadic because they're optional, munging everything passed into one big string to make @@ -33,7 +34,7 @@ func (c *Client) Pool(poolid string, filters ...string) (pool *Pool, err error) u.RawQuery = params.Encode() } - if err = c.Get(u.String(), &pool); err != nil { + if err = c.Get(ctx, u.String(), &pool); err != nil { return nil, err } pool.PoolID = poolid @@ -42,10 +43,10 @@ func (c *Client) Pool(poolid string, filters ...string) (pool *Pool, err error) return } -func (p *Pool) Update(opt *PoolUpdateOption) error { - return p.client.Put(fmt.Sprintf("/pools/%s", p.PoolID), opt, nil) +func (p *Pool) Update(ctx context.Context, opt *PoolUpdateOption) error { + return p.client.Put(ctx, fmt.Sprintf("/pools/%s", p.PoolID), opt, nil) } -func (p *Pool) Delete() error { - return p.client.Delete(fmt.Sprintf("/pools/%s", p.PoolID), nil) +func (p *Pool) Delete(ctx context.Context) error { + return p.client.Delete(ctx, fmt.Sprintf("/pools/%s", p.PoolID), nil) } diff --git a/pools_test.go b/pools_test.go index d5c6be4..3e725e1 100644 --- a/pools_test.go +++ b/pools_test.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "testing" "github.com/luthermonson/go-proxmox/tests/mocks" @@ -12,7 +13,7 @@ func TestPools(t *testing.T) { defer mocks.Off() client := mockClient() - pools, err := client.Pools() + pools, err := client.Pools(context.Background()) assert.Nil(t, err) assert.Len(t, pools, 1) } @@ -22,7 +23,7 @@ func TestPoolGet(t *testing.T) { defer mocks.Off() client := mockClient() - pool, err := client.Pool("test-pool") + pool, err := client.Pool(context.Background(), "test-pool") assert.Nil(t, err) assert.NotNil(t, pool) if pool != nil { @@ -37,7 +38,7 @@ func TestPoolCreate(t *testing.T) { defer mocks.Off() client := mockClient() - err := client.NewPool("test-pool", "Test pool") + err := client.NewPool(context.Background(), "test-pool", "Test pool") assert.Nil(t, err) } @@ -45,13 +46,14 @@ func TestPoolUpdate(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - pool, err := client.Pool("test-pool") + pool, err := client.Pool(ctx, "test-pool") assert.Nil(t, err) assert.NotNil(t, pool) if pool != nil { - err = pool.Update(&PoolUpdateOption{ + err = pool.Update(ctx, &PoolUpdateOption{ Comment: "Test pool updated", Delete: true, Storage: "local-zfs", @@ -65,13 +67,14 @@ func TestPoolDelete(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() - pool, err := client.Pool("test-pool") + pool, err := client.Pool(ctx, "test-pool") assert.Nil(t, err) assert.NotNil(t, pool) if pool != nil { - err = pool.Delete() + err = pool.Delete(ctx) assert.Nil(t, err) } } diff --git a/proxmox.go b/proxmox.go index 140cbec..4b5bc22 100644 --- a/proxmox.go +++ b/proxmox.go @@ -2,6 +2,7 @@ package proxmox import ( "bytes" + "context" "crypto/tls" "encoding/json" "errors" @@ -80,11 +81,11 @@ func NewClient(baseURL string, opts ...Option) *Client { return c } -func (c *Client) Version() (*Version, error) { - return c.version, c.Get("/version", &c.version) +func (c *Client) Version(ctx context.Context) (*Version, error) { + return c.version, c.Get(ctx, "/version", &c.version) } -func (c *Client) Req(method, path string, data []byte, v interface{}) error { +func (c *Client) Req(ctx context.Context, method, path string, data []byte, v interface{}) error { if strings.HasPrefix(path, "/") { path = c.baseURL + path } @@ -105,7 +106,7 @@ func (c *Client) Req(method, path string, data []byte, v interface{}) error { body = bytes.NewBuffer(data) } - req, err := http.NewRequest(method, path, body) + req, err := http.NewRequestWithContext(ctx, method, path, body) if err != nil { return err } @@ -119,7 +120,7 @@ func (c *Client) Req(method, path string, data []byte, v interface{}) error { if err != nil { return err } - defer res.Body.Close() + defer func() { _ = res.Body.Close() }() if res.StatusCode == http.StatusUnauthorized || res.StatusCode == http.StatusForbidden { if path == (c.baseURL + "/access/ticket") { @@ -129,10 +130,10 @@ func (c *Client) Req(method, path string, data []byte, v interface{}) error { if c.credentials != nil && c.session == nil { // credentials passed but no session started, try a login and retry the request - if _, err := c.Ticket(c.credentials); err != nil { + if _, err = c.Ticket(ctx, c.credentials); err != nil { return err } - return c.Req(method, path, data, v) + return c.Req(ctx, method, path, data, v) } return ErrNotAuthorized } @@ -141,11 +142,11 @@ func (c *Client) Req(method, path string, data []byte, v interface{}) error { } -func (c *Client) Get(p string, v interface{}) error { - return c.Req(http.MethodGet, p, nil, v) +func (c *Client) Get(ctx context.Context, p string, v interface{}) error { + return c.Req(ctx, http.MethodGet, p, nil, v) } -func (c *Client) Post(p string, d interface{}, v interface{}) error { +func (c *Client) Post(ctx context.Context, p string, d interface{}, v interface{}) error { var data []byte if d != nil { var err error @@ -155,10 +156,10 @@ func (c *Client) Post(p string, d interface{}, v interface{}) error { } } - return c.Req(http.MethodPost, p, data, v) + return c.Req(ctx, http.MethodPost, p, data, v) } -func (c *Client) Put(p string, d interface{}, v interface{}) error { +func (c *Client) Put(ctx context.Context, p string, d interface{}, v interface{}) error { var data []byte if d != nil { var err error @@ -168,11 +169,11 @@ func (c *Client) Put(p string, d interface{}, v interface{}) error { } } - return c.Req(http.MethodPut, p, data, v) + return c.Req(ctx, http.MethodPut, p, data, v) } -func (c *Client) Delete(p string, v interface{}) error { - return c.Req(http.MethodDelete, p, nil, v) +func (c *Client) Delete(ctx context.Context, p string, v interface{}) error { + return c.Req(ctx, http.MethodDelete, p, nil, v) } // Upload - There is some weird 16kb limit hardcoded in proxmox for the max POST size, hopefully in the future we make @@ -226,7 +227,7 @@ func (c *Client) Upload(path string, fields map[string]string, file *os.File, v if err != nil { return err } - defer res.Body.Close() + defer func() { _ = res.Body.Close() }() return c.handleResponse(res, &v) } @@ -275,7 +276,7 @@ func (c *Client) handleResponse(res *http.Response, v interface{}) error { return fmt.Errorf("bad request: %s - %s", res.Status, string(body)) } - // if nil passed dont bother to do any unmarshalling + // if nil passed don't bother to do any unmarshalling if nil == v { return nil } diff --git a/proxmox_test.go b/proxmox_test.go index da58fe9..a1fcbfc 100644 --- a/proxmox_test.go +++ b/proxmox_test.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "io" "net/http" "strings" @@ -72,7 +73,7 @@ func TestClient_Version7(t *testing.T) { mocks.ProxmoxVE7x(mockConfig) defer mocks.Off() - v, err := mockClient().Version() + v, err := mockClient().Version(context.Background()) assert.Nil(t, err) assert.Equal(t, "7.7-7", v.Version) assert.Equal(t, "777777", v.RepoID) @@ -83,7 +84,7 @@ func TestClient_Version6(t *testing.T) { mocks.ProxmoxVE6x(mockConfig) defer mocks.Off() - v, err := mockClient().Version() + v, err := mockClient().Version(context.Background()) assert.Nil(t, err) assert.Equal(t, "6.6-6", v.Version) assert.Equal(t, "666666", v.RepoID) @@ -94,22 +95,23 @@ func TestClientMethods(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() var err error var v Version - err = client.Get("/version", &v) + err = client.Get(ctx, "/version", &v) assert.Nil(t, err) assert.Equal(t, "7.7", v.Release) - err = client.Post("/version", struct{}{}, &v) + err = client.Post(ctx, "/version", struct{}{}, &v) assert.Nil(t, err) assert.Equal(t, "7.7", v.Release) - err = client.Put("/version", struct{}{}, &v) + err = client.Put(ctx, "/version", struct{}{}, &v) assert.Nil(t, err) assert.Equal(t, "7.7", v.Release) - err = client.Delete("/version", &v) + err = client.Delete(ctx, "/version", &v) assert.Nil(t, err) assert.Equal(t, "7.7", v.Release) } diff --git a/storage.go b/storage.go index a20eca3..fca7a5b 100644 --- a/storage.go +++ b/storage.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "fmt" "os" "path/filepath" @@ -53,7 +54,7 @@ func (s *Storage) upload(content, file string, extraArgs *map[string]string) (*T if err != nil { return nil, err } - defer f.Close() + defer func() { _ = f.Close() }() var upid UPID data := map[string]string{"content": content} @@ -71,18 +72,18 @@ func (s *Storage) upload(content, file string, extraArgs *map[string]string) (*T return NewTask(upid, s.client), nil } -func (s *Storage) DownloadURL(content, filename, url string) (*Task, error) { - return s.downloadURL(content, filename, url, nil) +func (s *Storage) DownloadURL(ctx context.Context, content, filename, url string) (*Task, error) { + return s.downloadURL(ctx, content, filename, url, nil) } -func (s *Storage) DownloadURLWithHash(content, filename, url string, checksum, checksumAlgorithm string) (*Task, error) { - return s.downloadURL(content, filename, url, &map[string]string{ +func (s *Storage) DownloadURLWithHash(ctx context.Context, content, filename, url string, checksum, checksumAlgorithm string) (*Task, error) { + return s.downloadURL(ctx, content, filename, url, &map[string]string{ "checksum": checksum, "checksum-algorithm": checksumAlgorithm, }) } -func (s *Storage) downloadURL(content, filename, url string, extraArgs *map[string]string) (*Task, error) { +func (s *Storage) downloadURL(ctx context.Context, content, filename, url string, extraArgs *map[string]string) (*Task, error) { if _, ok := validContent[content]; !ok { return nil, fmt.Errorf("only iso and vztmpl allowed") } @@ -99,15 +100,15 @@ func (s *Storage) downloadURL(content, filename, url string, extraArgs *map[stri data[k] = v } } - err := s.client.Post(fmt.Sprintf("/nodes/%s/storage/%s/download-url", s.Node, s.Name), data, &upid) + err := s.client.Post(ctx, fmt.Sprintf("/nodes/%s/storage/%s/download-url", s.Node, s.Name), data, &upid) if err != nil { return nil, err } return NewTask(upid, s.client), nil } -func (s *Storage) ISO(name string) (iso *ISO, err error) { - err = s.client.Get(fmt.Sprintf("/nodes/%s/storage/%s/content/%s:%s/%s", s.Node, s.Name, s.Name, "iso", name), &iso) +func (s *Storage) ISO(ctx context.Context, name string) (iso *ISO, err error) { + err = s.client.Get(ctx, fmt.Sprintf("/nodes/%s/storage/%s/content/%s:%s/%s", s.Node, s.Name, s.Name, "iso", name), &iso) if err != nil { return nil, err } @@ -121,8 +122,8 @@ func (s *Storage) ISO(name string) (iso *ISO, err error) { return } -func (s *Storage) VzTmpl(name string) (vztmpl *VzTmpl, err error) { - err = s.client.Get(fmt.Sprintf("/nodes/%s/storage/%s/content/%s:%s/%s", s.Node, s.Name, s.Name, "vztmpl", name), &vztmpl) +func (s *Storage) VzTmpl(ctx context.Context, name string) (vztmpl *VzTmpl, err error) { + err = s.client.Get(ctx, fmt.Sprintf("/nodes/%s/storage/%s/content/%s:%s/%s", s.Node, s.Name, s.Name, "vztmpl", name), &vztmpl) if err != nil { return nil, err } @@ -136,8 +137,8 @@ func (s *Storage) VzTmpl(name string) (vztmpl *VzTmpl, err error) { return } -func (s *Storage) Backup(name string) (backup *Backup, err error) { - err = s.client.Get(fmt.Sprintf("/nodes/%s/storage/%s/content/%s:%s/%s", s.Node, s.Name, s.Name, "backup", name), &backup) +func (s *Storage) Backup(ctx context.Context, name string) (backup *Backup, err error) { + err = s.client.Get(ctx, fmt.Sprintf("/nodes/%s/storage/%s/content/%s:%s/%s", s.Node, s.Name, s.Name, "backup", name), &backup) if err != nil { return nil, err } @@ -148,19 +149,19 @@ func (s *Storage) Backup(name string) (backup *Backup, err error) { return } -func (v *VzTmpl) Delete() (*Task, error) { - return deleteVolume(v.client, v.Node, v.Storage, v.VolID, v.Path, "vztmpl") +func (v *VzTmpl) Delete(ctx context.Context) (*Task, error) { + return deleteVolume(ctx, v.client, v.Node, v.Storage, v.VolID, v.Path, "vztmpl") } -func (b *Backup) Delete() (*Task, error) { - return deleteVolume(b.client, b.Node, b.Storage, b.VolID, b.Path, "backup") +func (b *Backup) Delete(ctx context.Context) (*Task, error) { + return deleteVolume(ctx, b.client, b.Node, b.Storage, b.VolID, b.Path, "backup") } -func (i *ISO) Delete() (*Task, error) { - return deleteVolume(i.client, i.Node, i.Storage, i.VolID, i.Path, "iso") +func (i *ISO) Delete(ctx context.Context) (*Task, error) { + return deleteVolume(ctx, i.client, i.Node, i.Storage, i.VolID, i.Path, "iso") } -func deleteVolume(c *Client, n, s, v, p, t string) (*Task, error) { +func deleteVolume(ctx context.Context, c *Client, n, s, v, p, t string) (*Task, error) { var upid UPID if v == "" && p == "" { return nil, fmt.Errorf("volid or path required for a delete") @@ -171,6 +172,6 @@ func deleteVolume(c *Client, n, s, v, p, t string) (*Task, error) { v = fmt.Sprintf("%s:%s/%s", s, t, filepath.Base(p)) } - err := c.Delete(fmt.Sprintf("/nodes/%s/storage/%s/content/%s", n, s, v), &upid) + err := c.Delete(ctx, fmt.Sprintf("/nodes/%s/storage/%s/content/%s", n, s, v), &upid) return NewTask(upid, c), err } diff --git a/tasks.go b/tasks.go index 6f24b58..62416b8 100644 --- a/tasks.go +++ b/tasks.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "fmt" "strings" "time" @@ -35,9 +36,9 @@ func NewTask(upid UPID, client *Client) *Task { return task } -func (t *Task) Ping() error { +func (t *Task) Ping(ctx context.Context) error { tmp := NewTask(t.UPID, t.client) - err := t.client.Get(fmt.Sprintf("/nodes/%s/tasks/%s/status", t.Node, t.UPID), t) + err := t.client.Get(ctx, fmt.Sprintf("/nodes/%s/tasks/%s/status", t.Node, t.UPID), t) if nil != err || nil == t { t = tmp } @@ -60,19 +61,19 @@ func (t *Task) Ping() error { return err } -func (t *Task) Stop() error { - return t.client.Delete(fmt.Sprintf("/nodes/%s/tasks/%s", t.Node, t.UPID), nil) +func (t *Task) Stop(ctx context.Context) error { + return t.client.Delete(ctx, fmt.Sprintf("/nodes/%s/tasks/%s", t.Node, t.UPID), nil) } -func (t *Task) Log(start, limit int) (l Log, err error) { - return l, t.client.Get(fmt.Sprintf("/nodes/%s/tasks/%s/log?start=%d&limit=%d", t.Node, t.UPID, start, limit), &l) +func (t *Task) Log(ctx context.Context, start, limit int) (l Log, err error) { + return l, t.client.Get(ctx, fmt.Sprintf("/nodes/%s/tasks/%s/log?start=%d&limit=%d", t.Node, t.UPID, start, limit), &l) } -func (t *Task) Watch(start int) (chan string, error) { +func (t *Task) Watch(ctx context.Context, start int) (chan string, error) { t.client.log.Debugf("starting watcher on %s", t.UPID) watch := make(chan string) - log, err := t.Log(start, 50) + log, err := t.Log(ctx, start, 50) if err != nil { return watch, err } @@ -85,7 +86,7 @@ func (t *Task) Watch(start int) (chan string, error) { } time.Sleep(1 * time.Second) - log, err = t.Log(start, 50) + log, err = t.Log(ctx, start, 50) if err != nil { return watch, err } @@ -101,7 +102,7 @@ func (t *Task) Watch(start int) (chan string, error) { watch <- ln } t.client.log.Debugf("watching task %s", t.UPID) - err := tasktail(len(log), watch, t) + err := tasktail(ctx, len(log), watch, t) if err != nil { t.client.log.Errorf("error watching logs: %s", err) } @@ -111,10 +112,10 @@ func (t *Task) Watch(start int) (chan string, error) { return watch, nil } -func tasktail(start int, watch chan string, task *Task) error { +func tasktail(ctx context.Context, start int, watch chan string, task *Task) error { for { task.client.log.Debugf("tailing log for task %s", task.UPID) - if err := task.Ping(); err != nil { + if err := task.Ping(ctx); err != nil { return err } @@ -124,7 +125,7 @@ func tasktail(start int, watch chan string, task *Task) error { return nil } - logs, err := task.Log(start, 50) + logs, err := task.Log(ctx, start, 50) if err != nil { return err } @@ -136,13 +137,13 @@ func tasktail(start int, watch chan string, task *Task) error { } } -func (t *Task) WaitFor(seconds int) error { - return t.Wait(DefaultWaitInterval, time.Duration(seconds)*time.Second) +func (t *Task) WaitFor(ctx context.Context, seconds int) error { + return t.Wait(ctx, DefaultWaitInterval, time.Duration(seconds)*time.Second) } -func (t *Task) Wait(interval, max time.Duration) error { +func (t *Task) Wait(ctx context.Context, interval, max time.Duration) error { // ping it quick to fill in all the details we need in case they're not there - err := t.Ping() + err := t.Ping(ctx) if err != nil { return err } @@ -155,7 +156,7 @@ func (t *Task) Wait(interval, max time.Duration) error { t.client.log.Debugf("timed out waiting for task %s for %fs", t.UPID, max.Seconds()) return ErrTimeout default: - if err = t.Ping(); err != nil { + if err = t.Ping(ctx); err != nil { return err } @@ -169,7 +170,7 @@ func (t *Task) Wait(interval, max time.Duration) error { } } -func (t *Task) WaitForCompleteStatus(timesNum int, steps ...int) (status bool, completed bool, err error) { +func (t *Task) WaitForCompleteStatus(ctx context.Context, timesNum int, steps ...int) (status bool, completed bool, err error) { step := 1 if len(steps) > 0 && steps[0] > 1 { step = steps[0] @@ -181,7 +182,7 @@ func (t *Task) WaitForCompleteStatus(timesNum int, steps ...int) (status bool, c case <-timeout: return default: - err = t.Ping() + err = t.Ping(ctx) if nil != err { t.client.log.Debugf("task %s ping error %+v", t.UPID, err) break diff --git a/tests/integration/cluster_test.go b/tests/integration/cluster_test.go index 43e1705..1c94715 100644 --- a/tests/integration/cluster_test.go +++ b/tests/integration/cluster_test.go @@ -1,6 +1,7 @@ package integration import ( + "context" "fmt" "regexp" "testing" @@ -10,17 +11,19 @@ import ( func TestClient_Cluster(t *testing.T) { client := ClientFromLogins() - cluster, err := client.Cluster() + ctx := context.Background() + cluster, err := client.Cluster(ctx) assert.NoError(t, err) fmt.Println(cluster) } func TestClusterResources(t *testing.T) { client := ClientFromLogins() + ctx := context.Background() // Check a call without parameters - cluster, err := client.Cluster() - rs, err := cluster.Resources() + cluster, err := client.Cluster(ctx) + rs, err := cluster.Resources(ctx) assert.Nil(t, err) assert.GreaterOrEqual(t, len(rs), 1) @@ -32,7 +35,7 @@ func TestClusterResources(t *testing.T) { // Check a call with all the valid filter values for _, rsType := range []string{"vm", "storage", "node", "sdn"} { - rs, err = cluster.Resources(rsType) + rs, err = cluster.Resources(ctx, rsType) assert.Nil(t, err) // vm and sdn may be empty as it is absolutely not mandatory @@ -57,17 +60,17 @@ func TestClusterResources(t *testing.T) { } // Check a call with more than one parameter - rs, err = cluster.Resources("bad", "call") + rs, err = cluster.Resources(ctx, "bad", "call") assert.NotNil(t, err) assert.Contains(t, err.Error(), "value 'badcall' does not have a value in the enumeration 'vm, storage, node, sdn'") // Check a call with a string parameter which is not a single word - rs, err = cluster.Resources("bad filter") + rs, err = cluster.Resources(ctx, "bad filter") assert.NotNil(t, err) assert.Contains(t, err.Error(), "value 'badfilter' does not have a value in the enumeration 'vm, storage, node, sdn'") // Check a call with a string parameter which is a word - rs, err = cluster.Resources("unknownword") + rs, err = cluster.Resources(ctx, "unknownword") assert.NotNil(t, err) assert.Contains(t, err.Error(), "bad request: 400 Parameter verification failed") } diff --git a/tests/integration/proxmox_test.go b/tests/integration/proxmox_test.go index ce0e0e6..0fb1d34 100644 --- a/tests/integration/proxmox_test.go +++ b/tests/integration/proxmox_test.go @@ -1,6 +1,7 @@ package integration import ( + "context" "crypto/tls" "encoding/json" "fmt" @@ -66,13 +67,15 @@ func init() { } td.client = ClientFromLogins() + ctx := context.Background() var err error - td.node, err = td.client.Node(td.nodeName) + + td.node, err = td.client.Node(ctx, td.nodeName) if err != nil { panic(err) } - td.storage, err = td.node.Storage(td.nodeStorage) + td.storage, err = td.node.Storage(ctx, td.nodeStorage) if err != nil { panic(err) } @@ -91,13 +94,13 @@ func downloadFile(src, dst string) error { if err != nil { return err } - defer out.Close() + defer func() { _ = out.Close() }() resp, err := http.Get(src) if err != nil { return err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() _, err = io.Copy(out, resp.Body) if err != nil { @@ -114,7 +117,7 @@ func createTestISO(file string) error { if err != nil { return err } - defer iso.Close() + defer func() { _ = iso.Close() }() fs, err := iso9660.Create(iso, 0, 0, blocksize, "") if err != nil { @@ -167,7 +170,8 @@ func ClientFromTicket() *proxmox.Client { func TestVersion(t *testing.T) { client := ClientFromLogins() - version, err := client.Version() + ctx := context.Background() + version, err := client.Version(ctx) assert.Nil(t, err) assert.NotEmpty(t, version.Version) } diff --git a/types.go b/types.go index 450ff37..e2c9ad9 100644 --- a/types.go +++ b/types.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "encoding/json" "fmt" "math" @@ -960,6 +961,7 @@ type Snapshot struct { type Pools []*Pool type Pool struct { client *Client + context context.Context PoolID string `json:"poolid,omitempty"` Comment string `json:"comment,omitempty"` Members []ClusterResource `json:"members,omitempty"` diff --git a/virtual_machine.go b/virtual_machine.go index 88e457d..3dd74aa 100644 --- a/virtual_machine.go +++ b/virtual_machine.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "fmt" "net/url" "os" @@ -27,22 +28,22 @@ const ( // DefaultAgentWaitInterval is the polling interval when waiting for agent exec commands var DefaultAgentWaitInterval = 100 * time.Millisecond -func (v *VirtualMachine) Ping() error { - return v.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/status/current", v.Node, v.VMID), &v) +func (v *VirtualMachine) Ping(ctx context.Context) error { + return v.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/current", v.Node, v.VMID), &v) } -func (v *VirtualMachine) Config(options ...VirtualMachineOption) (*Task, error) { +func (v *VirtualMachine) Config(ctx context.Context, options ...VirtualMachineOption) (*Task, error) { var upid UPID data := make(map[string]interface{}) for _, opt := range options { data[opt.Name] = opt.Value } - err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/config", v.Node, v.VMID), data, &upid) + err := v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/config", v.Node, v.VMID), data, &upid) return NewTask(upid, v.client), err } -func (v *VirtualMachine) TermProxy() (vnc *VNC, err error) { - return vnc, v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/termproxy", v.Node, v.VMID), nil, &vnc) +func (v *VirtualMachine) TermProxy(ctx context.Context) (vnc *VNC, err error) { + return vnc, v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/termproxy", v.Node, v.VMID), nil, &vnc) } func (v *VirtualMachine) HasTag(value string) bool { @@ -63,7 +64,7 @@ func (v *VirtualMachine) HasTag(value string) bool { return false } -func (v *VirtualMachine) AddTag(value string) (*Task, error) { +func (v *VirtualMachine) AddTag(ctx context.Context, value string) (*Task, error) { if v.HasTag(value) { return nil, ErrNoop } @@ -75,13 +76,13 @@ func (v *VirtualMachine) AddTag(value string) (*Task, error) { v.VirtualMachineConfig.TagsSlice = append(v.VirtualMachineConfig.TagsSlice, value) v.VirtualMachineConfig.Tags = strings.Join(v.VirtualMachineConfig.TagsSlice, TagSeperator) - return v.Config(VirtualMachineOption{ + return v.Config(ctx, VirtualMachineOption{ Name: "tags", Value: v.VirtualMachineConfig.Tags, }) } -func (v *VirtualMachine) RemoveTag(value string) (*Task, error) { +func (v *VirtualMachine) RemoveTag(ctx context.Context, value string) (*Task, error) { if !v.HasTag(value) { return nil, ErrNoop } @@ -100,7 +101,7 @@ func (v *VirtualMachine) RemoveTag(value string) (*Task, error) { } v.VirtualMachineConfig.Tags = strings.Join(v.VirtualMachineConfig.TagsSlice, TagSeperator) - return v.Config(VirtualMachineOption{ + return v.Config(ctx, VirtualMachineOption{ Name: "tags", Value: v.VirtualMachineConfig.Tags, }) @@ -114,7 +115,7 @@ func (v *VirtualMachine) SplitTags() { // mount it as a CD-ROM to be used with nocloud cloud-init. This is NOT how proxmox expects a user to do cloud-init // which can be found here: https://pve.proxmox.com/wiki/Cloud-Init_Support#:~:text=and%20meta.-,Cloud%2DInit%20specific%20Options,-cicustom%3A%20%5Bmeta // If you want to use the proxmox implementation you'll need to use the cloudinit APIs https://pve.proxmox.com/pve-docs/api-viewer/index.html#/nodes/{node}/qemu/{vmid}/cloudinit -func (v *VirtualMachine) CloudInit(device, userdata, metadata, vendordata, networkconfig string) error { +func (v *VirtualMachine) CloudInit(ctx context.Context, device, userdata, metadata, vendordata, networkconfig string) error { isoName := fmt.Sprintf(UserDataISOFormat, v.VMID) // create userdata iso file on the local fs iso, err := makeCloudInitISO(isoName, userdata, metadata, vendordata, networkconfig) @@ -126,12 +127,12 @@ func (v *VirtualMachine) CloudInit(device, userdata, metadata, vendordata, netwo // _ = os.Remove(iso.Name()) }() - node, err := v.client.Node(v.Node) + node, err := v.client.Node(ctx, v.Node) if err != nil { return err } - storage, err := node.StorageISO() + storage, err := node.StorageISO(ctx) if err != nil { return err } @@ -142,16 +143,16 @@ func (v *VirtualMachine) CloudInit(device, userdata, metadata, vendordata, netwo } // iso should only be < 5mb so wait for it and then mount it - if err := task.WaitFor(5); err != nil { + if err := task.WaitFor(ctx, 5); err != nil { return err } - _, err = v.AddTag(MakeTag(TagCloudInit)) + _, err = v.AddTag(ctx, MakeTag(TagCloudInit)) if err != nil && !IsErrNoop(err) { return err } - task, err = v.Config(VirtualMachineOption{ + task, err = v.Config(ctx, VirtualMachineOption{ Name: device, Value: fmt.Sprintf("%s:iso/%s,media=cdrom", storage.Name, isoName), }, VirtualMachineOption{ @@ -163,7 +164,7 @@ func (v *VirtualMachine) CloudInit(device, userdata, metadata, vendordata, netwo return err } - return task.WaitFor(2) + return task.WaitFor(ctx, 2) } func makeCloudInitISO(filename, userdata, metadata, vendordata, networkconfig string) (iso *os.File, err error) { @@ -231,9 +232,9 @@ func (v *VirtualMachine) IsRunning() bool { return v.Status == StatusVirtualMachineRunning && v.QMPStatus == StatusVirtualMachineRunning } -func (v *VirtualMachine) Start() (*Task, error) { +func (v *VirtualMachine) Start(ctx context.Context) (task *Task, err error) { var upid UPID - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/status/start", v.Node, v.VMID), nil, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/start", v.Node, v.VMID), nil, &upid); err != nil { return nil, err } @@ -244,27 +245,27 @@ func (v *VirtualMachine) IsStopped() bool { return v.Status == StatusVirtualMachineStopped && v.QMPStatus == StatusVirtualMachineStopped } -func (v *VirtualMachine) Reset() (task *Task, err error) { +func (v *VirtualMachine) Reset(ctx context.Context) (task *Task, err error) { var upid UPID - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/status/reset", v.Node, v.VMID), nil, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/reset", v.Node, v.VMID), nil, &upid); err != nil { return nil, err } return NewTask(upid, v.client), nil } -func (v *VirtualMachine) Shutdown() (task *Task, err error) { +func (v *VirtualMachine) Shutdown(ctx context.Context) (task *Task, err error) { var upid UPID - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/status/shutdown", v.Node, v.VMID), nil, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/shutdown", v.Node, v.VMID), nil, &upid); err != nil { return nil, err } return NewTask(upid, v.client), nil } -func (v *VirtualMachine) Stop() (task *Task, err error) { +func (v *VirtualMachine) Stop(ctx context.Context) (task *Task, err error) { var upid UPID - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/status/stop", v.Node, v.VMID), nil, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/stop", v.Node, v.VMID), nil, &upid); err != nil { return nil, err } @@ -275,9 +276,9 @@ func (v *VirtualMachine) IsPaused() bool { return v.Status == StatusVirtualMachineRunning && v.QMPStatus == StatusVirtualMachinePaused } -func (v *VirtualMachine) Pause() (task *Task, err error) { +func (v *VirtualMachine) Pause(ctx context.Context) (task *Task, err error) { var upid UPID - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/status/suspend", v.Node, v.VMID), nil, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/suspend", v.Node, v.VMID), nil, &upid); err != nil { return nil, err } @@ -288,68 +289,68 @@ func (v *VirtualMachine) IsHibernated() bool { return v.Status == StatusVirtualMachineStopped && v.QMPStatus == StatusVirtualMachineStopped && v.Lock == "suspended" } -func (v *VirtualMachine) Hibernate() (task *Task, err error) { +func (v *VirtualMachine) Hibernate(ctx context.Context) (task *Task, err error) { var upid UPID - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/status/suspend", v.Node, v.VMID), map[string]string{"todisk": "1"}, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/suspend", v.Node, v.VMID), map[string]string{"todisk": "1"}, &upid); err != nil { return nil, err } return NewTask(upid, v.client), nil } -func (v *VirtualMachine) Resume() (task *Task, err error) { +func (v *VirtualMachine) Resume(ctx context.Context) (task *Task, err error) { var upid UPID - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/status/resume", v.Node, v.VMID), nil, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/resume", v.Node, v.VMID), nil, &upid); err != nil { return nil, err } return NewTask(upid, v.client), nil } -func (v *VirtualMachine) Reboot() (task *Task, err error) { +func (v *VirtualMachine) Reboot(ctx context.Context) (task *Task, err error) { var upid UPID - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/status/reboot", v.Node, v.VMID), nil, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/status/reboot", v.Node, v.VMID), nil, &upid); err != nil { return nil, err } return NewTask(upid, v.client), nil } -func (v *VirtualMachine) Delete() (task *Task, err error) { - if ok, err := v.deleteCloudInitISO(); err != nil || !ok { +func (v *VirtualMachine) Delete(ctx context.Context) (task *Task, err error) { + if ok, err := v.deleteCloudInitISO(ctx); err != nil || !ok { return nil, err } var upid UPID - if err := v.client.Delete(fmt.Sprintf("/nodes/%s/qemu/%d", v.Node, v.VMID), &upid); err != nil { + if err = v.client.Delete(ctx, fmt.Sprintf("/nodes/%s/qemu/%d", v.Node, v.VMID), &upid); err != nil { return nil, err } return NewTask(upid, v.client), nil } -func (v *VirtualMachine) deleteCloudInitISO() (ok bool, err error) { +func (v *VirtualMachine) deleteCloudInitISO(ctx context.Context) (ok bool, err error) { if v.HasTag(MakeTag(TagCloudInit)) { - node, err := v.client.Node(v.Node) + node, err := v.client.Node(ctx, v.Node) if err != nil { return false, err } - isoStorage, err := node.StorageISO() + isoStorage, err := node.StorageISO(ctx) if err != nil { return false, err } var iso *ISO - iso, err = isoStorage.ISO(fmt.Sprintf(UserDataISOFormat, v.VMID)) + iso, err = isoStorage.ISO(ctx, fmt.Sprintf(UserDataISOFormat, v.VMID)) if err != nil { // skipping, iso not found return no error. return true, nil } - task, err := iso.Delete() + task, err := iso.Delete(ctx) if err != nil { return false, err } - if err := task.WaitFor(5); err != nil { + if err := task.WaitFor(ctx, 5); err != nil { return false, err } } @@ -357,7 +358,7 @@ func (v *VirtualMachine) deleteCloudInitISO() (ok bool, err error) { return true, nil } -func (v *VirtualMachine) Migrate(target, targetstorage string) (task *Task, err error) { +func (v *VirtualMachine) Migrate(ctx context.Context, target, targetstorage string) (task *Task, err error) { var upid UPID params := map[string]string{ "target": target, @@ -365,14 +366,14 @@ func (v *VirtualMachine) Migrate(target, targetstorage string) (task *Task, err if targetstorage != "" { params["targetstorage"] = targetstorage } - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/migrate", v.Node, v.VMID), params, &upid); err != nil { + if err := v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/migrate", v.Node, v.VMID), params, &upid); err != nil { return nil, err } return NewTask(upid, v.client), nil } -func (v *VirtualMachine) Clone(params *VirtualMachineCloneOptions) (newid int, task *Task, err error) { +func (v *VirtualMachine) Clone(ctx context.Context, params *VirtualMachineCloneOptions) (newid int, task *Task, err error) { var upid UPID if params == nil { @@ -380,27 +381,27 @@ func (v *VirtualMachine) Clone(params *VirtualMachineCloneOptions) (newid int, t } if params.NewID == 0 { - cluster, err := v.client.Cluster() + cluster, err := v.client.Cluster(ctx) if err != nil { return newid, nil, err } - newid, err = cluster.NextID() + newid, err = cluster.NextID(ctx) if err != nil { return newid, nil, err } params.NewID = newid } - if err := v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/clone", v.Node, v.VMID), params, &upid); err != nil { + if err := v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/clone", v.Node, v.VMID), params, &upid); err != nil { return newid, nil, err } return newid, NewTask(upid, v.client), nil } -func (v *VirtualMachine) ResizeDisk(disk, size string) (err error) { - err = v.client.Put(fmt.Sprintf("/nodes/%s/qemu/%d/resize", v.Node, v.VMID), map[string]string{ +func (v *VirtualMachine) ResizeDisk(ctx context.Context, disk, size string) (err error) { + err = v.client.Put(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/resize", v.Node, v.VMID), map[string]string{ "disk": disk, "size": size, }, nil) @@ -411,14 +412,14 @@ func (v *VirtualMachine) ResizeDisk(disk, size string) (err error) { return } -func (v *VirtualMachine) UnlinkDisk(diskID string, force bool) (task *Task, err error) { +func (v *VirtualMachine) UnlinkDisk(ctx context.Context, diskID string, force bool) (task *Task, err error) { var upid UPID params := map[string]string{"idlist": diskID} if force { params["force"] = "1" } - err = v.client.Put(fmt.Sprintf("/nodes/%s/qemu/%d/unlink", v.Node, v.VMID), params, &upid) + err = v.client.Put(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/unlink", v.Node, v.VMID), params, &upid) if err != nil { return } @@ -426,7 +427,7 @@ func (v *VirtualMachine) UnlinkDisk(diskID string, force bool) (task *Task, err return NewTask(upid, v.client), nil } -func (v *VirtualMachine) MoveDisk(disk string, params *VirtualMachineMoveDiskOptions) (task *Task, err error) { +func (v *VirtualMachine) MoveDisk(ctx context.Context, disk string, params *VirtualMachineMoveDiskOptions) (task *Task, err error) { var upid UPID if params == nil { @@ -437,7 +438,7 @@ func (v *VirtualMachine) MoveDisk(disk string, params *VirtualMachineMoveDiskOpt params.Disk = disk } - err = v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/move_disk", v.Node, v.VMID), params, &upid) + err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/move_disk", v.Node, v.VMID), params, &upid) if err != nil { return } @@ -445,14 +446,14 @@ func (v *VirtualMachine) MoveDisk(disk string, params *VirtualMachineMoveDiskOpt return NewTask(upid, v.client), nil } -func (v *VirtualMachine) AgentGetNetworkIFaces() (iFaces []*AgentNetworkIface, err error) { - node, err := v.client.Node(v.Node) +func (v *VirtualMachine) AgentGetNetworkIFaces(ctx context.Context) (iFaces []*AgentNetworkIface, err error) { + node, err := v.client.Node(ctx, v.Node) if err != nil { return } networks := map[string][]*AgentNetworkIface{} - err = v.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/agent/network-get-interfaces", node.Name, v.VMID), &networks) + err = v.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/agent/network-get-interfaces", node.Name, v.VMID), &networks) if err != nil { return } @@ -468,13 +469,13 @@ func (v *VirtualMachine) AgentGetNetworkIFaces() (iFaces []*AgentNetworkIface, e return } -func (v *VirtualMachine) WaitForAgent(seconds int) error { +func (v *VirtualMachine) WaitForAgent(ctx context.Context, seconds int) error { timeout := time.After(time.Duration(seconds) * time.Second) ticker := time.NewTicker(DefaultWaitInterval) defer ticker.Stop() for { - _, err := v.AgentOsInfo() + _, err := v.AgentOsInfo(ctx) if err == nil { return nil } @@ -490,8 +491,8 @@ func (v *VirtualMachine) WaitForAgent(seconds int) error { } } -func (v *VirtualMachine) AgentExec(command, inputData string) (pid int, err error) { - err = v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/agent/exec", v.Node, v.VMID), +func (v *VirtualMachine) AgentExec(ctx context.Context, command, inputData string) (pid int, err error) { + err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/agent/exec", v.Node, v.VMID), map[string]string{ "command": command, "input-data": inputData, @@ -501,8 +502,8 @@ func (v *VirtualMachine) AgentExec(command, inputData string) (pid int, err erro return } -func (v *VirtualMachine) AgentExecStatus(pid int) (status *AgentExecStatus, err error) { - err = v.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/agent/exec-status?pid=%d", v.Node, v.VMID, pid), &status) +func (v *VirtualMachine) AgentExecStatus(ctx context.Context, pid int) (status *AgentExecStatus, err error) { + err = v.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/agent/exec-status?pid=%d", v.Node, v.VMID, pid), &status) if err != nil { return nil, err } @@ -510,13 +511,13 @@ func (v *VirtualMachine) AgentExecStatus(pid int) (status *AgentExecStatus, err return } -func (v *VirtualMachine) WaitForAgentExecExit(pid, seconds int) (*AgentExecStatus, error) { +func (v *VirtualMachine) WaitForAgentExecExit(ctx context.Context, pid, seconds int) (*AgentExecStatus, error) { timeout := time.After(time.Duration(seconds) * time.Second) ticker := time.NewTicker(DefaultAgentWaitInterval) defer ticker.Stop() for { - status, err := v.AgentExecStatus(pid) + status, err := v.AgentExecStatus(ctx, pid) if err != nil { return nil, err } @@ -532,9 +533,9 @@ func (v *VirtualMachine) WaitForAgentExecExit(pid, seconds int) (*AgentExecStatu } } -func (v *VirtualMachine) AgentOsInfo() (info *AgentOsInfo, err error) { +func (v *VirtualMachine) AgentOsInfo(ctx context.Context) (info *AgentOsInfo, err error) { results := map[string]*AgentOsInfo{} - err = v.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/agent/get-osinfo", v.Node, v.VMID), &results) + err = v.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/agent/get-osinfo", v.Node, v.VMID), &results) if err != nil { return } @@ -548,53 +549,53 @@ func (v *VirtualMachine) AgentOsInfo() (info *AgentOsInfo, err error) { return } -func (v *VirtualMachine) AgentSetUserPassword(password string, username string) error { - return v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/agent/set-user-password", v.Node, v.VMID), map[string]string{"password": password, "username": username}, nil) +func (v *VirtualMachine) AgentSetUserPassword(ctx context.Context, password string, username string) error { + return v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/agent/set-user-password", v.Node, v.VMID), map[string]string{"password": password, "username": username}, nil) } -func (v *VirtualMachine) FirewallOptionGet() (firewallOption *FirewallVirtualMachineOption, err error) { - err = v.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/firewall/options", v.Node, v.VMID), firewallOption) +func (v *VirtualMachine) FirewallOptionGet(ctx context.Context) (firewallOption *FirewallVirtualMachineOption, err error) { + err = v.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/firewall/options", v.Node, v.VMID), firewallOption) return } -func (v *VirtualMachine) FirewallOptionSet(firewallOption *FirewallVirtualMachineOption) error { - return v.client.Put(fmt.Sprintf("/nodes/%s/qemu/%d/firewall/options", v.Node, v.VMID), firewallOption, nil) +func (v *VirtualMachine) FirewallOptionSet(ctx context.Context, firewallOption *FirewallVirtualMachineOption) error { + return v.client.Put(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/firewall/options", v.Node, v.VMID), firewallOption, nil) } -func (v *VirtualMachine) FirewallGetRules() (rules []*FirewallRule, err error) { - err = v.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/firewall/rules", v.Node, v.VMID), &rules) +func (v *VirtualMachine) FirewallGetRules(ctx context.Context) (rules []*FirewallRule, err error) { + err = v.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/firewall/rules", v.Node, v.VMID), &rules) return } -func (v *VirtualMachine) FirewallRulesCreate(rule *FirewallRule) error { - return v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/firewall/rules", v.Node, v.VMID), rule, nil) +func (v *VirtualMachine) FirewallRulesCreate(ctx context.Context, rule *FirewallRule) error { + return v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/firewall/rules", v.Node, v.VMID), rule, nil) } -func (v *VirtualMachine) FirewallRulesUpdate(rule *FirewallRule) error { - return v.client.Put(fmt.Sprintf("/nodes/%s/qemu/%d/firewall/rules/%d", v.Node, v.VMID, rule.Pos), rule, nil) +func (v *VirtualMachine) FirewallRulesUpdate(ctx context.Context, rule *FirewallRule) error { + return v.client.Put(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/firewall/rules/%d", v.Node, v.VMID, rule.Pos), rule, nil) } -func (v *VirtualMachine) FirewallRulesDelete(rulePos int) error { - return v.client.Delete(fmt.Sprintf("/nodes/%s/qemu/%d/firewall/rules/%d", v.Node, v.VMID, rulePos), nil) +func (v *VirtualMachine) FirewallRulesDelete(ctx context.Context, rulePos int) error { + return v.client.Delete(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/firewall/rules/%d", v.Node, v.VMID, rulePos), nil) } -func (v *VirtualMachine) NewSnapshot(name string) (task *Task, err error) { +func (v *VirtualMachine) NewSnapshot(ctx context.Context, name string) (task *Task, err error) { var upid UPID - if err = v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/snapshot", v.Node, v.VMID), map[string]string{"snapname": name}, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/snapshot", v.Node, v.VMID), map[string]string{"snapname": name}, &upid); err != nil { return nil, err } return NewTask(upid, v.client), nil } -func (v *VirtualMachine) Snapshots() (snapshots []*Snapshot, err error) { - err = v.client.Get(fmt.Sprintf("/nodes/%s/qemu/%d/snapshot", v.Node, v.VMID), &snapshots) +func (v *VirtualMachine) Snapshots(ctx context.Context) (snapshots []*Snapshot, err error) { + err = v.client.Get(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/snapshot", v.Node, v.VMID), &snapshots) return } -func (v *VirtualMachine) SnapshotRollback(name string) (task *Task, err error) { +func (v *VirtualMachine) SnapshotRollback(ctx context.Context, name string) (task *Task, err error) { var upid UPID - if err = v.client.Post(fmt.Sprintf("/nodes/%s/qemu/%d/snapshot/%s/rollback", v.Node, v.VMID, name), nil, &upid); err != nil { + if err = v.client.Post(ctx, fmt.Sprintf("/nodes/%s/qemu/%d/snapshot/%s/rollback", v.Node, v.VMID, name), nil, &upid); err != nil { return nil, err } @@ -603,7 +604,7 @@ func (v *VirtualMachine) SnapshotRollback(name string) (task *Task, err error) { // RRDData takes a timeframe enum and an optional consolidation function // usage: vm.RRDData(HOURLY) or vm.RRDData(HOURLY, AVERAGE) -func (v *VirtualMachine) RRDData(timeframe Timeframe, consolidationFunction ...ConsolidationFunction) (rrddata []*RRDData, err error) { +func (v *VirtualMachine) RRDData(ctx context.Context, timeframe Timeframe, consolidationFunction ...ConsolidationFunction) (rrddata []*RRDData, err error) { u := url.URL{Path: fmt.Sprintf("/nodes/%s/qemu/%d/rrddata", v.Node, v.VMID)} // consolidation functions are variadic because they're optional, putting everything into one string and sending that @@ -620,6 +621,6 @@ func (v *VirtualMachine) RRDData(timeframe Timeframe, consolidationFunction ...C params.Add("timeframe", string(timeframe)) u.RawQuery = params.Encode() - err = v.client.Get(u.String(), &rrddata) + err = v.client.Get(ctx, u.String(), &rrddata) return } diff --git a/virtual_machine_test.go b/virtual_machine_test.go index fd5f4eb..10d08a8 100644 --- a/virtual_machine_test.go +++ b/virtual_machine_test.go @@ -1,6 +1,7 @@ package proxmox import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -12,13 +13,14 @@ func TestVirtualMachine_Ping(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() vm := VirtualMachine{ client: client, VMID: 101, Node: "node1", } - assert.Nil(t, vm.Ping()) + assert.Nil(t, vm.Ping(ctx)) assert.Equal(t, StringOrUint64(101), vm.VMID) } @@ -26,13 +28,14 @@ func TestVirtualMachine_RRDData(t *testing.T) { mocks.On(mockConfig) defer mocks.Off() client := mockClient() + ctx := context.Background() vm := VirtualMachine{ client: client, VMID: 101, Node: "node1", } - rdddata, err := vm.RRDData(TimeframeHour) + rdddata, err := vm.RRDData(ctx, TimeframeHour) assert.Nil(t, err) assert.Len(t, rdddata, 70) }