diff --git a/.golangci.yaml b/.golangci.yaml index 682abf3..cf83e85 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -130,6 +130,8 @@ issues: - dupl - gosec - lll + - goconst + - dogsled run: timeout: 5m diff --git a/container_registry.go b/container_registry.go new file mode 100644 index 0000000..00d7ba8 --- /dev/null +++ b/container_registry.go @@ -0,0 +1,425 @@ +package govultr + +import ( + "context" + "fmt" + "net/http" + + "github.com/google/go-querystring/query" +) + +const vcrPath = "/v2/registry" +const vcrListPath = "/v2/registries" + +// ContainerRegistryService is the interface to interact with the container +// registry endpoints on the Vultr API. Link : +// https://www.vultr.com/api/#tag/Container-Registry +type ContainerRegistryService interface { + Create(ctx context.Context, createReq *ContainerRegistryReq) (*ContainerRegistry, *http.Response, error) + Get(ctx context.Context, vcrID string) (*ContainerRegistry, *http.Response, error) + Update(ctx context.Context, vcrID string, updateReq *ContainerRegistryUpdateReq) (*ContainerRegistry, *http.Response, error) + Delete(ctx context.Context, vcrID string) error + List(ctx context.Context, options *ListOptions) ([]ContainerRegistry, *Meta, *http.Response, error) + ListRepositories(ctx context.Context, vcrID string, options *ListOptions) ([]ContainerRegistryRepo, *Meta, *http.Response, error) + GetRepository(ctx context.Context, vcrID, imageName string) (*ContainerRegistryRepo, *http.Response, error) + UpdateRepository(ctx context.Context, vcrID, imageName string, updateReq *ContainerRegistryRepoUpdateReq) (*ContainerRegistryRepo, *http.Response, error) //nolint:lll + DeleteRepository(ctx context.Context, vcrID, imageName string) error + CreateDockerCredentials(ctx context.Context, vcrID string, createOptions *DockerCredentialsOpt) (*ContainerRegistryDockerCredentials, *http.Response, error) //nolint:lll + ListRegions(ctx context.Context, options *ListOptions) ([]ContainerRegistryRegion, *Meta, *http.Response, error) + ListPlans(ctx context.Context) (*ContainerRegistryPlans, *http.Response, error) +} + +// ContainerRegistryServiceHandler handles interaction between the container +// registry service and the Vultr API. +type ContainerRegistryServiceHandler struct { + client *Client +} + +// ContainerRegistry represents a Vultr container registry subscription. +type ContainerRegistry struct { + ID string `json:"id"` + Name string `json:"name"` + URN string `json:"urn"` + Storage ContainerRegistryStorage `json:"storage"` + DateCreated string `json:"date_created"` + Public bool `json:"public"` + RootUser ContainerRegistryUser `json:"root_user"` + Metadata ContainerRegistryMetadata `json:"metadata"` +} + +type containerRegistries struct { + ContainerRegistries []ContainerRegistry `json:"registries"` + Meta *Meta `json:"meta"` +} + +// ContainerRegistryStorage represents the storage usage and limit +type ContainerRegistryStorage struct { + Used ContainerRegistryStorageCount `json:"used"` + Allowed ContainerRegistryStorageCount `json:"allowed"` +} + +// ContainerRegistryStorageCount represents the different storage usage counts +type ContainerRegistryStorageCount struct { + Bytes float32 `json:"bytes"` + MegaBytes float32 `json:"mb"` + GigaBytes float32 `json:"gb"` + TeraBytes float32 `json:"tb"` + DateModified string `json:"updated_at"` +} + +// ContainerRegistryUser contains the user data +type ContainerRegistryUser struct { + ID int `json:"id"` + UserName string `json:"username"` + Password string `json:"password"` + Root bool `json:"root"` + DateCreated string `json:"added_at"` + DateModified string `json:"updated_at"` +} + +// ContainerRegistryMetadata contains the meta data for the registry +type ContainerRegistryMetadata struct { + Region ContainerRegistryRegion `json:"region"` + Subscription ContainerRegistrySubscription `json:"subscription"` +} + +// ContainerRegistrySubscription contains the subscription information for the +// registry +type ContainerRegistrySubscription struct { + Billing ContainerRegistrySubscriptionBilling `json:"billing"` +} + +// ContainerRegistrySubscriptionBilling represents the subscription billing +// data on the registry +type ContainerRegistrySubscriptionBilling struct { + MonthlyPrice float32 `json:"monthly_price"` + PendingCharges float32 `json:"pending_charges"` +} + +// ContainerRegistryReq represents the data used to create a registry +type ContainerRegistryReq struct { + Name string `json:"name"` + Public bool `json:"public"` + Region string `json:"region"` + Plan string `json:"plan"` +} + +// ContainerRegistryUpdateReq represents the data used to update a registry +type ContainerRegistryUpdateReq struct { + Public *bool `json:"public"` + Plan *string `json:"plan"` +} + +// ContainerRegistryRepo represents the data of a registry repository +type ContainerRegistryRepo struct { + Name string `json:"name"` + Image string `json:"image"` + Description string `json:"description"` + DateCreated string `json:"added_at"` + DateModified string `json:"updated_at"` + PullCount int `json:"pull_count"` + ArtifactCount int `json:"artifact_count"` +} + +type containerRegistryRepos struct { + Repositories []ContainerRegistryRepo `json:"repositories"` + Meta *Meta `json:"meta"` +} + +// ContainerRegistryRepoUpdateReq is the data to update a registry repository +type ContainerRegistryRepoUpdateReq struct { + Description string `json:"description"` +} + +// DockerCredentialsOpt contains the options used to create Docker credentials +type DockerCredentialsOpt struct { + ExpirySeconds *int + WriteAccess *bool +} + +// ContainerRegistryDockerCredentials represents the byte array of character +// data returned after creating a Docker credential +type ContainerRegistryDockerCredentials []byte + +// UnmarshalJSON is a custom unmarshal function for +// ContainerRegistryDockerCredentials +func (c *ContainerRegistryDockerCredentials) UnmarshalJSON(b []byte) error { + *c = b + return nil +} + +// String converts the ContainerRegistryDockerCredentials to a string +func (c *ContainerRegistryDockerCredentials) String() string { + return string(*c) +} + +// ContainerRegistryRegion represents the region data +type ContainerRegistryRegion struct { + ID int `json:"id"` + Name string `json:"name"` + URN string `json:"urn"` + BaseURL string `json:"base_url"` + Public bool `json:"public"` + DateCreated string `json:"added_at"` + DateModified string `json:"updated_at"` + DataCenter ContainerRegistryRegionDataCenter `json:"data_center"` +} + +// ContainerRegistryRegionDataCenter is the datacenter info for a given region +type ContainerRegistryRegionDataCenter struct { + ID int `json:"id"` + Name string `json:"name"` + SiteCode string `json:"site_code"` + Region string `json:"region"` + Country string `json:"country"` + Continent string `json:"continent"` + Description string `json:"description"` + Airport string `json:"airport"` +} + +type containerRegistryRegions struct { + Regions []ContainerRegistryRegion `json:"regions"` + Meta *Meta `json:"meta"` +} + +// ContainerRegistryPlans contains all plan types +type ContainerRegistryPlans struct { + Plans ContainerRegistryPlanTypes `json:"plans"` +} + +// ContainerRegistryPlanTypes represent the different plan types +type ContainerRegistryPlanTypes struct { + StartUp ContainerRegistryPlan `json:"start_up"` + Business ContainerRegistryPlan `json:"business"` + Premium ContainerRegistryPlan `json:"premium"` + Enterprise ContainerRegistryPlan `json:"enterprise"` +} + +// ContainerRegistryPlan represent the plan data +type ContainerRegistryPlan struct { + VanityName string `json:"vanity_name"` + MaxStorageMB int `json:"max_storage_mb"` + MonthlyPrice int `json:"monthly_price"` +} + +// Get retrieves a contrainer registry by ID +func (h *ContainerRegistryServiceHandler) Get(ctx context.Context, id string) (*ContainerRegistry, *http.Response, error) { + req, errReq := h.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s", vcrPath, id), nil) + if errReq != nil { + return nil, nil, errReq + } + + vcr := new(ContainerRegistry) + resp, errResp := h.client.DoWithContext(ctx, req, &vcr) + if errResp != nil { + return nil, resp, errResp + } + + return vcr, resp, nil +} + +// List retrieves the list of all container registries +func (h *ContainerRegistryServiceHandler) List(ctx context.Context, options *ListOptions) ([]ContainerRegistry, *Meta, *http.Response, error) { //nolint:lll,dupl + req, errReq := h.client.NewRequest(ctx, http.MethodGet, vcrListPath, nil) + if errReq != nil { + return nil, nil, nil, errReq + } + + qStrings, errQ := query.Values(options) + if errQ != nil { + return nil, nil, nil, errQ + } + + req.URL.RawQuery = qStrings.Encode() + + vcrs := new(containerRegistries) + resp, errResp := h.client.DoWithContext(ctx, req, &vcrs) + if errResp != nil { + return nil, nil, resp, errResp + } + + return vcrs.ContainerRegistries, vcrs.Meta, resp, nil +} + +// Create creates a container registry +func (h *ContainerRegistryServiceHandler) Create(ctx context.Context, createReq *ContainerRegistryReq) (*ContainerRegistry, *http.Response, error) { //nolint:lll + req, errReq := h.client.NewRequest(ctx, http.MethodPost, vcrPath, createReq) + if errReq != nil { + return nil, nil, errReq + } + + vcr := new(ContainerRegistry) + resp, errResp := h.client.DoWithContext(ctx, req, &vcr) + if errResp != nil { + return nil, resp, errResp + } + + return vcr, resp, nil +} + +// Update will update an existing container registry +func (h *ContainerRegistryServiceHandler) Update(ctx context.Context, vcrID string, updateReq *ContainerRegistryUpdateReq) (*ContainerRegistry, *http.Response, error) { //nolint:lll + req, errReq := h.client.NewRequest(ctx, http.MethodPut, fmt.Sprintf("%s/%s", vcrPath, vcrID), updateReq) + if errReq != nil { + return nil, nil, errReq + } + + vcr := new(ContainerRegistry) + resp, errResp := h.client.DoWithContext(ctx, req, &vcr) + if errResp != nil { + return nil, resp, errResp + } + + return vcr, resp, nil +} + +// Delete will delete a container registry +func (h *ContainerRegistryServiceHandler) Delete(ctx context.Context, vcrID string) error { + req, errReq := h.client.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/%s", vcrPath, vcrID), nil) + if errReq != nil { + return errReq + } + + _, errResp := h.client.DoWithContext(ctx, req, nil) + if errResp != nil { + return errResp + } + + return nil +} + +// ListRepositories will get a list of the repositories for a existing +// container registry +func (h *ContainerRegistryServiceHandler) ListRepositories(ctx context.Context, vcrID string, options *ListOptions) ([]ContainerRegistryRepo, *Meta, *http.Response, error) { //nolint:lll,dupl + req, errReq := h.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/repositories", vcrPath, vcrID), nil) + if errReq != nil { + return nil, nil, nil, errReq + } + + qStrings, errQ := query.Values(options) + if errQ != nil { + return nil, nil, nil, errQ + } + + req.URL.RawQuery = qStrings.Encode() + + vcrRepos := new(containerRegistryRepos) + resp, errResp := h.client.DoWithContext(ctx, req, &vcrRepos) + if errResp != nil { + return nil, nil, resp, errResp + } + + return vcrRepos.Repositories, vcrRepos.Meta, resp, nil +} + +// GetRepository will return an existing repository of the requested registry +// ID and image name +func (h *ContainerRegistryServiceHandler) GetRepository(ctx context.Context, vcrID, imageName string) (*ContainerRegistryRepo, *http.Response, error) { //nolint:lll + req, errReq := h.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/repository/%s", vcrPath, vcrID, imageName), nil) + if errReq != nil { + return nil, nil, errReq + } + + vcrRepo := new(ContainerRegistryRepo) + resp, errResp := h.client.DoWithContext(ctx, req, &vcrRepo) + if errResp != nil { + return nil, resp, errResp + } + + return vcrRepo, resp, nil +} + +// UpdateRepository allows updating the repository with the specified registry +// ID and image name +func (h *ContainerRegistryServiceHandler) UpdateRepository(ctx context.Context, vcrID, imageName string, updateReq *ContainerRegistryRepoUpdateReq) (*ContainerRegistryRepo, *http.Response, error) { //nolint: lll + req, errReq := h.client.NewRequest(ctx, http.MethodPut, fmt.Sprintf("%s/%s/repository/%s", vcrPath, vcrID, imageName), updateReq) + if errReq != nil { + return nil, nil, errReq + } + + vcrRepo := new(ContainerRegistryRepo) + resp, errResp := h.client.DoWithContext(ctx, req, &vcrRepo) + if errResp != nil { + return nil, resp, errResp + } + + return vcrRepo, resp, nil +} + +// DeleteRepository remove a repository from the container registry +func (h *ContainerRegistryServiceHandler) DeleteRepository(ctx context.Context, vcrID, imageName string) error { + req, errReq := h.client.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/%s/repository/%s", vcrPath, vcrID, imageName), nil) + if errReq != nil { + return errReq + } + + _, errResp := h.client.DoWithContext(ctx, req, nil) + if errResp != nil { + return errResp + } + + return nil +} + +// CreateDockerCredentials will create new Docker credentials used by the +// Docker CLI +func (h *ContainerRegistryServiceHandler) CreateDockerCredentials(ctx context.Context, vcrID string, createOptions *DockerCredentialsOpt) (*ContainerRegistryDockerCredentials, *http.Response, error) { //nolint:lll + url := fmt.Sprintf("%s/%s/docker-credentials", vcrPath, vcrID) + req, errReq := h.client.NewRequest(ctx, http.MethodOptions, url, nil) + if errReq != nil { + return nil, nil, errReq + } + + queryParam := req.URL.Query() + if createOptions.ExpirySeconds != nil { + queryParam.Add("expiry_seconds", fmt.Sprintf("%d", createOptions.ExpirySeconds)) + } + + if createOptions.WriteAccess != nil { + queryParam.Add("read_write", fmt.Sprintf("%t", *createOptions.WriteAccess)) + } + + req.URL.RawQuery = queryParam.Encode() + + creds := new(ContainerRegistryDockerCredentials) + resp, errResp := h.client.DoWithContext(ctx, req, &creds) + if errResp != nil { + return nil, nil, errResp + } + + return creds, resp, nil +} + +// ListRegions will return a list of regions relevant to the container registry +// API operations +func (h *ContainerRegistryServiceHandler) ListRegions(ctx context.Context, options *ListOptions) ([]ContainerRegistryRegion, *Meta, *http.Response, error) { //nolint:lll + req, errReq := h.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/region/list", vcrPath), nil) + if errReq != nil { + return nil, nil, nil, errReq + } + + vcrRegions := new(containerRegistryRegions) + resp, errResp := h.client.DoWithContext(ctx, req, &vcrRegions) + if errResp != nil { + return nil, nil, resp, errResp + } + + return vcrRegions.Regions, vcrRegions.Meta, resp, nil +} + +// ListPlans returns a list of plans relevant to the container registry +// offerings +func (h *ContainerRegistryServiceHandler) ListPlans(ctx context.Context) (*ContainerRegistryPlans, *http.Response, error) { + req, errReq := h.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/plan/list", vcrPath), nil) + if errReq != nil { + return nil, nil, errReq + } + + vcrPlans := new(ContainerRegistryPlans) + resp, errResp := h.client.DoWithContext(ctx, req, &vcrPlans) + if errResp != nil { + return nil, resp, errResp + } + + return vcrPlans, resp, nil +} diff --git a/container_registry_test.go b/container_registry_test.go new file mode 100644 index 0000000..75d1537 --- /dev/null +++ b/container_registry_test.go @@ -0,0 +1,1095 @@ +package govultr + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestVCRServiceHandler_Create(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc(vcrPath, func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "id": "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + "name": "govultrtest", + "region": "sjc", + "urn": "sjc.vultrcr.com/govultrtest", + "storage": { + "used": { + "updated_at": "2023-11-09 13:37:12", + "bytes": 0, + "mb": 0, + "gb": 0, + "tb": 0 + }, + "allowed": { + "bytes": 21474836480, + "mb": 20480, + "gb": 20, + "tb": 0.02 + } + }, + "date_created": "2023-11-09 13:37:12", + "public": false, + "root_user": { + "id": 635, + "username": "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + "password": "5dEr33smkSYauMmRWusNFk9HpweL5CevpnFr", + "root": true, + "added_at": "2023-11-09 13:37:12", + "updated_at": "2023-11-09 13:37:12" + }, + "metadata": { + "region": { + "id": 3, + "name": "sjc", + "urn": "sjc.vultrcr.com", + "base_url": "https://sjc.vultrcr.com", + "public": true, + "added_at": "2023-09-14 09:09:16", + "updated_at": "2023-09-14 09:09:16", + "data_center": { + "id": 12, + "name": "Silicon Valley", + "site_code": "SJC2", + "region": "West", + "country": "US", + "continent": "North America", + "description": "Silicon Valley, California", + "airport": "SJC" + } + }, + "subscription": { + "billing": { + "monthly_price": 5, + "pending_charges": 0 + } + } + } +}` + fmt.Fprint(writer, response) + }) + + req := &ContainerRegistryReq{ + Name: "govultrtest", + Public: false, + Region: "sjc", + Plan: "business", + } + + vcr, _, err := client.ContainerRegistry.Create(ctx, req) + if err != nil { + t.Errorf("ContainerRegistry.Create returned %v", err) + } + + expected := &ContainerRegistry{ + ID: "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + Name: "govultrtest", + URN: "sjc.vultrcr.com/govultrtest", + Storage: ContainerRegistryStorage{ + Used: ContainerRegistryStorageCount{ + Bytes: 0, + MegaBytes: 0, + GigaBytes: 0, + TeraBytes: 0, + DateModified: "2023-11-09 13:37:12", + }, + Allowed: ContainerRegistryStorageCount{ + Bytes: 21474836480, + MegaBytes: 20480, + GigaBytes: 20, + TeraBytes: 0.02, + DateModified: "", + }, + }, + DateCreated: "2023-11-09 13:37:12", + Public: false, + RootUser: ContainerRegistryUser{ + ID: 635, + UserName: "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + Password: "5dEr33smkSYauMmRWusNFk9HpweL5CevpnFr", + Root: true, + DateCreated: "2023-11-09 13:37:12", + DateModified: "2023-11-09 13:37:12", + }, + Metadata: ContainerRegistryMetadata{ + Region: ContainerRegistryRegion{ + ID: 3, + Name: "sjc", + URN: "sjc.vultrcr.com", + BaseURL: "https://sjc.vultrcr.com", + Public: true, + DateCreated: "2023-09-14 09:09:16", + DateModified: "2023-09-14 09:09:16", + DataCenter: ContainerRegistryRegionDataCenter{ + ID: 12, + Name: "Silicon Valley", + SiteCode: "SJC2", + Region: "West", + Country: "US", + Continent: "North America", + Description: "Silicon Valley, California", + Airport: "SJC", + }, + }, + Subscription: ContainerRegistrySubscription{ + Billing: ContainerRegistrySubscriptionBilling{ + MonthlyPrice: 5, + PendingCharges: 0, + }, + }, + }, + } + + if !reflect.DeepEqual(vcr, expected) { + t.Errorf("ContainerRegistry.Create returned %+v, expected %+v", vcr, expected) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + _, _, err = client.ContainerRegistry.Create(c, req) + if err == nil { + t.Error("ContainerRegistry.Create returned nil") + } +} + +func TestVCRServiceHandler_List(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc(vcrListPath, func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "registries": [ + { + "id": "297cfaff-cb5a-4b7c-ac2e-407fcdbd643e", + "name": "govultrtest", + "region": "sjc", + "urn": "sjc.vultrcr.com/govultrtest", + "storage": { + "used": { + "updated_at": "2023-11-09 23:09:24", + "bytes": 0, + "mb": 0, + "gb": 0, + "tb": 0 + }, + "allowed": { + "bytes": 21474836480, + "mb": 20480, + "gb": 20, + "tb": 0.02 + } + }, + "date_created": "2023-11-09 17:32:17", + "public": true, + "root_user": { + "id": 639, + "username": "297cfaff-cb5a-4b7c-ac2e-407fcdbd643e", + "password": "Je2S9SkjrowwMtP933SSaxZG4BPR7D8Au33P", + "root": true, + "added_at": "2023-11-09 17:32:17", + "updated_at": "2023-11-09 17:32:17" + }, + "metadata": { + "region": { + "id": 3, + "name": "sjc", + "urn": "sjc.vultrcr.com", + "base_url": "https://sjc.vultrcr.com", + "public": true, + "added_at": "2023-09-14 09:09:16", + "updated_at": "2023-09-14 09:09:16", + "data_center": { + "id": 12, + "name": "Silicon Valley", + "site_code": "SJC2", + "region": "West", + "country": "US", + "continent": "North America", + "description": "Silicon Valley, California", + "airport": "SJC" + } + }, + "subscription": { + "billing": { + "monthly_price": 5, + "pending_charges": 0.01 + } + } + } + }, + { + "id": "c247a5d7-b3e1-468c-bcba-c23d0716ffc8", + "name": "govultrtest2", + "region": "sjc", + "urn": "sjc.vultrcr.com/govultrtest2", + "storage": { + "used": { + "updated_at": "2023-11-09 23:09:24", + "bytes": 0, + "mb": 0, + "gb": 0, + "tb": 0 + }, + "allowed": { + "bytes": 21474836480, + "mb": 20480, + "gb": 20, + "tb": 0.02 + } + }, + "date_created": "2023-11-09 17:33:13", + "public": true, + "root_user": { + "id": 640, + "username": "c247a5d7-b3e1-468c-bcba-c23d0716ffc8", + "password": "c9NhkeH7aeF7zj3cRFHbMFizxEik4rhWYGdW", + "root": true, + "added_at": "2023-11-09 17:33:13", + "updated_at": "2023-11-09 17:33:13" + }, + "metadata": { + "region": { + "id": 3, + "name": "sjc", + "urn": "sjc.vultrcr.com", + "base_url": "https://sjc.vultrcr.com", + "public": true, + "added_at": "2023-09-14 09:09:16", + "updated_at": "2023-09-14 09:09:16", + "data_center": { + "id": 12, + "name": "Silicon Valley", + "site_code": "SJC2", + "region": "West", + "country": "US", + "continent": "North America", + "description": "Silicon Valley, California", + "airport": "SJC" + } + }, + "subscription": { + "billing": { + "monthly_price": 5, + "pending_charges": 0.01 + } + } + } + } + ], + "meta": { + "total": 2, + "links": { + "next": "", + "prev": "" + } + } +}` + fmt.Fprint(writer, response) + }) + + vcrs, meta, _, err := client.ContainerRegistry.List(ctx, nil) + if err != nil { + t.Errorf("ContainerRegistry.List returned %+v", err) + } + + expected := []ContainerRegistry{ + { + ID: "297cfaff-cb5a-4b7c-ac2e-407fcdbd643e", + Name: "govultrtest", + URN: "sjc.vultrcr.com/govultrtest", + Storage: ContainerRegistryStorage{ + Used: ContainerRegistryStorageCount{ + Bytes: 0, + MegaBytes: 0, + GigaBytes: 0, + TeraBytes: 0, + DateModified: "2023-11-09 23:09:24", + }, + Allowed: ContainerRegistryStorageCount{ + Bytes: 21474836480, + MegaBytes: 20480, + GigaBytes: 20, + TeraBytes: 0.02, + DateModified: "", + }, + }, + DateCreated: "2023-11-09 17:32:17", + Public: true, + RootUser: ContainerRegistryUser{ + ID: 639, + UserName: "297cfaff-cb5a-4b7c-ac2e-407fcdbd643e", + Password: "Je2S9SkjrowwMtP933SSaxZG4BPR7D8Au33P", + Root: true, + DateCreated: "2023-11-09 17:32:17", + DateModified: "2023-11-09 17:32:17", + }, + Metadata: ContainerRegistryMetadata{ + Region: ContainerRegistryRegion{ + ID: 3, + Name: "sjc", + URN: "sjc.vultrcr.com", + BaseURL: "https://sjc.vultrcr.com", + Public: true, + DateCreated: "2023-09-14 09:09:16", + DateModified: "2023-09-14 09:09:16", + DataCenter: ContainerRegistryRegionDataCenter{ + ID: 12, + Name: "Silicon Valley", + SiteCode: "SJC2", + Region: "West", + Country: "US", + Continent: "North America", + Description: "Silicon Valley, California", + Airport: "SJC", + }, + }, + Subscription: ContainerRegistrySubscription{ + Billing: ContainerRegistrySubscriptionBilling{ + MonthlyPrice: 5, + PendingCharges: 0.01, + }, + }, + }, + }, + { + ID: "c247a5d7-b3e1-468c-bcba-c23d0716ffc8", + Name: "govultrtest2", + URN: "sjc.vultrcr.com/govultrtest2", + Storage: ContainerRegistryStorage{ + Used: ContainerRegistryStorageCount{ + Bytes: 0, + MegaBytes: 0, + GigaBytes: 0, + TeraBytes: 0, + DateModified: "2023-11-09 23:09:24", + }, + Allowed: ContainerRegistryStorageCount{ + Bytes: 21474836480, + MegaBytes: 20480, + GigaBytes: 20, + TeraBytes: 0.02, + DateModified: "", + }, + }, + DateCreated: "2023-11-09 17:33:13", + Public: true, + RootUser: ContainerRegistryUser{ + ID: 640, + UserName: "c247a5d7-b3e1-468c-bcba-c23d0716ffc8", + Password: "c9NhkeH7aeF7zj3cRFHbMFizxEik4rhWYGdW", + Root: true, + DateCreated: "2023-11-09 17:33:13", + DateModified: "2023-11-09 17:33:13", + }, + Metadata: ContainerRegistryMetadata{ + Region: ContainerRegistryRegion{ + ID: 3, + Name: "sjc", + URN: "sjc.vultrcr.com", + BaseURL: "https://sjc.vultrcr.com", + Public: true, + DateCreated: "2023-09-14 09:09:16", + DateModified: "2023-09-14 09:09:16", + DataCenter: ContainerRegistryRegionDataCenter{ + ID: 12, + Name: "Silicon Valley", + SiteCode: "SJC2", + Region: "West", + Country: "US", + Continent: "North America", + Description: "Silicon Valley, California", + Airport: "SJC", + }, + }, + Subscription: ContainerRegistrySubscription{ + Billing: ContainerRegistrySubscriptionBilling{ + MonthlyPrice: 5, + PendingCharges: 0.01, + }, + }, + }, + }, + } + + expectedMeta := &Meta{ + Total: 2, + Links: &Links{ + Next: "", + Prev: "", + }, + } + + if !reflect.DeepEqual(vcrs, expected) { + t.Errorf("ContainerRegistry.List returned %+v, expected %+v", vcrs, expected) + } + + if !reflect.DeepEqual(meta, expectedMeta) { + t.Errorf("ContainerRegistry.List meta returned %+v, expected %+v", meta, expectedMeta) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + if _, _, _, err = client.ContainerRegistry.List(c, nil); err == nil { + t.Error("ContainerRegistry.List returned nil") + } +} + +func TestVCRServiceHandler_Get(t *testing.T) { + setup() + defer teardown() + + vcrID := "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0" + + mux.HandleFunc(fmt.Sprintf("%s/%s", vcrPath, vcrID), func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "id": "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + "name": "govultrtest", + "region": "sjc", + "urn": "sjc.vultrcr.com/govultrtest", + "storage": { + "used": { + "updated_at": "2023-11-09 13:37:12", + "bytes": 0, + "mb": 0, + "gb": 0, + "tb": 0 + }, + "allowed": { + "bytes": 21474836480, + "mb": 20480, + "gb": 20, + "tb": 0.02 + } + }, + "date_created": "2023-11-09 13:37:12", + "public": false, + "root_user": { + "id": 635, + "username": "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + "password": "5dEr33smkSYauMmRWusNFk9HpweL5CevpnFr", + "root": true, + "added_at": "2023-11-09 13:37:12", + "updated_at": "2023-11-09 13:37:12" + }, + "metadata": { + "region": { + "id": 3, + "name": "sjc", + "urn": "sjc.vultrcr.com", + "base_url": "https://sjc.vultrcr.com", + "public": true, + "added_at": "2023-09-14 09:09:16", + "updated_at": "2023-09-14 09:09:16", + "data_center": { + "id": 12, + "name": "Silicon Valley", + "site_code": "SJC2", + "region": "West", + "country": "US", + "continent": "North America", + "description": "Silicon Valley, California", + "airport": "SJC" + } + }, + "subscription": { + "billing": { + "monthly_price": 5, + "pending_charges": 0 + } + } + } +}` + fmt.Fprint(writer, response) + }) + + vcr, _, err := client.ContainerRegistry.Get(ctx, vcrID) + if err != nil { + t.Errorf("ContainerRegistry.Get returned %v", err) + } + + expected := &ContainerRegistry{ + ID: "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + Name: "govultrtest", + URN: "sjc.vultrcr.com/govultrtest", + Storage: ContainerRegistryStorage{ + Used: ContainerRegistryStorageCount{ + Bytes: 0, + MegaBytes: 0, + GigaBytes: 0, + TeraBytes: 0, + DateModified: "2023-11-09 13:37:12", + }, + Allowed: ContainerRegistryStorageCount{ + Bytes: 21474836480, + MegaBytes: 20480, + GigaBytes: 20, + TeraBytes: 0.02, + DateModified: "", + }, + }, + DateCreated: "2023-11-09 13:37:12", + Public: false, + RootUser: ContainerRegistryUser{ + ID: 635, + UserName: "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + Password: "5dEr33smkSYauMmRWusNFk9HpweL5CevpnFr", + Root: true, + DateCreated: "2023-11-09 13:37:12", + DateModified: "2023-11-09 13:37:12", + }, + Metadata: ContainerRegistryMetadata{ + Region: ContainerRegistryRegion{ + ID: 3, + Name: "sjc", + URN: "sjc.vultrcr.com", + BaseURL: "https://sjc.vultrcr.com", + Public: true, + DateCreated: "2023-09-14 09:09:16", + DateModified: "2023-09-14 09:09:16", + DataCenter: ContainerRegistryRegionDataCenter{ + ID: 12, + Name: "Silicon Valley", + SiteCode: "SJC2", + Region: "West", + Country: "US", + Continent: "North America", + Description: "Silicon Valley, California", + Airport: "SJC", + }, + }, + Subscription: ContainerRegistrySubscription{ + Billing: ContainerRegistrySubscriptionBilling{ + MonthlyPrice: 5, + PendingCharges: 0, + }, + }, + }, + } + + if !reflect.DeepEqual(vcr, expected) { + t.Errorf("ContainerRegistry.Get returned %+v, expected %+v", vcr, expected) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + _, _, err = client.ContainerRegistry.Get(c, vcrID) + if err == nil { + t.Error("ContainerRegistry.Get returned nil") + } +} + +func TestVCRServiceHandler_Update(t *testing.T) { + setup() + defer teardown() + + vcrID := "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0" + + mux.HandleFunc(fmt.Sprintf("%s/%s", vcrPath, vcrID), func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "id": "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + "name": "govultrtest", + "region": "sjc", + "urn": "sjc.vultrcr.com/govultrtest", + "storage": { + "used": { + "updated_at": "2023-11-09 13:37:12", + "bytes": 0, + "mb": 0, + "gb": 0, + "tb": 0 + }, + "allowed": { + "bytes": 21474836480, + "mb": 20480, + "gb": 20, + "tb": 0.02 + } + }, + "date_created": "2023-11-09 13:37:12", + "public": false, + "root_user": { + "id": 635, + "username": "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + "password": "5dEr33smkSYauMmRWusNFk9HpweL5CevpnFr", + "root": true, + "added_at": "2023-11-09 13:37:12", + "updated_at": "2023-11-09 13:37:12" + }, + "metadata": { + "region": { + "id": 3, + "name": "sjc", + "urn": "sjc.vultrcr.com", + "base_url": "https://sjc.vultrcr.com", + "public": true, + "added_at": "2023-09-14 09:09:16", + "updated_at": "2023-09-14 09:09:16", + "data_center": { + "id": 12, + "name": "Silicon Valley", + "site_code": "SJC2", + "region": "West", + "country": "US", + "continent": "North America", + "description": "Silicon Valley, California", + "airport": "SJC" + } + }, + "subscription": { + "billing": { + "monthly_price": 5, + "pending_charges": 0 + } + } + } +}` + fmt.Fprint(writer, response) + }) + + req := &ContainerRegistryUpdateReq{ + Public: BoolToBoolPtr(true), + } + + vcr, _, err := client.ContainerRegistry.Update(ctx, vcrID, req) + if err != nil { + t.Errorf("ContainerRegistry.Update returned %v", err) + } + + expected := &ContainerRegistry{ + ID: "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + Name: "govultrtest", + URN: "sjc.vultrcr.com/govultrtest", + Storage: ContainerRegistryStorage{ + Used: ContainerRegistryStorageCount{ + Bytes: 0, + MegaBytes: 0, + GigaBytes: 0, + TeraBytes: 0, + DateModified: "2023-11-09 13:37:12", + }, + Allowed: ContainerRegistryStorageCount{ + Bytes: 21474836480, + MegaBytes: 20480, + GigaBytes: 20, + TeraBytes: 0.02, + DateModified: "", + }, + }, + DateCreated: "2023-11-09 13:37:12", + Public: false, + RootUser: ContainerRegistryUser{ + ID: 635, + UserName: "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0", + Password: "5dEr33smkSYauMmRWusNFk9HpweL5CevpnFr", + Root: true, + DateCreated: "2023-11-09 13:37:12", + DateModified: "2023-11-09 13:37:12", + }, + Metadata: ContainerRegistryMetadata{ + Region: ContainerRegistryRegion{ + ID: 3, + Name: "sjc", + URN: "sjc.vultrcr.com", + BaseURL: "https://sjc.vultrcr.com", + Public: true, + DateCreated: "2023-09-14 09:09:16", + DateModified: "2023-09-14 09:09:16", + DataCenter: ContainerRegistryRegionDataCenter{ + ID: 12, + Name: "Silicon Valley", + SiteCode: "SJC2", + Region: "West", + Country: "US", + Continent: "North America", + Description: "Silicon Valley, California", + Airport: "SJC", + }, + }, + Subscription: ContainerRegistrySubscription{ + Billing: ContainerRegistrySubscriptionBilling{ + MonthlyPrice: 5, + PendingCharges: 0, + }, + }, + }, + } + + if !reflect.DeepEqual(vcr, expected) { + t.Errorf("ContainerRegistry.Update returned %+v \n\n expected %+v", vcr, expected) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + _, _, err = client.ContainerRegistry.Update(c, vcrID, req) + if err == nil { + t.Error("ContainerRegistry.Update returned nil") + } +} + +func TestVCRServiceHandler_Delete(t *testing.T) { + setup() + defer teardown() + + vcrID := "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0" + + mux.HandleFunc(fmt.Sprintf("%s/%s", vcrPath, vcrID), func(writer http.ResponseWriter, request *http.Request) { + fmt.Fprint(writer) + }) + + err := client.ContainerRegistry.Delete(ctx, vcrID) + if err != nil { + t.Errorf("ContainerRegistry.Delete returned %+v", err) + } +} + +func TestVCRServiceHandler_GetRepository(t *testing.T) { + setup() + defer teardown() + + vcrID := "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0" + vcrImage := "vultr-csi" + + mux.HandleFunc(fmt.Sprintf("%s/%s/repository/%s", vcrPath, vcrID, vcrImage), func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "name": "govultrtest/vultr-csi", + "image": "vultr-csi", + "description": "", + "added_at": "2023-10-05T18:22:17.041Z", + "updated_at": "2023-10-27T23:26:09.369Z", + "pull_count": 9, + "artifact_count": 7 +}` + fmt.Fprint(writer, response) + }) + + vcrRepo, _, err := client.ContainerRegistry.GetRepository(ctx, vcrID, vcrImage) + if err != nil { + t.Errorf("ContainerRegistry.GetRepository returned %+v", err) + } + + expected := &ContainerRegistryRepo{ + Name: "govultrtest/vultr-csi", + Image: "vultr-csi", + Description: "", + DateCreated: "2023-10-05T18:22:17.041Z", + DateModified: "2023-10-27T23:26:09.369Z", + PullCount: 9, + ArtifactCount: 7, + } + + if !reflect.DeepEqual(vcrRepo, expected) { + t.Errorf("ContainerRegistry.GetRepository returned %+v, expected %+v", vcrRepo, expected) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + if _, _, err = client.ContainerRegistry.GetRepository(c, vcrID, vcrImage); err == nil { + t.Error("ContainerRegistry.GetRepository returned nil") + } +} + +func TestVCRServiceHandler_ListRepositories(t *testing.T) { + setup() + defer teardown() + + vcrID := "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0" + + mux.HandleFunc(fmt.Sprintf("%s/%s/repositories", vcrPath, vcrID), func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "repositories": [ + { + "name": "govultrtest/vultr-csi", + "image": "vultr-csi", + "description": "", + "added_at": "2023-10-05T18:22:17.041Z", + "updated_at": "2023-10-27T23:26:09.369Z", + "pull_count": 9, + "artifact_count": 7 + } + ], + "meta": { + "total": 1, + "links": { + "next": "", + "prev": "" + } + } +}` + fmt.Fprint(writer, response) + }) + + vcrRepos, meta, _, err := client.ContainerRegistry.ListRepositories(ctx, vcrID, nil) + if err != nil { + t.Errorf("ContainerRegistry.ListRepositories returned %+v", err) + } + + expected := []ContainerRegistryRepo{ + { + Name: "govultrtest/vultr-csi", + Image: "vultr-csi", + Description: "", + DateCreated: "2023-10-05T18:22:17.041Z", + DateModified: "2023-10-27T23:26:09.369Z", + PullCount: 9, + ArtifactCount: 7, + }, + } + + expectedMeta := &Meta{ + Total: 1, + Links: &Links{ + Next: "", + Prev: "", + }, + } + + if !reflect.DeepEqual(vcrRepos, expected) { + t.Errorf("ContainerRegistry.ListRepositories returned %+v, expected %+v", vcrRepos, expected) + } + + if !reflect.DeepEqual(meta, expectedMeta) { + t.Errorf("ContainerRegistry.ListRepositories meta returned %+v, expected %+v", meta, expectedMeta) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + if _, _, _, err = client.ContainerRegistry.ListRepositories(c, vcrID, nil); err == nil { + t.Error("ContainerRegistry.ListRepositories returned nil") + } +} + +func TestVCRServiceHandler_UpdateRepository(t *testing.T) { + setup() + defer teardown() + + vcrID := "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0" + vcrImage := "vultr-csi" + + mux.HandleFunc(fmt.Sprintf("%s/%s/repository/%s", vcrPath, vcrID, vcrImage), func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "name": "govultrtest/vultr-csi", + "image": "vultr-csi", + "description": "test", + "added_at": "2023-10-05T18:22:17.041Z", + "updated_at": "2023-10-27T23:26:09.369Z", + "pull_count": 9, + "artifact_count": 7 +}` + fmt.Fprint(writer, response) + }) + + req := &ContainerRegistryRepoUpdateReq{ + Description: "test", + } + + vcrRepo, _, err := client.ContainerRegistry.UpdateRepository(ctx, vcrID, vcrImage, req) + if err != nil { + t.Errorf("ContainerRegistry.UpdateRepository returned %+v", err) + } + + expected := &ContainerRegistryRepo{ + Name: "govultrtest/vultr-csi", + Image: "vultr-csi", + Description: "test", + DateCreated: "2023-10-05T18:22:17.041Z", + DateModified: "2023-10-27T23:26:09.369Z", + PullCount: 9, + ArtifactCount: 7, + } + + if !reflect.DeepEqual(vcrRepo, expected) { + t.Errorf("ContainerRegistry.UpdateRepository returned %+v, expected %+v", vcrRepo, expected) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + if _, _, err = client.ContainerRegistry.UpdateRepository(c, vcrID, vcrImage, req); err == nil { + t.Error("ContainerRegistry.UpdateRepository returned nil") + } +} + +func TestVCRServiceHandler_DeleteRepository(t *testing.T) { + setup() + defer teardown() + + vcrID := "e1d6be16-2b0c-4d76-a3eb-f28bf6ea5fe0" + vcrImage := "vultr-csi" + + mux.HandleFunc(fmt.Sprintf("%s/%s/repository/%s", vcrPath, vcrID, vcrImage), func(writer http.ResponseWriter, request *http.Request) { + fmt.Fprint(writer) + }) + + err := client.ContainerRegistry.DeleteRepository(ctx, vcrID, vcrImage) + if err != nil { + t.Errorf("ContainerRegistry.DeleteRepository returned %+v", err) + } +} + +func TestVCRServiceHandler_ListRegions(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc(fmt.Sprintf("%s/region/list", vcrPath), func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "regions": [ + { + "id": 3, + "name": "sjc", + "urn": "sjc.vultrcr.com", + "base_url": "https://sjc.vultrcr.com", + "public": true, + "added_at": "2023-09-14 09:09:16", + "updated_at": "2023-09-14 09:09:16", + "data_center": { + "id": 12, + "name": "Silicon Valley", + "site_code": "SJC2", + "region": "West", + "country": "US", + "continent": "North America", + "description": "Silicon Valley, California", + "airport": "SJC" + } + } + ], + "meta": { + "total": 1, + "links": { + "next": "", + "prev": "" + } + } +}` + fmt.Fprint(writer, response) + }) + + vcrRegions, meta, _, err := client.ContainerRegistry.ListRegions(ctx, nil) + if err != nil { + t.Errorf("ContainerRegistry.ListRegions returned %v", err) + } + + expected := []ContainerRegistryRegion{ + { + ID: 3, + Name: "sjc", + URN: "sjc.vultrcr.com", + BaseURL: "https://sjc.vultrcr.com", + Public: true, + DateCreated: "2023-09-14 09:09:16", + DateModified: "2023-09-14 09:09:16", + DataCenter: ContainerRegistryRegionDataCenter{ + ID: 12, + Name: "Silicon Valley", + SiteCode: "SJC2", + Region: "West", + Country: "US", + Continent: "North America", + Description: "Silicon Valley, California", + Airport: "SJC", + }, + }, + } + + expectedMeta := &Meta{ + Total: 1, + Links: &Links{ + Next: "", + Prev: "", + }, + } + + if !reflect.DeepEqual(vcrRegions, expected) { + t.Errorf("ContainerRegistry.ListRegions returned %+v, expected %+v", vcrRegions, expected) + } + + if !reflect.DeepEqual(meta, expectedMeta) { + t.Errorf("ContainerRegistry.ListRegions meta returned %+v, expected %+v", meta, expectedMeta) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + _, _, _, err = client.ContainerRegistry.ListRegions(c, nil) + if err == nil { + t.Error("ContainerRegistry.ListRegions returned nil") + } +} + +func TestVCRServiceHandler_ListPlans(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc(fmt.Sprintf("%s/plan/list", vcrPath), func(writer http.ResponseWriter, request *http.Request) { + response := `{ + "plans": { + "start_up": { + "vanity_name": "Start Up", + "max_storage_mb": 10240, + "monthly_price": 0 + }, + "business": { + "vanity_name": "Business", + "max_storage_mb": 20480, + "monthly_price": 5 + }, + "premium": { + "vanity_name": "Premium", + "max_storage_mb": 51200, + "monthly_price": 10 + }, + "enterprise": { + "vanity_name": "Enterprise", + "max_storage_mb": 1048576, + "monthly_price": 20 + } + } +}` + fmt.Fprint(writer, response) + }) + + vcrPlans, _, err := client.ContainerRegistry.ListPlans(ctx) + if err != nil { + t.Errorf("ContainerRegistry.ListPlans returned %v", err) + } + + expected := &ContainerRegistryPlans{ + Plans: ContainerRegistryPlanTypes{ + StartUp: ContainerRegistryPlan{ + VanityName: "Start Up", + MaxStorageMB: 10240, + MonthlyPrice: 0, + }, + Business: ContainerRegistryPlan{ + VanityName: "Business", + MaxStorageMB: 20480, + MonthlyPrice: 5, + }, + Premium: ContainerRegistryPlan{ + VanityName: "Premium", + MaxStorageMB: 51200, + MonthlyPrice: 10, + }, + Enterprise: ContainerRegistryPlan{ + VanityName: "Enterprise", + MaxStorageMB: 1048576, + MonthlyPrice: 20, + }, + }, + } + + if !reflect.DeepEqual(vcrPlans, expected) { + t.Errorf("ContainerRegistry.ListPlans returned %+v, expected %+v", vcrPlans, expected) + } + + c, can := context.WithTimeout(ctx, 1*time.Microsecond) + defer can() + _, _, err = client.ContainerRegistry.ListPlans(c) + if err == nil { + t.Error("ContainerRegistry.ListPlans returned nil") + } +} diff --git a/govultr.go b/govultr.go index 9b2ac4e..97d4e0b 100644 --- a/govultr.go +++ b/govultr.go @@ -1,3 +1,5 @@ +// Package govultr contains the functionality to interact with the Vultr public +// HTTP REST API. package govultr import ( @@ -39,21 +41,22 @@ type Client struct { UserAgent string // Services used to interact with the API - Account AccountService - Application ApplicationService - Backup BackupService - BareMetalServer BareMetalServerService - Billing BillingService - BlockStorage BlockStorageService - Database DatabaseService - Domain DomainService - DomainRecord DomainRecordService - FirewallGroup FirewallGroupService - FirewallRule FireWallRuleService - Instance InstanceService - ISO ISOService - Kubernetes KubernetesService - LoadBalancer LoadBalancerService + Account AccountService + Application ApplicationService + Backup BackupService + BareMetalServer BareMetalServerService + Billing BillingService + BlockStorage BlockStorageService + ContainerRegistry ContainerRegistryService + Database DatabaseService + Domain DomainService + DomainRecord DomainRecordService + FirewallGroup FirewallGroupService + FirewallRule FireWallRuleService + Instance InstanceService + ISO ISOService + Kubernetes KubernetesService + LoadBalancer LoadBalancerService // Deprecated: Network should no longer be used. Instead, use VPC. Network NetworkService ObjectStorage ObjectStorageService @@ -116,6 +119,7 @@ func NewClient(httpClient *http.Client) *Client { client.BareMetalServer = &BareMetalServerServiceHandler{client} client.Billing = &BillingServiceHandler{client} client.BlockStorage = &BlockStorageServiceHandler{client} + client.ContainerRegistry = &ContainerRegistryServiceHandler{client} client.Database = &DatabaseServiceHandler{client} client.Domain = &DomainServiceHandler{client} client.DomainRecord = &DomainRecordsServiceHandler{client} @@ -178,14 +182,14 @@ func (c *Client) DoWithContext(ctx context.Context, r *http.Request, data interf rreq = rreq.WithContext(ctx) - res, err := c.client.Do(rreq) + res, errDo := c.client.Do(rreq) if c.onRequestCompleted != nil { c.onRequestCompleted(r, res) } - if err != nil { - return nil, err + if errDo != nil { + return nil, errDo } defer func() { diff --git a/kubernetes.go b/kubernetes.go index 91447ee..90ead5a 100644 --- a/kubernetes.go +++ b/kubernetes.go @@ -258,7 +258,7 @@ func (k *KubernetesHandler) CreateNodePool(ctx context.Context, vkeID string, no } // ListNodePools will return all nodepools for a given VKE cluster -func (k *KubernetesHandler) ListNodePools(ctx context.Context, vkeID string, options *ListOptions) ([]NodePool, *Meta, *http.Response, error) { //nolint:lll +func (k *KubernetesHandler) ListNodePools(ctx context.Context, vkeID string, options *ListOptions) ([]NodePool, *Meta, *http.Response, error) { //nolint:lll,dupl req, err := k.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/node-pools", vkePath, vkeID), nil) if err != nil { return nil, nil, nil, err diff --git a/kubernetes_test.go b/kubernetes_test.go index 22f9620..20c4690 100644 --- a/kubernetes_test.go +++ b/kubernetes_test.go @@ -308,7 +308,7 @@ func TestKubernetesHandler_ListClusters(t *testing.T) { } if !reflect.DeepEqual(meta, expectedMeta) { - t.Errorf("Kubernetes.List meta returned %+v, expected %+v", vke, expected) + t.Errorf("Kubernetes.List meta returned %+v, expected %+v", meta, expectedMeta) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) diff --git a/object_storage.go b/object_storage.go index 1597fcd..f3624da 100644 --- a/object_storage.go +++ b/object_storage.go @@ -21,7 +21,7 @@ type ObjectStorageService interface { RegenerateKeys(ctx context.Context, id string) (*S3Keys, *http.Response, error) } -// ObjectStorageServiceHandler handles interaction with the firewall rule methods for the Vultr API. +// ObjectStorageServiceHandler handles interaction between the object storage service and the Vultr API. type ObjectStorageServiceHandler struct { client *Client }