diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8534f71a7..00ed21d17 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,4 +4,8 @@ updates: directory: "/" schedule: interval: daily - open-pull-requests-limit: 10 \ No newline at end of file + open-pull-requests-limit: 10 + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/gosec_pr.yml b/.github/workflows/gosec_pr.yml index 71896c31f..64e63dff0 100644 --- a/.github/workflows/gosec_pr.yml +++ b/.github/workflows/gosec_pr.yml @@ -9,7 +9,7 @@ jobs: GO111MODULE: on steps: - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run Gosec Security Scanner on root directory uses: securego/gosec@master diff --git a/.github/workflows/integration_tests_pr.yml b/.github/workflows/integration_tests_pr.yml index c4c076b3d..b1c991ffb 100644 --- a/.github/workflows/integration_tests_pr.yml +++ b/.github/workflows/integration_tests_pr.yml @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/setup-go@v4 with: - go-version: '1.19' + go-version: 'stable' - run: go version - uses: actions-ecosystem/action-regex-match@v2 id: disallowed-char-check @@ -32,7 +32,7 @@ jobs: # Check out merge commit - name: Checkout PR - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ inputs.sha }} diff --git a/.github/workflows/label-sync.yml b/.github/workflows/label-sync.yml index a52d11482..bf8a624ac 100644 --- a/.github/workflows/label-sync.yml +++ b/.github/workflows/label-sync.yml @@ -9,7 +9,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: micnncim/action-label-syncer@3abd5ab72fda571e69fffd97bd4e0033dd5f495c # pin@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/nightly-smoke-tests.yml b/.github/workflows/nightly-smoke-tests.yml index adaa2b642..990af90c8 100644 --- a/.github/workflows/nightly-smoke-tests.yml +++ b/.github/workflows/nightly-smoke-tests.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: main diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 0dbf2f626..ab7e530b6 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -9,7 +9,7 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@569eb7ee3a85817ab916c8f8ff03a5bd96c9c83e # pin@v5.23.0 + - uses: release-drafter/release-drafter@65c5fb495d1e69aa8c08a3317bc44ff8aabe9772 # pin@v5.24.0 with: config-name: release-drafter.yml env: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0bdfc99f0..2ed589ca5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: 'stable' @@ -59,6 +59,7 @@ jobs: SKIP_LINT: 1 - name: Convert JSON Report to XML + if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: | filename=$(ls | grep -E '^[0-9]{12}_linodego_test_report\.json') @@ -76,6 +77,7 @@ jobs: GO111MODULE: on - name: Add additional information to XML report + if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: | filename=$(ls | grep -E '^[0-9]{12}_linodego_test_report\.xml$') python scripts/add_to_xml_test_report.py \ @@ -85,6 +87,7 @@ jobs: --xmlfile "${filename}" - name: Upload test results to bucket + if: github.ref == 'refs/heads/main' && github.event_name == 'push' env: LINODE_CLI_OBJ_ACCESS_KEY: ${{ secrets.LINODE_CLI_OBJ_ACCESS_KEY }} LINODE_CLI_OBJ_SECRET_KEY: ${{ secrets.LINODE_CLI_OBJ_SECRET_KEY }} diff --git a/.gitignore b/.gitignore index dcb4cc633..cec47a064 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,8 @@ # Common IDE paths .vscode/ +.idea/ vendor/**/ .env coverage.txt - diff --git a/account_invoices.go b/account_invoices.go index 26529aaa7..d068662fa 100644 --- a/account_invoices.go +++ b/account_invoices.go @@ -18,13 +18,15 @@ type Invoice struct { Date *time.Time `json:"-"` } -// InvoiceItem structs reflect an single billable activity associate with an Invoice +// InvoiceItem structs reflect a single billable activity associate with an Invoice type InvoiceItem struct { Label string `json:"label"` Type string `json:"type"` UnitPrice int `json:"unitprice"` Quantity int `json:"quantity"` Amount float32 `json:"amount"` + Tax float32 `json:"tax"` + Region *string `json:"region"` From *time.Time `json:"-"` To *time.Time `json:"-"` } @@ -103,7 +105,7 @@ func (i *InvoiceItem) UnmarshalJSON(b []byte) error { return nil } -// GetInvoice gets the a single Invoice matching the provided ID +// GetInvoice gets a single Invoice matching the provided ID func (c *Client) GetInvoice(ctx context.Context, invoiceID int) (*Invoice, error) { req := c.R(ctx).SetResult(&Invoice{}) e := fmt.Sprintf("account/invoices/%d", invoiceID) diff --git a/account_transfer.go b/account_transfer.go new file mode 100644 index 000000000..115573021 --- /dev/null +++ b/account_transfer.go @@ -0,0 +1,33 @@ +package linodego + +import "context" + +// AccountTransfer represents an Account's network utilization for the current month. +type AccountTransfer struct { + Billable int `json:"billable"` + Quota int `json:"quota"` + Used int `json:"used"` + + RegionTransfers []AccountTransferRegion `json:"region_transfers"` +} + +// AccountTransferRegion represents an Account's network utilization for the current month +// in a given region. +type AccountTransferRegion struct { + ID string `json:"id"` + Billable int `json:"billable"` + Quota int `json:"quota"` + Used int `json:"used"` +} + +// GetAccountTransfer gets current Account's network utilization for the current month. +func (c *Client) GetAccountTransfer(ctx context.Context) (*AccountTransfer, error) { + req := c.R(ctx).SetResult(&AccountTransfer{}) + e := "account/transfer" + r, err := coupleAPIErrors(req.Get(e)) + if err != nil { + return nil, err + } + + return r.Result().(*AccountTransfer), nil +} diff --git a/client.go b/client.go index 5262c8485..a2d676f5e 100644 --- a/client.go +++ b/client.go @@ -3,7 +3,6 @@ package linodego import ( "context" "fmt" - "io/ioutil" "log" "net/http" "net/url" @@ -82,7 +81,10 @@ type clientCacheEntry struct { ExpiryOverride *time.Duration } -type Request = resty.Request +type ( + Request = resty.Request + Logger = resty.Logger +) func init() { // Wether or not we will enable Resty debugging output @@ -121,6 +123,14 @@ func (c *Client) SetDebug(debug bool) *Client { return c } +// SetLogger allows the user to override the output +// logger for debug logs. +func (c *Client) SetLogger(logger Logger) *Client { + c.resty.SetLogger(logger) + + return c +} + // OnBeforeRequest adds a handler to the request body to run before the request is sent func (c *Client) OnBeforeRequest(m func(request *Request) error) { c.resty.OnBeforeRequest(func(client *resty.Client, req *resty.Request) error { @@ -166,7 +176,7 @@ func (c *Client) updateHostURL() { apiProto = c.apiProto } - c.resty.SetHostURL( + c.resty.SetBaseURL( fmt.Sprintf( "%s://%s/%s", apiProto, @@ -183,7 +193,7 @@ func (c *Client) SetRootCertificate(path string) *Client { } // SetToken sets the API token for all requests from this client -// Only necessary if you haven't already provided an http client to NewClient() configured with the token. +// Only necessary if you haven't already provided the http client to NewClient() configured with the token. func (c *Client) SetToken(token string) *Client { c.resty.SetHeader("Authorization", fmt.Sprintf("Bearer %s", token)) return c @@ -398,7 +408,7 @@ func NewClient(hc *http.Client) (client Client) { certPath, certPathExists := os.LookupEnv(APIHostCert) if certPathExists { - cert, err := ioutil.ReadFile(filepath.Clean(certPath)) + cert, err := os.ReadFile(filepath.Clean(certPath)) if err != nil { log.Fatalf("[ERROR] Error when reading cert at %s: %s\n", certPath, err.Error()) } diff --git a/client_test.go b/client_test.go index f969d9178..3e0c35906 100644 --- a/client_test.go +++ b/client_test.go @@ -24,39 +24,39 @@ func TestClient_SetAPIVersion(t *testing.T) { client := NewClient(nil) - if client.resty.HostURL != defaultURL { - t.Fatal(cmp.Diff(client.resty.HostURL, defaultURL)) + if client.resty.BaseURL != defaultURL { + t.Fatal(cmp.Diff(client.resty.BaseURL, defaultURL)) } client.SetBaseURL(baseURL) client.SetAPIVersion(apiVersion) - if client.resty.HostURL != expectedHost { - t.Fatal(cmp.Diff(client.resty.HostURL, expectedHost)) + if client.resty.BaseURL != expectedHost { + t.Fatal(cmp.Diff(client.resty.BaseURL, expectedHost)) } // Ensure setting twice does not cause conflicts client.SetBaseURL(updatedBaseURL) client.SetAPIVersion(updatedAPIVersion) - if client.resty.HostURL != updatedExpectedHost { - t.Fatal(cmp.Diff(client.resty.HostURL, updatedExpectedHost)) + if client.resty.BaseURL != updatedExpectedHost { + t.Fatal(cmp.Diff(client.resty.BaseURL, updatedExpectedHost)) } // Revert client.SetBaseURL(baseURL) client.SetAPIVersion(apiVersion) - if client.resty.HostURL != expectedHost { - t.Fatal(cmp.Diff(client.resty.HostURL, expectedHost)) + if client.resty.BaseURL != expectedHost { + t.Fatal(cmp.Diff(client.resty.BaseURL, expectedHost)) } // Custom protocol client.SetBaseURL(protocolBaseURL) client.SetAPIVersion(protocolAPIVersion) - if client.resty.HostURL != protocolExpectedHost { - t.Fatal(cmp.Diff(client.resty.HostURL, expectedHost)) + if client.resty.BaseURL != protocolExpectedHost { + t.Fatal(cmp.Diff(client.resty.BaseURL, expectedHost)) } } diff --git a/config.go b/config.go index 0d0b2f507..6bccf83f7 100644 --- a/config.go +++ b/config.go @@ -29,7 +29,7 @@ type LoadConfigOptions struct { SkipLoadProfile bool } -// LoadConfig loads a Linode config according to the options argument. +// LoadConfig loads a Linode config according to the option's argument. // If no options are specified, the following defaults will be used: // Path: ~/.config/linode // Profile: default diff --git a/config_test.go b/config_test.go index 2ec9d8d97..b4b3db418 100644 --- a/config_test.go +++ b/config_test.go @@ -2,7 +2,6 @@ package linodego import ( "fmt" - "io/ioutil" "os" "testing" ) @@ -43,8 +42,8 @@ func TestConfig_LoadWithDefaults(t *testing.T) { expectedURL := "https://api.cool.linode.com/v4beta" - if client.resty.HostURL != expectedURL { - t.Fatalf("mismatched host url: %s != %s", client.resty.HostURL, expectedURL) + if client.resty.BaseURL != expectedURL { + t.Fatalf("mismatched host url: %s != %s", client.resty.BaseURL, expectedURL) } if client.resty.Header.Get("Authorization") != "Bearer "+p.APIToken { @@ -89,8 +88,8 @@ func TestConfig_OverrideDefaults(t *testing.T) { expectedURL := "https://api.cool.linode.com/v4" - if client.resty.HostURL != expectedURL { - t.Fatalf("mismatched host url: %s != %s", client.resty.HostURL, expectedURL) + if client.resty.BaseURL != expectedURL { + t.Fatalf("mismatched host url: %s != %s", client.resty.BaseURL, expectedURL) } if client.resty.Header.Get("Authorization") != "Bearer "+p.APIToken { @@ -131,7 +130,7 @@ func TestConfig_NoDefaults(t *testing.T) { } func createTestConfig(t *testing.T, conf string) *os.File { - file, err := ioutil.TempFile("", "linode") + file, err := os.CreateTemp("", "linode") if err != nil { t.Fatal(err) } diff --git a/errors.go b/errors.go index d7e7ae291..702959049 100644 --- a/errors.go +++ b/errors.go @@ -2,20 +2,21 @@ package linodego import ( "fmt" - "log" "net/http" + "reflect" "strings" "github.com/go-resty/resty/v2" ) const ( + ErrorUnsupported = iota // ErrorFromString is the Code identifying Errors created by string types - ErrorFromString = 1 + ErrorFromString // ErrorFromError is the Code identifying Errors created by error types - ErrorFromError = 2 + ErrorFromError // ErrorFromStringer is the Code identifying Errors created by fmt.Stringer types - ErrorFromStringer = 3 + ErrorFromStringer ) // Error wraps the LinodeGo error with the relevant http.Response @@ -113,7 +114,7 @@ func NewError(err any) *Error { apiError, ok := e.Error().(*APIError) if !ok { - log.Fatalln("Unexpected Resty Error Response") + return &Error{Code: ErrorUnsupported, Message: "Unexpected Resty Error Response, no error"} } return &Error{ @@ -128,7 +129,6 @@ func NewError(err any) *Error { case fmt.Stringer: return &Error{Code: ErrorFromStringer, Message: e.String()} default: - log.Fatalln("Unsupported type to linodego.NewError") - panic(err) + return &Error{Code: ErrorUnsupported, Message: fmt.Sprintf("Unsupported type to linodego.NewError: %s", reflect.TypeOf(e))} } } diff --git a/errors_test.go b/errors_test.go index ee379365d..15510ec72 100644 --- a/errors_test.go +++ b/errors_test.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "errors" - "io/ioutil" + "io" "net/http" "net/http/httptest" "testing" @@ -13,6 +13,58 @@ import ( "github.com/google/go-cmp/cmp" ) +type tstringer string + +func (t tstringer) String() string { + return string(t) +} + +func TestNewError(t *testing.T) { + if NewError(nil) != nil { + t.Errorf("nil error should return nil") + } + if NewError(struct{}{}).Code != ErrorUnsupported { + t.Error("empty struct should return unsupported error type") + } + err := errors.New("test") + newErr := NewError(&err) + if newErr.Message == err.Error() && newErr.Code == ErrorFromError { + t.Error("nil error should return nil") + } + + if err := NewError(&resty.Response{Request: &resty.Request{}}); err.Message != "Unexpected Resty Error Response, no error" { + t.Error("Unexpected Resty Error Response, no error") + } + + rerr := &resty.Response{ + RawResponse: &http.Response{ + StatusCode: 500, + }, + Request: &resty.Request{ + Error: &APIError{ + []APIErrorReason{ + { + Reason: "testreason", + Field: "testfield", + }, + }, + }, + }, + } + + if err := NewError(rerr); err.Message != "[testfield] testreason" { + t.Error("rest response error should should be set") + } + + if err := NewError("stringerror"); err.Message != "stringerror" || err.Code != ErrorFromString { + t.Errorf("string error should be set") + } + + if err := NewError(tstringer("teststringer")); err.Message != "teststringer" || err.Code != ErrorFromStringer { + t.Errorf("stringer error should be set") + } +} + func createTestServer(method, route, contentType, body string, statusCode int) (*httptest.Server, *Client) { h := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { if r.Method == method && r.URL.Path == route { @@ -62,7 +114,7 @@ func TestCoupleAPIErrors_badGatewayError(t *testing.T) {
nginx
`) - buf := ioutil.NopCloser(bytes.NewBuffer(rawResponse)) + buf := io.NopCloser(bytes.NewBuffer(rawResponse)) resp := &resty.Response{ Request: &resty.Request{ diff --git a/go.mod b/go.mod index 67a6ca30b..b28518192 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,12 @@ module github.com/linode/linodego require ( github.com/go-resty/resty/v2 v2.7.0 github.com/google/go-cmp v0.5.7 - golang.org/x/net v0.15.0 + golang.org/x/net v0.16.0 + golang.org/x/text v0.13.0 gopkg.in/ini.v1 v1.66.6 ) -require ( - github.com/stretchr/testify v1.8.4 // indirect - golang.org/x/text v0.13.0 // indirect -) +require github.com/stretchr/testify v1.8.4 // indirect go 1.20 diff --git a/go.sum b/go.sum index cac833ff5..30c64c007 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/go.work.sum b/go.work.sum index b347b1ca0..be4939f3c 100644 --- a/go.work.sum +++ b/go.work.sum @@ -167,6 +167,7 @@ golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= @@ -195,6 +196,8 @@ golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= diff --git a/images.go b/images.go index d9e29f49e..7875d6fdc 100644 --- a/images.go +++ b/images.go @@ -148,7 +148,7 @@ func (c *Client) GetImage(ctx context.Context, imageID string) (*Image, error) { return r.Result().(*Image), nil } -// CreateImage creates a Image +// CreateImage creates an Image func (c *Client) CreateImage(ctx context.Context, opts ImageCreateOptions) (*Image, error) { body, err := json.Marshal(opts) if err != nil { diff --git a/k8s/go.mod b/k8s/go.mod index 081d363ea..b0f89e6b3 100644 --- a/k8s/go.mod +++ b/k8s/go.mod @@ -29,10 +29,10 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.15.0 // indirect + golang.org/x/net v0.16.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/term v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/k8s/go.sum b/k8s/go.sum index f971790f0..1b67d338b 100644 --- a/k8s/go.sum +++ b/k8s/go.sum @@ -84,8 +84,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -96,11 +96,11 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/mysql.go b/mysql.go index 52161bb65..575d0b27b 100644 --- a/mysql.go +++ b/mysql.go @@ -19,7 +19,7 @@ const ( MySQLDatabaseTargetSecondary MySQLDatabaseTarget = "secondary" ) -// A MySQLDatabase is a instance of Linode MySQL Managed Databases +// A MySQLDatabase is an instance of Linode MySQL Managed Databases type MySQLDatabase struct { ID int `json:"id"` Status DatabaseStatus `json:"status"` diff --git a/postgres.go b/postgres.go index 2484ecdf9..35158b6e5 100644 --- a/postgres.go +++ b/postgres.go @@ -35,7 +35,7 @@ const ( PostgresReplicationSemiSynch PostgresReplicationType = "semi_synch" ) -// A PostgresDatabase is a instance of Linode Postgres Managed Databases +// A PostgresDatabase is an instance of Linode Postgres Managed Databases type PostgresDatabase struct { ID int `json:"id"` Status DatabaseStatus `json:"status"` diff --git a/test/go.mod b/test/go.mod index 91cf3878f..974dec2b0 100644 --- a/test/go.mod +++ b/test/go.mod @@ -6,7 +6,7 @@ require ( github.com/jarcoal/httpmock v1.2.0 github.com/linode/linodego v0.20.1 github.com/linode/linodego/k8s v0.0.0-00010101000000-000000000000 - golang.org/x/net v0.15.0 + golang.org/x/net v0.16.0 golang.org/x/oauth2 v0.8.0 k8s.io/client-go v0.28.1 ) @@ -32,8 +32,8 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/term v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/test/go.sum b/test/go.sum index 6f691527e..6831a7a45 100644 --- a/test/go.sum +++ b/test/go.sum @@ -90,8 +90,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -102,11 +102,11 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/test/integration/account_transfer_test.go b/test/integration/account_transfer_test.go new file mode 100644 index 000000000..1cac97b72 --- /dev/null +++ b/test/integration/account_transfer_test.go @@ -0,0 +1,41 @@ +package integration + +import ( + "context" + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/jarcoal/httpmock" + "github.com/linode/linodego" +) + +func TestAccount_getTransfer(t *testing.T) { + client := createMockClient(t) + + desiredResponse := linodego.AccountTransfer{ + Billable: 123, + Quota: 456, + Used: 789, + RegionTransfers: []linodego.AccountTransferRegion{ + { + ID: "us-southeast", + Billable: 987, + Quota: 654, + Used: 3211, + }, + }, + } + + httpmock.RegisterRegexpResponder("GET", mockRequestURL(t, "/account/transfer"), + httpmock.NewJsonResponderOrPanic(200, &desiredResponse)) + + questions, err := client.GetAccountTransfer(context.Background()) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(*questions, desiredResponse) { + t.Fatalf("actual response does not equal desired response: %s", cmp.Diff(questions, desiredResponse)) + } +} diff --git a/test/integration/fixtures/TestTypes_RegionSpecific.yaml b/test/integration/fixtures/TestTypes_RegionSpecific.yaml new file mode 100644 index 000000000..3482e58e5 --- /dev/null +++ b/test/integration/fixtures/TestTypes_RegionSpecific.yaml @@ -0,0 +1,269 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/linode/types + method: GET + response: + body: '{"data": [{"id": "g6-nanode-1", "label": "Nanode 1GB", "price": {"hourly": + 0.0075, "monthly": 5.0}, "region_prices": [{"id": "us-east", "hourly": 0.009, + "monthly": 6.0}], "addons": {"backups": {"price": {"hourly": 0.003, "monthly": + 2.0}, "region_prices": [{"id": "us-east", "hourly": 0.0036, "monthly": 2.4}]}}, + "memory": 1024, "disk": 25600, "transfer": 1000, "vcpus": 1, "gpus": 0, "network_out": + 1000, "class": "nanode", "successor": null}, {"id": "g6-standard-1", "label": + "Linode 2GB", "price": {"hourly": 0.015, "monthly": 10.0}, "region_prices": + [{"id": "us-east", "hourly": 0.018, "monthly": 12.0}], "addons": {"backups": + {"price": {"hourly": 0.004, "monthly": 2.5}, "region_prices": [{"id": "us-east", + "hourly": 0.0048, "monthly": 3.0}]}}, "memory": 2048, "disk": 51200, "transfer": + 2000, "vcpus": 1, "gpus": 0, "network_out": 2000, "class": "standard", "successor": + null}, {"id": "g6-standard-2", "label": "Linode 4GB", "price": {"hourly": 0.03, + "monthly": 20.0}, "region_prices": [{"id": "us-east", "hourly": 0.036, "monthly": + 24.0}], "addons": {"backups": {"price": {"hourly": 0.008, "monthly": 5.0}, "region_prices": + [{"id": "us-east", "hourly": 0.0096, "monthly": 6.0}]}}, "memory": 4096, "disk": + 81920, "transfer": 4000, "vcpus": 2, "gpus": 0, "network_out": 4000, "class": + "standard", "successor": null}, {"id": "g6-standard-4", "label": "Linode 8GB", + "price": {"hourly": 0.06, "monthly": 40.0}, "region_prices": [{"id": "us-east", + "hourly": 0.072, "monthly": 48.0}], "addons": {"backups": {"price": {"hourly": + 0.015, "monthly": 10.0}, "region_prices": [{"id": "us-east", "hourly": 0.018, + "monthly": 12.0}]}}, "memory": 8192, "disk": 163840, "transfer": 5000, "vcpus": + 4, "gpus": 0, "network_out": 5000, "class": "standard", "successor": null}, + {"id": "g6-standard-6", "label": "Linode 16GB", "price": {"hourly": 0.12, "monthly": + 80.0}, "region_prices": [{"id": "us-east", "hourly": 0.144, "monthly": 96.0}], + "addons": {"backups": {"price": {"hourly": 0.03, "monthly": 20.0}, "region_prices": + [{"id": "us-east", "hourly": 0.036, "monthly": 24.0}]}}, "memory": 16384, "disk": + 327680, "transfer": 8000, "vcpus": 6, "gpus": 0, "network_out": 6000, "class": + "standard", "successor": null}, {"id": "g6-standard-8", "label": "Linode 32GB", + "price": {"hourly": 0.24, "monthly": 160.0}, "region_prices": [{"id": "us-east", + "hourly": 0.288, "monthly": 192.0}], "addons": {"backups": {"price": {"hourly": + 0.06, "monthly": 40.0}, "region_prices": [{"id": "us-east", "hourly": 0.072, + "monthly": 48.0}]}}, "memory": 32768, "disk": 655360, "transfer": 16000, "vcpus": + 8, "gpus": 0, "network_out": 7000, "class": "standard", "successor": null}, + {"id": "g6-standard-16", "label": "Linode 64GB", "price": {"hourly": 0.48, "monthly": + 320.0}, "region_prices": [{"id": "us-east", "hourly": 0.576, "monthly": 384.0}], + "addons": {"backups": {"price": {"hourly": 0.12, "monthly": 80.0}, "region_prices": + [{"id": "us-east", "hourly": 0.144, "monthly": 96.0}]}}, "memory": 65536, "disk": + 1310720, "transfer": 20000, "vcpus": 16, "gpus": 0, "network_out": 9000, "class": + "standard", "successor": null}, {"id": "g6-standard-20", "label": "Linode 96GB", + "price": {"hourly": 0.72, "monthly": 480.0}, "region_prices": [{"id": "us-east", + "hourly": 0.864, "monthly": 576.0}], "addons": {"backups": {"price": {"hourly": + 0.18, "monthly": 120.0}, "region_prices": [{"id": "us-east", "hourly": 0.216, + "monthly": 144.0}]}}, "memory": 98304, "disk": 1966080, "transfer": 20000, "vcpus": + 20, "gpus": 0, "network_out": 10000, "class": "standard", "successor": null}, + {"id": "g6-standard-24", "label": "Linode 128GB", "price": {"hourly": 0.96, + "monthly": 640.0}, "region_prices": [{"id": "us-east", "hourly": 1.152, "monthly": + 768.0}], "addons": {"backups": {"price": {"hourly": 0.24, "monthly": 160.0}, + "region_prices": [{"id": "us-east", "hourly": 0.288, "monthly": 192.0}]}}, "memory": + 131072, "disk": 2621440, "transfer": 20000, "vcpus": 24, "gpus": 0, "network_out": + 11000, "class": "standard", "successor": null}, {"id": "g6-standard-32", "label": + "Linode 192GB", "price": {"hourly": 1.44, "monthly": 960.0}, "region_prices": + [{"id": "us-east", "hourly": 1.728, "monthly": 1152.0}], "addons": {"backups": + {"price": {"hourly": 0.36, "monthly": 240.0}, "region_prices": [{"id": "us-east", + "hourly": 0.432, "monthly": 288.0}]}}, "memory": 196608, "disk": 3932160, "transfer": + 20000, "vcpus": 32, "gpus": 0, "network_out": 12000, "class": "standard", "successor": + null}, {"id": "g7-highmem-1", "label": "Linode 24GB", "price": {"hourly": 0.09, + "monthly": 60.0}, "region_prices": [{"id": "us-east", "hourly": 0.108, "monthly": + 72.0}], "addons": {"backups": {"price": {"hourly": 0.0075, "monthly": 5.0}, + "region_prices": [{"id": "us-east", "hourly": 0.009, "monthly": 6.0}]}}, "memory": + 24576, "disk": 20480, "transfer": 5000, "vcpus": 2, "gpus": 0, "network_out": + 5000, "class": "highmem", "successor": null}, {"id": "g7-highmem-2", "label": + "Linode 48GB", "price": {"hourly": 0.18, "monthly": 120.0}, "region_prices": + [{"id": "us-east", "hourly": 0.216, "monthly": 144.0}], "addons": {"backups": + {"price": {"hourly": 0.015, "monthly": 10.0}, "region_prices": [{"id": "us-east", + "hourly": 0.018, "monthly": 12.0}]}}, "memory": 49152, "disk": 40960, "transfer": + 6000, "vcpus": 2, "gpus": 0, "network_out": 6000, "class": "highmem", "successor": + null}, {"id": "g7-highmem-4", "label": "Linode 90GB", "price": {"hourly": 0.36, + "monthly": 240.0}, "region_prices": [{"id": "us-east", "hourly": 0.432, "monthly": + 288.0}], "addons": {"backups": {"price": {"hourly": 0.03, "monthly": 20.0}, + "region_prices": [{"id": "us-east", "hourly": 0.036, "monthly": 24.0}]}}, "memory": + 92160, "disk": 92160, "transfer": 7000, "vcpus": 4, "gpus": 0, "network_out": + 7000, "class": "highmem", "successor": null}, {"id": "g7-highmem-8", "label": + "Linode 150GB", "price": {"hourly": 0.72, "monthly": 480.0}, "region_prices": + [{"id": "us-east", "hourly": 0.864, "monthly": 576.0}], "addons": {"backups": + {"price": {"hourly": 0.06, "monthly": 40.0}, "region_prices": [{"id": "us-east", + "hourly": 0.072, "monthly": 48.0}]}}, "memory": 153600, "disk": 204800, "transfer": + 8000, "vcpus": 8, "gpus": 0, "network_out": 8000, "class": "highmem", "successor": + null}, {"id": "g7-highmem-16", "label": "Linode 300GB", "price": {"hourly": + 1.44, "monthly": 960.0}, "region_prices": [{"id": "us-east", "hourly": 1.728, + "monthly": 1152.0}], "addons": {"backups": {"price": {"hourly": 0.12, "monthly": + 80.0}, "region_prices": [{"id": "us-east", "hourly": 0.144, "monthly": 96.0}]}}, + "memory": 307200, "disk": 348160, "transfer": 9000, "vcpus": 16, "gpus": 0, + "network_out": 9000, "class": "highmem", "successor": null}, {"id": "g6-dedicated-2", + "label": "Dedicated 4GB", "price": {"hourly": 0.045, "monthly": 30.0}, "region_prices": + [{"id": "us-east", "hourly": 0.054, "monthly": 36.0}], "addons": {"backups": + {"price": {"hourly": 0.008, "monthly": 5.0}, "region_prices": [{"id": "us-east", + "hourly": 0.0096, "monthly": 6.0}]}}, "memory": 4096, "disk": 81920, "transfer": + 4000, "vcpus": 2, "gpus": 0, "network_out": 4000, "class": "dedicated", "successor": + null}, {"id": "g6-dedicated-4", "label": "Dedicated 8GB", "price": {"hourly": + 0.09, "monthly": 60.0}, "region_prices": [{"id": "us-east", "hourly": 0.108, + "monthly": 72.0}], "addons": {"backups": {"price": {"hourly": 0.015, "monthly": + 10.0}, "region_prices": [{"id": "us-east", "hourly": 0.018, "monthly": 12.0}]}}, + "memory": 8192, "disk": 163840, "transfer": 5000, "vcpus": 4, "gpus": 0, "network_out": + 5000, "class": "dedicated", "successor": null}, {"id": "g6-dedicated-8", "label": + "Dedicated 16GB", "price": {"hourly": 0.18, "monthly": 120.0}, "region_prices": + [{"id": "us-east", "hourly": 0.216, "monthly": 144.0}], "addons": {"backups": + {"price": {"hourly": 0.03, "monthly": 20.0}, "region_prices": [{"id": "us-east", + "hourly": 0.036, "monthly": 24.0}]}}, "memory": 16384, "disk": 327680, "transfer": + 8000, "vcpus": 8, "gpus": 0, "network_out": 6000, "class": "dedicated", "successor": + null}, {"id": "g6-dedicated-16", "label": "Dedicated 32GB", "price": {"hourly": + 0.36, "monthly": 240.0}, "region_prices": [{"id": "us-east", "hourly": 0.432, + "monthly": 288.0}], "addons": {"backups": {"price": {"hourly": 0.06, "monthly": + 40.0}, "region_prices": [{"id": "us-east", "hourly": 0.072, "monthly": 48.0}]}}, + "memory": 32768, "disk": 655360, "transfer": 16000, "vcpus": 16, "gpus": 0, + "network_out": 7000, "class": "dedicated", "successor": null}, {"id": "g6-dedicated-32", + "label": "Dedicated 64GB", "price": {"hourly": 0.72, "monthly": 480.0}, "region_prices": + [{"id": "us-east", "hourly": 0.864, "monthly": 576.0}], "addons": {"backups": + {"price": {"hourly": 0.12, "monthly": 80.0}, "region_prices": [{"id": "us-east", + "hourly": 0.144, "monthly": 96.0}]}}, "memory": 65536, "disk": 1310720, "transfer": + 20000, "vcpus": 32, "gpus": 0, "network_out": 8000, "class": "dedicated", "successor": + null}, {"id": "g6-dedicated-48", "label": "Dedicated 96GB", "price": {"hourly": + 1.08, "monthly": 720.0}, "region_prices": [{"id": "us-east", "hourly": 1.296, + "monthly": 864.0}], "addons": {"backups": {"price": {"hourly": 0.18, "monthly": + 120.0}, "region_prices": [{"id": "us-east", "hourly": 0.216, "monthly": 144.0}]}}, + "memory": 98304, "disk": 1966080, "transfer": 20000, "vcpus": 48, "gpus": 0, + "network_out": 9000, "class": "dedicated", "successor": null}, {"id": "g6-sawhorse-64", + "label": "Sawhorse 192GB", "price": {"hourly": 4.5, "monthly": 3000.0}, "region_prices": + [{"id": "us-east", "hourly": 5.4, "monthly": 3600.0}], "addons": {"backups": + {"price": {"hourly": 0.0, "monthly": 0.0}, "region_prices": [{"id": "us-east", + "hourly": 0.0, "monthly": 0.0}]}}, "memory": 196608, "disk": 8388608, "transfer": + 9000, "vcpus": 64, "gpus": 0, "network_out": 10000, "class": "sawhorse", "successor": + null}, {"id": "g6-dedicated-50", "label": "Dedicated 128GB", "price": {"hourly": + 1.44, "monthly": 960.0}, "region_prices": [{"id": "us-east", "hourly": 1.728, + "monthly": 1152.0}], "addons": {"backups": {"price": {"hourly": 0.24, "monthly": + 160.0}, "region_prices": [{"id": "us-east", "hourly": 0.288, "monthly": 192.0}]}}, + "memory": 131072, "disk": 2560000, "transfer": 10000, "vcpus": 50, "gpus": 0, + "network_out": 10000, "class": "dedicated", "successor": null}, {"id": "g6-dedicated-56", + "label": "Dedicated 256GB", "price": {"hourly": 2.88, "monthly": 1920.0}, "region_prices": + [{"id": "us-east", "hourly": 3.456, "monthly": 2304.0}], "addons": {"backups": + {"price": {"hourly": 0.3, "monthly": 200.0}, "region_prices": [{"id": "us-east", + "hourly": 0.36, "monthly": 240.0}]}}, "memory": 262144, "disk": 5120000, "transfer": + 11000, "vcpus": 56, "gpus": 0, "network_out": 11000, "class": "dedicated", "successor": + null}, {"id": "g6-dedicated-64", "label": "Dedicated 512GB", "price": {"hourly": + 5.76, "monthly": 3840.0}, "region_prices": [{"id": "us-east", "hourly": 6.912, + "monthly": 4608.0}], "addons": {"backups": {"price": {"hourly": 0.36, "monthly": + 240.0}, "region_prices": [{"id": "us-east", "hourly": 0.432, "monthly": 288.0}]}}, + "memory": 524288, "disk": 7372800, "transfer": 12000, "vcpus": 64, "gpus": 0, + "network_out": 12000, "class": "dedicated", "successor": null}, {"id": "g1-gpu-rtx6000-1", + "label": "Dedicated 32GB + RTX6000 GPU x1", "price": {"hourly": 1.5, "monthly": + 1000.0}, "region_prices": [{"id": "us-east", "hourly": 1.8, "monthly": 1200.0}], + "addons": {"backups": {"price": {"hourly": 0.06, "monthly": 40.0}, "region_prices": + [{"id": "us-east", "hourly": 0.072, "monthly": 48.0}]}}, "memory": 32768, "disk": + 655360, "transfer": 16000, "vcpus": 8, "gpus": 0, "network_out": 10000, "class": + "gpu", "successor": null}, {"id": "g1-gpu-rtx6000-2", "label": "Dedicated 64GB + + RTX6000 GPU x2", "price": {"hourly": 3.0, "monthly": 2000.0}, "region_prices": + [{"id": "us-east", "hourly": 3.6, "monthly": 2400.0}], "addons": {"backups": + {"price": {"hourly": 0.12, "monthly": 80.0}, "region_prices": [{"id": "us-east", + "hourly": 0.144, "monthly": 96.0}]}}, "memory": 65536, "disk": 1310720, "transfer": + 20000, "vcpus": 16, "gpus": 0, "network_out": 10000, "class": "gpu", "successor": + null}, {"id": "g1-gpu-rtx6000-3", "label": "Dedicated 96GB + RTX6000 GPU x3", + "price": {"hourly": 4.5, "monthly": 3000.0}, "region_prices": [{"id": "us-east", + "hourly": 5.4, "monthly": 3600.0}], "addons": {"backups": {"price": {"hourly": + 0.18, "monthly": 160.0}, "region_prices": [{"id": "us-east", "hourly": 0.216, + "monthly": 192.0}]}}, "memory": 98304, "disk": 1966080, "transfer": 20000, "vcpus": + 20, "gpus": 0, "network_out": 10000, "class": "gpu", "successor": null}, {"id": + "g1-gpu-rtx6000-4", "label": "Dedicated 128GB + RTX6000 GPU x4", "price": {"hourly": + 6.0, "monthly": 4000.0}, "region_prices": [{"id": "us-east", "hourly": 7.2, + "monthly": 4800.0}], "addons": {"backups": {"price": {"hourly": 0.24, "monthly": + 320.0}, "region_prices": [{"id": "us-east", "hourly": 0.288, "monthly": 384.0}]}}, + "memory": 131072, "disk": 2621440, "transfer": 20000, "vcpus": 24, "gpus": 0, + "network_out": 10000, "class": "gpu", "successor": null}, {"id": "g6-premium-2", + "label": "Premium 4GB", "price": {"hourly": 0.05, "monthly": 36.0}, "region_prices": + [{"id": "us-east", "hourly": 0.06, "monthly": 43.2}], "addons": {"backups": + {"price": {"hourly": 0.008, "monthly": 5.0}, "region_prices": [{"id": "us-east", + "hourly": 0.0096, "monthly": 6.0}]}}, "memory": 4096, "disk": 81920, "transfer": + 4000, "vcpus": 2, "gpus": 0, "network_out": 4000, "class": "premium", "successor": + null}, {"id": "g6-premium-4", "label": "Premium 8GB", "price": {"hourly": 0.11, + "monthly": 72.0}, "region_prices": [{"id": "us-east", "hourly": 0.132, "monthly": + 86.4}], "addons": {"backups": {"price": {"hourly": 0.015, "monthly": 10.0}, + "region_prices": [{"id": "us-east", "hourly": 0.018, "monthly": 12.0}]}}, "memory": + 8192, "disk": 163840, "transfer": 5000, "vcpus": 4, "gpus": 0, "network_out": + 5000, "class": "premium", "successor": null}, {"id": "g6-premium-8", "label": + "Premium 16GB", "price": {"hourly": 0.22, "monthly": 144.0}, "region_prices": + [{"id": "us-east", "hourly": 0.264, "monthly": 172.8}], "addons": {"backups": + {"price": {"hourly": 0.03, "monthly": 20.0}, "region_prices": [{"id": "us-east", + "hourly": 0.036, "monthly": 24.0}]}}, "memory": 16384, "disk": 327680, "transfer": + 6000, "vcpus": 8, "gpus": 0, "network_out": 6000, "class": "premium", "successor": + null}, {"id": "g6-premium-16", "label": "Premium 32GB", "price": {"hourly": + 0.43, "monthly": 288.0}, "region_prices": [{"id": "us-east", "hourly": 0.516, + "monthly": 345.6}], "addons": {"backups": {"price": {"hourly": 0.06, "monthly": + 40.0}, "region_prices": [{"id": "us-east", "hourly": 0.072, "monthly": 48.0}]}}, + "memory": 32768, "disk": 655360, "transfer": 7000, "vcpus": 16, "gpus": 0, "network_out": + 7000, "class": "premium", "successor": null}, {"id": "g6-premium-32", "label": + "Premium 64GB", "price": {"hourly": 0.86, "monthly": 576.0}, "region_prices": + [{"id": "us-east", "hourly": 1.032, "monthly": 691.2}], "addons": {"backups": + {"price": {"hourly": 0.12, "monthly": 80.0}, "region_prices": [{"id": "us-east", + "hourly": 0.144, "monthly": 96.0}]}}, "memory": 65536, "disk": 1310720, "transfer": + 8000, "vcpus": 32, "gpus": 0, "network_out": 8000, "class": "premium", "successor": + null}, {"id": "g6-premium-48", "label": "Premium 96GB", "price": {"hourly": + 1.3, "monthly": 864.0}, "region_prices": [{"id": "us-east", "hourly": 1.56, + "monthly": 1036.8}], "addons": {"backups": {"price": {"hourly": 0.18, "monthly": + 120.0}, "region_prices": [{"id": "us-east", "hourly": 0.216, "monthly": 144.0}]}}, + "memory": 98304, "disk": 1966080, "transfer": 9000, "vcpus": 48, "gpus": 0, + "network_out": 9000, "class": "premium", "successor": null}, {"id": "g6-premium-50", + "label": "Premium 128GB", "price": {"hourly": 1.73, "monthly": 1152.0}, "region_prices": + [{"id": "us-east", "hourly": 2.076, "monthly": 1382.4}], "addons": {"backups": + {"price": {"hourly": 0.24, "monthly": 160.0}, "region_prices": [{"id": "us-east", + "hourly": 0.288, "monthly": 192.0}]}}, "memory": 131072, "disk": 2560000, "transfer": + 10000, "vcpus": 50, "gpus": 0, "network_out": 10000, "class": "premium", "successor": + null}, {"id": "g6-premium-56", "label": "Premium 256GB", "price": {"hourly": + 3.46, "monthly": 2304.0}, "region_prices": [{"id": "us-east", "hourly": 4.152, + "monthly": 2764.8}], "addons": {"backups": {"price": {"hourly": 0.3, "monthly": + 200.0}, "region_prices": [{"id": "us-east", "hourly": 0.36, "monthly": 240.0}]}}, + "memory": 262144, "disk": 5120000, "transfer": 11000, "vcpus": 56, "gpus": 0, + "network_out": 11000, "class": "premium", "successor": null}, {"id": "g6-premium-64", + "label": "Premium 512GB", "price": {"hourly": 6.91, "monthly": 4608.0}, "region_prices": + [{"id": "us-east", "hourly": 8.292, "monthly": 5529.6}], "addons": {"backups": + {"price": {"hourly": 0.36, "monthly": 240.0}, "region_prices": [{"id": "us-east", + "hourly": 0.432, "monthly": 288.0}]}}, "memory": 524288, "disk": 7372800, "transfer": + 12000, "vcpus": 64, "gpus": 0, "network_out": 12000, "class": "premium", "successor": + null}], "page": 1, "pages": 1, "results": 46}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=900 + - private, max-age=60, s-maxage=60 + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - '*' + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1200" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/longview_test.go b/test/integration/longview_test.go index 1e0ba0179..da0a29339 100644 --- a/test/integration/longview_test.go +++ b/test/integration/longview_test.go @@ -99,7 +99,7 @@ func TestLongviewClient_Delete(t *testing.T) { t.Errorf("Error getting longview client:%s", getErr) } - // If there is no error, the longview client was delete properly + // If there is no error, the longview client was deleted properly if err := client.DeleteLongviewClient(context.Background(), testingLongviewClient.ID); err != nil { t.Fatal(err) } diff --git a/test/integration/types_test.go b/test/integration/types_test.go index 519f08c9c..3331c6739 100644 --- a/test/integration/types_test.go +++ b/test/integration/types_test.go @@ -50,3 +50,50 @@ func TestTypes_List(t *testing.T) { t.Errorf("Expected a list of images, but got none %v", i) } } + +func TestTypes_RegionSpecific(t *testing.T) { + client, teardown := createTestClient(t, "fixtures/TestTypes_RegionSpecific") + defer teardown() + + validateOverride := func(override linodego.LinodeRegionPrice) { + if override.ID == "" { + t.Fatal("Expected region; got nil") + } + + if override.Monthly <= 0 { + t.Fatalf("Expected monthly cost; got %f", override.Monthly) + } + + if override.Hourly <= 0 { + t.Fatalf("Expected hourly cost; got %f", override.Hourly) + } + } + + types, err := client.ListTypes(context.Background(), nil) + if err != nil { + t.Errorf("Error listing images, expected struct, got error %v", err) + } + + var targetType *linodego.LinodeType + for _, t := range types { + if t.RegionPrices != nil && + len(t.RegionPrices) > 0 && + t.RegionPrices[0].Hourly > 0 { + targetType = &t + break + } + } + + if targetType == nil { + t.Fatal("expected type with region override, got none") + } + + // Validate overrides + for _, override := range targetType.RegionPrices { + validateOverride(override) + } + + for _, override := range targetType.Addons.Backups.RegionPrices { + validateOverride(override) + } +} diff --git a/test/integration/util_test.go b/test/integration/util_test.go index a3ad1cb41..f73ca223e 100644 --- a/test/integration/util_test.go +++ b/test/integration/util_test.go @@ -3,7 +3,7 @@ package integration import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "reflect" "regexp" @@ -29,7 +29,7 @@ func mockRequestBodyValidate(t *testing.T, expected interface{}, response interf i := result.Interface() - data, err := ioutil.ReadAll(request.Body) + data, err := io.ReadAll(request.Body) if err != nil { t.Fatal(err) } diff --git a/types.go b/types.go index 01d655340..1d8341367 100644 --- a/types.go +++ b/types.go @@ -10,18 +10,19 @@ import ( // LinodeType represents a linode type object type LinodeType struct { - ID string `json:"id"` - Disk int `json:"disk"` - Class LinodeTypeClass `json:"class"` // enum: nanode, standard, highmem, dedicated, gpu - Price *LinodePrice `json:"price"` - Label string `json:"label"` - Addons *LinodeAddons `json:"addons"` - NetworkOut int `json:"network_out"` - Memory int `json:"memory"` - Transfer int `json:"transfer"` - VCPUs int `json:"vcpus"` - GPUs int `json:"gpus"` - Successor string `json:"successor"` + ID string `json:"id"` + Disk int `json:"disk"` + Class LinodeTypeClass `json:"class"` // enum: nanode, standard, highmem, dedicated, gpu + Price *LinodePrice `json:"price"` + Label string `json:"label"` + Addons *LinodeAddons `json:"addons"` + RegionPrices []LinodeRegionPrice `json:"region_prices"` + NetworkOut int `json:"network_out"` + Memory int `json:"memory"` + Transfer int `json:"transfer"` + VCPUs int `json:"vcpus"` + GPUs int `json:"gpus"` + Successor string `json:"successor"` } // LinodePrice represents a linode type price object @@ -32,7 +33,8 @@ type LinodePrice struct { // LinodeBackupsAddon represents a linode backups addon object type LinodeBackupsAddon struct { - Price *LinodePrice `json:"price"` + Price *LinodePrice `json:"price"` + RegionPrices []LinodeRegionPrice `json:"region_prices"` } // LinodeAddons represent the linode addons object @@ -40,6 +42,14 @@ type LinodeAddons struct { Backups *LinodeBackupsAddon `json:"backups"` } +// LinodeRegionPrice represents an individual type or addon +// price exception for a region. +type LinodeRegionPrice struct { + ID string `json:"id"` + Hourly float32 `json:"hourly"` + Monthly float32 `json:"monthly"` +} + // LinodeTypeClass constants start with Class and include Linode API Instance Type Classes type LinodeTypeClass string diff --git a/waitfor.go b/waitfor.go index 649554b4f..e31426948 100644 --- a/waitfor.go +++ b/waitfor.go @@ -6,10 +6,14 @@ import ( "log" "net/http" "strconv" - "strings" "time" + + "golang.org/x/text/cases" + "golang.org/x/text/language" ) +var englishTitle = cases.Title(language.English) + type EventPoller struct { EntityID any EntityType EntityType @@ -284,7 +288,7 @@ func (client Client) WaitForEventFinished( minStart time.Time, timeoutSeconds int, ) (*Event, error) { - titledEntityType := strings.Title(string(entityType)) + titledEntityType := englishTitle.String(string(entityType)) filter := Filter{ Order: Descending, OrderBy: "created",