pax_global_header00006660000000000000000000000064142521142140014506gustar00rootroot0000000000000052 comment=d9b3f1938e2b70434fa95137ae518ea1876feaae golang-github-vultr-govultr-2.17.2/000077500000000000000000000000001425211421400172005ustar00rootroot00000000000000golang-github-vultr-govultr-2.17.2/.github/000077500000000000000000000000001425211421400205405ustar00rootroot00000000000000golang-github-vultr-govultr-2.17.2/.github/dependabot.yml000066400000000000000000000001771425211421400233750ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: "/" schedule: interval: daily open-pull-requests-limit: 10 golang-github-vultr-govultr-2.17.2/.github/workflows/000077500000000000000000000000001425211421400225755ustar00rootroot00000000000000golang-github-vultr-govultr-2.17.2/.github/workflows/codeql-analysis.yml000066400000000000000000000046121425211421400264130ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '0 3 * * *' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 golang-github-vultr-govultr-2.17.2/.github/workflows/coverage.yml000066400000000000000000000030331425211421400251120ustar00rootroot00000000000000name: "Unit/Coverage Tests" on: push: branches: - master pull_request_target: branches: - master jobs: coverage: runs-on: ubuntu-latest outputs: msg: ${{ steps.results.outputs.content }} steps: - uses: actions/checkout@v2 - name: Setup Go uses: actions/setup-go@v2 with: go-version: 1.17 - name: Run unit tests and coverage test id: test-coverage run: | go test -cover -v > output.txt - name: Transform output id: results if: always() run: | CONTENT=$(cat output.txt) CONTENT="${CONTENT//'%'/'%25'}" CONTENT="${CONTENT//$'\n'/'%0A'}" CONTENT="${CONTENT//$'\r'/'%0D'}" echo "::set-output name=content::$CONTENT" comment: runs-on: ubuntu-latest if: github.event_name != 'push' needs: coverage steps: - name: Add Comment uses: actions/github-script@v5 if: always() with: script: | const output = `### Unit Tests and Coverage
Show Output \`\`\` ${{ needs.coverage.outputs.msg }} \`\`\`
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; await github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: output }) golang-github-vultr-govultr-2.17.2/.github/workflows/gochecks.yml000066400000000000000000000021431425211421400251060ustar00rootroot00000000000000name: Checks on: push: branches: - master pull_request: branches: - master jobs: Go-Lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup Go uses: actions/setup-go@v2 with: go-version: '1.17' - name: Install dependencies run: | go version go get -u golang.org/x/lint/golint - name: Run Lint run: | golint_files=$(golint .) if [[ -n ${golint_files} ]]; then echo 'fix the following linting errors:' echo "${golint_files}" exit 1 fi exit 0 Go-Fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup Go uses: actions/setup-go@v2 with: go-version: '1.17' - name: Run fmt run: | gofmt_files=$(gofmt -l .) if [[ -n ${gofmt_files} ]]; then echo 'gofmt needs running on the following files:' echo "${gofmt_files}" exit 1 fi exit 0 golang-github-vultr-govultr-2.17.2/.github/workflows/notify-issue.yml000066400000000000000000000010651425211421400257600ustar00rootroot00000000000000name: notify-issue on: issues: types: [opened] jobs: issue: runs-on: ubuntu-latest name: New Issue Notification steps: - run: | echo "{\"text\":\"GoVultr : New Issue https://github.com/vultr/govultr/issues/${{ github.event.issue.number }} \"}" > mattermost.json - uses: mattermost/action-mattermost-notify@master env: MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }} MATTERMOST_USERNAME: ${{ secrets.MATTERMOST_USERNAME}} MATTERMOST_ICON: ${{ secrets.MATTERMOST_ICON }} golang-github-vultr-govultr-2.17.2/.github/workflows/notify-pr.yml000066400000000000000000000010311425211421400252420ustar00rootroot00000000000000name: notify-pr on: pull_request_target jobs: pr: runs-on: ubuntu-latest name: Pull Request Notification steps: - run: | echo "{\"text\":\"GoVultr : PR https://github.com/vultr/govultr/pull/${{ github.event.number }} \"}" > mattermost.json - uses: mattermost/action-mattermost-notify@master env: MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }} MATTERMOST_USERNAME: ${{ secrets.MATTERMOST_USERNAME}} MATTERMOST_ICON: ${{ secrets.MATTERMOST_ICON }} golang-github-vultr-govultr-2.17.2/.github/workflows/releaser.yml000066400000000000000000000040631425211421400251250ustar00rootroot00000000000000name: "Automatic Releaser" on: push: branches: - master permissions: contents: write jobs: check-commit: runs-on: ubuntu-latest outputs: msg_check: ${{ steps.check-msg.outputs.match }} steps: - name: Check Message id: check-msg run: | pattern="^Release v[0-9]+.[0-9]+.[0-9]+ #(minor|major|patch)$" if [[ "${{ github.event.head_commit.message }}" =~ ${pattern} ]]; then echo ::set-output name=match::true fi create-tag: runs-on: ubuntu-latest if: needs.check-commit.outputs.msg_check == 'true' needs: check-commit outputs: new_tag: ${{ steps.tagger.outputs.new_tag }} steps: - uses: actions/checkout@v2 with: fetch-depth: '0' - name: Bump version and push tag id: tagger uses: anothrNick/github-tag-action@1.36.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} WITH_V: true DEFAULT_BUMP: "none" goreleaser: runs-on: ubuntu-latest needs: create-tag steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v2 with: go-version: 1.17 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: distribution: goreleaser version: latest args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} release: runs-on: ubuntu-latest needs: ["goreleaser", "create-tag"] name: Release Notification steps: - run: | echo "{\"text\":\"GoVultr : Release https://github.com/${{ github.repository }}/releases/tag/${{ needs.create-tag.outputs.new_tag }} \"}" > mattermost.json - uses: mattermost/action-mattermost-notify@master env: MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }} MATTERMOST_USERNAME: ${{ secrets.MATTERMOST_USERNAME}} MATTERMOST_ICON: ${{ secrets.MATTERMOST_ICON }} golang-github-vultr-govultr-2.17.2/.gitignore000066400000000000000000000004121425211421400211650ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out ### Go Patch ### /vendor/ /Godeps/ .DS_Store .idea .vscode ### Misc. cmdgolang-github-vultr-govultr-2.17.2/.goreleaser.yml000066400000000000000000000006011425211421400221260ustar00rootroot00000000000000before: hooks: - go mod download - go generate ./... builds: - skip: true checksum: name_template: "{{ .ProjectName }}_v{{ .Version }}_checksums.txt" algorithm: sha256 snapshot: name_template: "{{ .ProjectName }}_v{{ .Version }}" changelog: sort: asc filters: exclude: - '^docs:' - '^test:' release: github: owner: Vultr name: govultrgolang-github-vultr-govultr-2.17.2/CHANGELOG.md000066400000000000000000000267421425211421400210240ustar00rootroot00000000000000# Change Log ## GoVultr v1 changelog is located [here](https://github.com/vultr/govultr/blob/v1/CHANGELOG.md) ## [v2.17.2](https://github.com/vultr/govultr/compare/v2.17.1...v2.17.2) (2022-06-13) ### Enhancement * Reserved IP: Add support for updating label [227](https://github.com/vultr/govultr/pull/227) ## [v2.17.1](https://github.com/vultr/govultr/compare/v2.17.0...v2.17.1) (2022-06-02) * Plans: Add GPU specific fields to plan struct [224](https://github.com/vultr/govultr/pull/224) ## [v2.17.0](https://github.com/vultr/govultr/compare/v2.16.0..v2.17.0) (2022-05-17) ### Enhancement * Kubernetes: allow `tag` update to delete existing value [222](https://github.com/vultr/govultr/pull/222) * Baremetal: allow `tag` update to delete existing value [222](https://github.com/vultr/govultr/pull/222) * Instance: allow `tag` update to delete existing value [222](https://github.com/vultr/govultr/pull/222) ### Bug fixes * Kubernetes: fix data type for `auto_scaler` to avoid sending null values in requests when not set [222](https://github.com/vultr/govultr/pull/222) ### Breaking Change * Kubernetes: change data type for `Tag` in node pool update requirements struct [222](https://github.com/vultr/govultr/pull/222) * Kubernetes: change data type for `AutoScaler` in node pool update requirements struct [222](https://github.com/vultr/govultr/pull/222) * Baremetal: change data type for `Tag` in update requirements struct [222](https://github.com/vultr/govultr/pull/222) * Instance: change data type for `Tag` in update requirements struct [222](https://github.com/vultr/govultr/pull/222) ## [v2.16.0](https://github.com/vultr/govultr/compare/v2.15.1..v2.16.0) (2022-05-04) ### Enhancement * Kubernetes: added auto scaler options to node pools [215](https://github.com/vultr/govultr/pull/215) * Firewall rules: added new field `ip_type` in get/list responses to be consistent with the create calls [216](https://github.com/vultr/govultr/pull/216) * Kubernetes: Upgrade support [217](https://github.com/vultr/govultr/pull/217) * Baremetal: Added support for new `tags` field. This field allows multiple string tags to be associated with an instance [218](https://github.com/vultr/govultr/pull/218) * Instance: Added support for new `tags` field. This field allows multiple string tags to be associated with an instance [218](https://github.com/vultr/govultr/pull/218) ### Deprecations * Instance: The `tag` field has been deprecated in favor for `tags` [218](https://github.com/vultr/govultr/pull/218) * Baremetal: The `tag` field has been deprecated in favor for `tags` [218](https://github.com/vultr/govultr/pull/218) * Firewall rules: The `type` field has been deprecated in favor for `ip_type` [216](https://github.com/vultr/govultr/pull/216) ### Dependency Update * Bump github.com/hashicorp/go-retryablehttp from 0.7.0 to 0.7.1 [214](https://github.com/vultr/govultr/pull/214) ## [v2.15.1](https://github.com/vultr/govultr/compare/v2.15.0..v2.15.1) (2022-04-12) ### Bug fixes * Block : add `omityempty` to `block_type` to prevent deploy issues [212](https://github.com/vultr/govultr/pull/212) ## [v2.15.0](https://github.com/vultr/govultr/compare/v2.14.2..v2.15.0) (2022-04-12) ### Enhancement * Block : New optional field `block_type`. This new field is currently optional but may become required at a later release [209](https://github.com/vultr/govultr/pull/209) * VPC : New API endpoints that will be replacing `network` [210](https://github.com/vultr/govultr/pull/210) * Updated Go version from 1.16 to 1.17 [208](https://github.com/vultr/govultr/pull/208) ### Deprecations * Network : The network resource and all related private network fields on structs are deprecated. You should now be using the VPC provided replacements [210](https://github.com/vultr/govultr/pull/210) ## [v2.14.2](https://github.com/vultr/govultr/compare/v2.14.1..v2.14.2) (2022-03-23) ### Bug Fix * Instances : restore support requestBody [206](https://github.com/vultr/govultr/pull/206) Thanks @andrake81 ## [v2.14.1](https://github.com/vultr/govultr/compare/v2.14.0..v2.14.1) (2022-02-02) ### Enhancement * Improved retry error response [204](https://github.com/vultr/govultr/pull/204) ## [v2.14.0](https://github.com/vultr/govultr/compare/v2.13.0..v2.14.0) (2022-01-21) ### Enhancement * ListOptions : [Added query param Region](https://www.vultr.com/api/#operation/list-instances) that can be used with `Instance.List` [200](https://github.com/vultr/govultr/pull/200) * ListOptions : [Added query param Description](https://www.vultr.com/api/#operation/list-snapshots) that can be used with `Snapshot.List` [202](https://github.com/vultr/govultr/pull/202) * Snapshot : `CreateFromURL` has new optional field called `description` which lets you set a custom description [202](https://github.com/vultr/govultr/pull/202) ## [v2.13.0](https://github.com/vultr/govultr/compare/v2.12.0..v2.13.0) (2022-01-05) ### Enhancement * ListOptions : [Added query params](https://www.vultr.com/api/#operation/list-instances) that can be used with `Instance.List` [197](https://github.com/vultr/govultr/pull/197) ## [v2.12.0](https://github.com/vultr/govultr/compare/v2.11.1..v2.12.0) (2021-12-01) ### Breaking Changes * Plans : Changed `MonthlyCost` from `int` to `float32` [192](https://github.com/vultr/govultr/pull/192) ## [v2.11.1](https://github.com/vultr/govultr/compare/v2.11.0..v2.11.1) (2021-11-26) ### Bug fixes * LoadBalancers : Fixed SSL struct json params to the proper API fields [189](https://github.com/vultr/govultr/pull/189) ## [v2.11.0](https://github.com/vultr/govultr/compare/v2.10.0..v2.11.0) (2021-11-18) ### Breaking Changes * Instances : Update call will now return `*Instance` in addition to `error` [185](https://github.com/vultr/govultr/pull/185) * Instances : Reinstall call now allows changing of hostname and also returns `*Instance` in addition to `error` [181](https://github.com/vultr/govultr/pull/181) ### Enhancement * Instances : The hostname of the instance is now returned in any call that returns Instance data [187](https://github.com/vultr/govultr/pull/187) * Domains : There is a new field called `dns_sec` which will return `enabled` or `disabled` depending on how your domain is configured [184](https://github.com/vultr/govultr/pull/184) ## [v2.10.0](https://github.com/vultr/govultr/compare/v2.9.2..v2.10.0) (2021-11-03) ### Enhancement * Billing : Added support for billing [178](https://github.com/vultr/govultr/pull/178) ## [v2.9.2](https://github.com/vultr/govultr/compare/v2.9.1..v2.9.2) (2021-10-20) ### Change * Iso : Changed `client` field to be unexported [168](https://github.com/vultr/govultr/pull/168) * Snapshot : Changed `client` field to be unexported [168](https://github.com/vultr/govultr/pull/168) * Plans : Changed `client` field to be unexported [168](https://github.com/vultr/govultr/pull/168) * Regions : Changed `client` field to be unexported [168](https://github.com/vultr/govultr/pull/168) ## [v2.9.1](https://github.com/vultr/govultr/compare/v2.9.0..v2.9.1) (2021-10-18) ### Enhancement * Kubernetes : Added `Tag` support for nodepools [164](https://github.com/vultr/govultr/pull/164) ## [v2.9.0](https://github.com/vultr/govultr/compare/v2.8.1..v2.9.0) (2021-09-27) ### Breaking Change * Kubernetes : PlanID is now Plan and Count is now NodeQuantity to follow API pattern [161](https://github.com/vultr/govultr/pull/161) ### Enhancement * Snapshots : Add compressed size field [162](https://github.com/vultr/govultr/pull/162) ## [v2.8.1](https://github.com/vultr/govultr/compare/v2.8.0..v2.8.1) (2021-08-31) ### Enhancement * Kubernetes : Add support for deletion with resources [159](https://github.com/vultr/govultr/pull/159) * Kubernetes : Add support for getting available versions[159](https://github.com/vultr/govultr/pull/159) ### Dependency Update * Bump Go version to 1.16 [158](https://github.com/vultr/govultr/pull/158) ## [v2.8.0](https://github.com/vultr/govultr/compare/v2.7.1..v2.8.0) (2021-08-18) ### Enhancement * Added support for Vultr Kubernetes Engine [156](https://github.com/vultr/govultr/pull/156) ## [v2.7.1](https://github.com/vultr/govultr/compare/v2.7.0..v2.7.1) (2021-07-15) ### Enhancement * BareMetal : Add support for `image_id` on update [152](https://github.com/vultr/govultr/pull/152) * Instances : Add support for `image_id` on update [152](https://github.com/vultr/govultr/pull/152) ## [v2.7.0](https://github.com/vultr/govultr/compare/v2.6.0..v2.7.0) (2021-07-14) ### Enhancement * BareMetal : Add support for `image_id` [150](https://github.com/vultr/govultr/pull/150) * Instances : Add support for `image_id` [150](https://github.com/vultr/govultr/pull/150) * Applications : added support for marketplace applications [150](https://github.com/vultr/govultr/pull/150) ## [v2.6.0](https://github.com/vultr/govultr/compare/v2.5.1..v2.6.0) (2021-07-02) ### Enhancement * BareMetal : Add support for `persistent_pxe` [148](https://github.com/vultr/govultr/pull/148) ## [v2.5.1](https://github.com/vultr/govultr/compare/v2.5.0..v2.5.1) (2021-05-10) ### Bug fix * Instances : BackupScheduleReq change DOW + Hour to pointers [145](https://github.com/vultr/govultr/pull/145) ## [v2.5.0](https://github.com/vultr/govultr/compare/v2.4.2..v2.5.0) (2021-05-06) ### Enhancement * LoadBalancers : New Features and endpoints [143](https://github.com/vultr/govultr/pull/143) * Ability to attach private networks * Ability to set firewalls * Get Firewall Rules * List Firewall Rules ## [v2.4.2](https://github.com/vultr/govultr/compare/v2.4.1..v2.4.2) (2021-05-03) ### Bug fix * Instances : ListPrivateNetworks missing paging ability [140](https://github.com/vultr/govultr/pull/140) ## [v2.4.1](https://github.com/vultr/govultr/compare/v2.4.0..v2.4.1) (2021-05-03) ### Dependency updates * Bump github.com/hashicorp/go-retryablehttp from 0.6.8 to 0.7.0 [138](https://github.com/vultr/govultr/pull/138) * Bump github.com/google/go-querystring from 1.0.0 to 1.1.0 [137](https://github.com/vultr/govultr/pull/137) ## [v2.4.0](https://github.com/vultr/govultr/compare/v2.3.2..v2.4.0) (2021-02-11) ### Enhancement * Block Storage - add `mount_id` field to BlockStorage struct [131](https://github.com/vultr/govultr/pull/131) * Plans - add `disk_count` field to Plan and BareMetalPlan struct [130](https://github.com/vultr/govultr/pull/130) ## [v2.3.2](https://github.com/vultr/govultr/compare/v2.3.1..v2.3.2) (2021-01-06) ### Bug Fix * Instances - Fixed DetachPrivateNetwork which had wrong URI [122](https://github.com/vultr/govultr/pull/122) ## [v2.3.1](https://github.com/vultr/govultr/compare/v2.3.0..v2.3.1) (2021-01-04) ### Bug Fix * Domain Record - removed `omitempty` on `name` field in `DomainRecordReq` to allow creation of nameless records. [120](https://github.com/vultr/govultr/pull/120) ## [v2.3.0](https://github.com/vultr/govultr/compare/v2.2.0..v2.3.0) (2020-12-17) ### Enhancement * Bare Metal - Start call added [118](https://github.com/vultr/govultr/pull/118) ## [v2.2.0](https://github.com/vultr/govultr/compare/v2.1.0..v2.2.0) (2020-12-07) ### Breaking Change * All bools have been updated to pointers to avoid issues where false values not being sent in request. Thanks @Static-Flow [115](https://github.com/vultr/govultr/pull/115) ## [v2.1.0](https://github.com/vultr/govultr/compare/v2.0.0..v2.1.0) (2020-11-30) ### Bug fixes * ReservedIP - Attach call creates proper json. [112](https://github.com/vultr/govultr/pull/112) * User - APIEnabled takes pointer of bool [112](https://github.com/vultr/govultr/pull/112) ## v2.0.0 (2020-11-20) ### Initial Release * GoVultr v2.0.0 Release - Uses Vultr API v2. * GoVultr v1.0.0 is now on [branch v1](https://github.com/vultr/govultr/tree/v1) golang-github-vultr-govultr-2.17.2/CONTRIBUTING.md000066400000000000000000000033771425211421400214430ustar00rootroot00000000000000# Contributing to `govultr` We would love to get your feedback, thoughts, and overall improvements to `govultr`! ## Overview - All code should run through `go fmt` - All code **must be tested** - All types, structs, and funcs **must be documented** for GoDocs ## Getting started GoVultr supports `go modules` so you can pull down the repo outside of your `$GOPATH`. You can also run: `go get -u github.com/vultr/govultr` ## Testing We aim to have as much code coverage as possible. To run tests locally: ```sh go test . ``` If you want to get more information on your local unit tests. You can run the following: ```sh go test -v -coverprofile cover.out go tool cover -html=cover.out ``` Upon opening a pull request we have CodeCov checks to make sure that code coverage meets a minimum requirement. In addition to CodeCov we have Travis CI that will run your unit tests on each pull request as well. ## Versioning GoVultr follows [SemVer](http://semver.org/) for versioning. New functionality will result in a increment to the minor version and bug fixes will result in a increment to the patch version. ## Releases Releases of new versions are done as independent pull requests and will be made by the maintainers. To release a new version of `govultr` we must do the following: - Update version number in `govultr.go` to reflect the new release version - Make the appropriate updates to `CHANGELOG.md`. This should include the: - Version, - List of fix/features with accompanying pull request ID - Description of each fix/feature ``` ## v0.0.1 (2019-05-05) ### Fixes * Fixed random bug #12 ### Features * BareMetalServer functionality #13 ``` - Submit a pull request with the changes above. - Once the pull request is merged in, create a new tag and publish. golang-github-vultr-govultr-2.17.2/LICENSE000066400000000000000000000020461425211421400202070ustar00rootroot00000000000000MIT License Copyright (c) 2019 Vultr Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-vultr-govultr-2.17.2/README.md000066400000000000000000000103351425211421400204610ustar00rootroot00000000000000# GoVultr [![Automatic Releaser](https://github.com/vultr/govultr/actions/workflows/releaser.yml/badge.svg)](https://github.com/vultr/govultr/actions/workflows/releaser.yml) [![PkgGoDev](https://pkg.go.dev/badge/github.com/vultr/govultr/v2)](https://pkg.go.dev/github.com/vultr/govultr/v2) [![Unit/Coverage Tests](https://github.com/vultr/govultr/actions/workflows/coverage.yml/badge.svg)](https://github.com/vultr/govultr/actions/workflows/coverage.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/vultr/govultr)](https://goreportcard.com/report/github.com/vultr/govultr) The official Vultr Go client - GoVultr allows you to interact with the Vultr V2 API. GoVultr V1 that interacts with Vultr V1 API is now on the [v1 branch](https://github.com/vultr/govultr/tree/v1) ## Installation ```sh go get -u github.com/vultr/govultr/v2 ``` ## Usage Vultr uses a PAT (Personal Access token) to interact/authenticate with the APIs. Generate an API Key from the [API menu](https://my.vultr.com/settings/#settingsapi) in the Vultr Customer Portal. To instantiate a GoVultr client, invoke `NewClient()`. You must pass your `PAT` to an `oauth2` library to create the `*http.Client`, which configures the `Authorization` header with your PAT as the `bearer api-key`. The client has three optional parameters: - BaseUrl: Change the Vultr default base URL - UserAgent: Change the Vultr default UserAgent - RateLimit: Set a delay between calls. Vultr limits the rate of back-to-back calls. Use this parameter to avoid rate-limit errors. ### Example Client Setup ```go package main import ( "context" "os" "github.com/vultr/govultr/v2" "golang.org/x/oauth2" ) func main() { apiKey := os.Getenv("VultrAPIKey") config := &oauth2.Config{} ctx := context.Background() ts := config.TokenSource(ctx, &oauth2.Token{AccessToken: apiKey}) vultrClient := govultr.NewClient(oauth2.NewClient(ctx, ts)) // Optional changes _ = vultrClient.SetBaseURL("https://api.vultr.com") vultrClient.SetUserAgent("mycool-app") vultrClient.SetRateLimit(500) } ``` ### Example Usage Create a VPS ```go instanceOptions := &govultr.InstanceCreateReq{ Label: "awesome-go-app", Hostname: "awesome-go.com", Backups: "enabled", EnableIPv6: BoolToBoolPtr(false), OsID: 362, Plan: "vc2-1c-2gb", Region: "ewr", } res, err := vultrClient.Instance.Create(context.Background(), instanceOptions) if err != nil { fmt.Println(err) } ``` ## Pagination GoVultr v2 introduces pagination for all list calls. Each list call returns a `meta` struct containing the total amount of items in the list and next/previous links to navigate the paging. ```go // Meta represents the available pagination information type Meta struct { Total int `json:"total"` Links *Links } // Links represent the next/previous cursor in your pagination calls type Links struct { Next string `json:"next"` Prev string `json:"prev"` } ``` Pass a `per_page` value to the `list_options` struct to adjust the number of items returned per call. The default is 100 items per page and max is 500 items per page. This example demonstrates how to retrieve all of your instances, with one instance per page. ```go listOptions := &govultr.ListOptions{PerPage: 1} for { i, meta, err := client.Instance.List(ctx, listOptions) if err != nil { return nil, err } for _, v := range i { fmt.Println(v) } if meta.Links.Next == "" { break } else { listOptions.Cursor = meta.Links.Next continue } } ``` ## Versioning This project follows [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/vultr/govultr/tags). ## Documentation See our documentation for [detailed information about API v2](https://www.vultr.com/api/). See our [GoDoc](https://pkg.go.dev/github.com/vultr/govultr/v2) documentation for more details about this client's functionality. ## Contributing Feel free to send pull requests our way! Please see the [contributing guidelines](CONTRIBUTING.md). ## License This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details. golang-github-vultr-govultr-2.17.2/account.go000066400000000000000000000023421425211421400211640ustar00rootroot00000000000000package govultr import ( "context" "net/http" ) // AccountService is the interface to interact with Accounts endpoint on the Vultr API // Link : https://www.vultr.com/api/#tag/account type AccountService interface { Get(ctx context.Context) (*Account, error) } // AccountServiceHandler handles interaction with the account methods for the Vultr API type AccountServiceHandler struct { client *Client } type accountBase struct { Account *Account `json:"account"` } // Account represents a Vultr account type Account struct { Balance float32 `json:"balance"` PendingCharges float32 `json:"pending_charges"` LastPaymentDate string `json:"last_payment_date"` LastPaymentAmount float32 `json:"last_payment_amount"` Name string `json:"name"` Email string `json:"email"` ACL []string `json:"acls"` } // Get Vultr account info func (a *AccountServiceHandler) Get(ctx context.Context) (*Account, error) { uri := "/v2/account" req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } account := new(accountBase) if err = a.client.DoWithContext(ctx, req, account); err != nil { return nil, err } return account.Account, nil } golang-github-vultr-govultr-2.17.2/account_test.go000066400000000000000000000020621425211421400222220ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestAccountServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/account", func(w http.ResponseWriter, r *http.Request) { response := ` { "account" : { "balance": -5519.11, "pending_charges": 57.03, "last_payment_date": "2014-07-18 15:31:01", "last_payment_amount": -1.00, "name": "Test Tester", "email" : "example@vultr.com", "acls": [ "subscriptions", "billing", "support", "provisioning" ] } } ` fmt.Fprint(w, response) }) account, err := client.Account.Get(ctx) if err != nil { t.Errorf("Account.Get returned error: %v", err) } expected := &Account{Balance: -5519.11, PendingCharges: 57.03, LastPaymentDate: "2014-07-18 15:31:01", LastPaymentAmount: -1.00, Name: "Test Tester", Email: "example@vultr.com", ACL: []string{"subscriptions", "billing", "support", "provisioning"}} if !reflect.DeepEqual(account, expected) { t.Errorf("Account.Get returned %+v, expected %+v", account, expected) } } golang-github-vultr-govultr-2.17.2/application.go000066400000000000000000000031771425211421400220420ustar00rootroot00000000000000package govultr import ( "context" "net/http" "github.com/google/go-querystring/query" ) // ApplicationService is the interface to interact with the Application endpoint on the Vultr API. // Link : https://www.vultr.com/api/#tag/application type ApplicationService interface { List(ctx context.Context, options *ListOptions) ([]Application, *Meta, error) } // ApplicationServiceHandler handles interaction with the application methods for the Vultr API. type ApplicationServiceHandler struct { client *Client } // Application represents all available apps that can be used to deployed with vultr Instances. type Application struct { ID int `json:"id"` Name string `json:"name"` ShortName string `json:"short_name"` DeployName string `json:"deploy_name"` Type string `json:"type"` Vendor string `json:"vendor"` ImageID string `json:"image_id"` } type applicationBase struct { Applications []Application `json:"applications"` Meta *Meta `json:"meta"` } // List retrieves a list of available applications that can be launched when creating a Vultr instance func (a *ApplicationServiceHandler) List(ctx context.Context, options *ListOptions) ([]Application, *Meta, error) { uri := "/v2/applications" req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() apps := new(applicationBase) err = a.client.DoWithContext(ctx, req, apps) if err != nil { return nil, nil, err } return apps.Applications, apps.Meta, nil } golang-github-vultr-govultr-2.17.2/application_test.go000066400000000000000000000026401425211421400230730ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestApplicationServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/applications", func(w http.ResponseWriter, r *http.Request) { response := ` { "applications": [ { "id": 1, "name": "LEMP", "short_name": "lemp", "deploy_name": "LEMP on CentOS 6 x64", "type": "one-click", "vendor": "", "image_id": "" } ], "meta": { "total": 29, "links": { "next": "bmV4dF9fNDM=", "prev": "" } } } ` fmt.Fprint(w, response) }) options := &ListOptions{ PerPage: 1, Cursor: "", } apps, meta, err := client.Application.List(ctx, options) if err != nil { t.Errorf("Application.List returned error: %v", err) } expected := []Application{ { ID: 1, Name: "LEMP", ShortName: "lemp", DeployName: "LEMP on CentOS 6 x64", Vendor: "", Type: "one-click", ImageID: "", }, } if !reflect.DeepEqual(apps, expected) { t.Errorf("Application.List apps returned %+v, expected %+v", apps, expected) } expectedMeta := &Meta{ Total: 29, Links: &Links{ Next: "bmV4dF9fNDM=", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Application.List meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/backup.go000066400000000000000000000037021425211421400207760ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // BackupService is the interface to interact with the backup endpoint on the Vultr API // Link : https://www.vultr.com/api/#tag/backup type BackupService interface { Get(ctx context.Context, backupID string) (*Backup, error) List(ctx context.Context, options *ListOptions) ([]Backup, *Meta, error) } // BackupServiceHandler handles interaction with the backup methods for the Vultr API type BackupServiceHandler struct { client *Client } // Backup represents a Vultr backup type Backup struct { ID string `json:"id"` DateCreated string `json:"date_created"` Description string `json:"description"` Size int `json:"size"` Status string `json:"status"` } type backupsBase struct { Backups []Backup `json:"backups"` Meta *Meta `json:"meta"` } type backupBase struct { Backup *Backup `json:"backup"` } // Get retrieves a backup that matches the given backupID func (b *BackupServiceHandler) Get(ctx context.Context, backupID string) (*Backup, error) { uri := fmt.Sprintf("/v2/backups/%s", backupID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } backup := new(backupBase) if err := b.client.DoWithContext(ctx, req, backup); err != nil { return nil, err } return backup.Backup, nil } // List retrieves a list of all backups on the current account func (b *BackupServiceHandler) List(ctx context.Context, options *ListOptions) ([]Backup, *Meta, error) { uri := "/v2/backups" req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() backups := new(backupsBase) if err = b.client.DoWithContext(ctx, req, backups); err != nil { return nil, nil, err } return backups.Backups, backups.Meta, nil } golang-github-vultr-govultr-2.17.2/backup_test.go000066400000000000000000000065061425211421400220420ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestBackupServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/backups", func(w http.ResponseWriter, r *http.Request) { response := ` { "backups": [ { "id": "543d34149403a", "date_created": "2014-10-14 12:40:40", "description": "Automatic server backup", "size": 42949672960, "status": "complete" } ], "meta": { "total":8, "links": { "next":"", "prev":"" } } } ` fmt.Fprint(w, response) }) backups, meta, err := client.Backup.List(ctx, nil) if err != nil { t.Errorf("Backup.List returned error: %v", err) } expected := []Backup{ { ID: "543d34149403a", DateCreated: "2014-10-14 12:40:40", Description: "Automatic server backup", Size: 42949672960, Status: "complete", }, } expectedMeta := &Meta{ Total: 8, Links: &Links{}, } if !reflect.DeepEqual(backups, expected) { t.Errorf("Backup.List returned %+v, expected %+v", backups, expected) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Backup.List returned %+v, expected %+v", meta, expectedMeta) } } func TestBackupServiceHandler_ListEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/backups", func(w http.ResponseWriter, r *http.Request) { response := ` { "backups": [], "meta": { "total":0, "links": { "next":"", "prev":"" } } } ` fmt.Fprint(w, response) }) backups, meta, err := client.Backup.List(ctx, nil) if err != nil { t.Errorf("Backup.List returned error: %v", err) } expected := []Backup{} if !reflect.DeepEqual(backups, expected) { t.Errorf("Backup.List returned %+v, expected %+v", backups, expected) } expectedMeta := &Meta{ Total: 0, Links: &Links{ Next: "", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Backup.List meta returned %+v, expected %+v", meta, expectedMeta) } } func TestBackupServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/backups/543d34149403a", func(w http.ResponseWriter, r *http.Request) { response := ` { "backup": { "id": "543d34149403a", "date_created": "2014-10-14 12:40:40", "description": "Automatic server backup", "size": 42949672960, "status": "complete" } } ` fmt.Fprint(w, response) }) backup, err := client.Backup.Get(ctx, "543d34149403a") if err != nil { t.Errorf("Backup.Get returned error: %v", err) } expected := &Backup{ ID: "543d34149403a", DateCreated: "2014-10-14 12:40:40", Description: "Automatic server backup", Size: 42949672960, Status: "complete", } if !reflect.DeepEqual(backup, expected) { t.Errorf("Backup.Get returned %+v, expected %+v", backup, expected) } } func TestBackupServiceHandler_GetEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/backups/543d34149403a", func(w http.ResponseWriter, r *http.Request) { response := ` { "backup": {} } ` fmt.Fprint(w, response) }) backup, err := client.Backup.Get(ctx, "543d34149403a") if err != nil { t.Errorf("Backup.Get returned error: %v", err) } expected := &Backup{} if !reflect.DeepEqual(backup, expected) { t.Errorf("Backup.Get returned %+v, expected %+v", backup, expected) } } golang-github-vultr-govultr-2.17.2/bare_metal_server.go000066400000000000000000000330141425211421400232110ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const bmPath = "/v2/bare-metals" // BareMetalServerService is the interface to interact with the Bare Metal endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/baremetal type BareMetalServerService interface { Create(ctx context.Context, bmCreate *BareMetalCreate) (*BareMetalServer, error) Get(ctx context.Context, serverID string) (*BareMetalServer, error) Update(ctx context.Context, serverID string, bmReq *BareMetalUpdate) (*BareMetalServer, error) Delete(ctx context.Context, serverID string) error List(ctx context.Context, options *ListOptions) ([]BareMetalServer, *Meta, error) GetBandwidth(ctx context.Context, serverID string) (*Bandwidth, error) GetUserData(ctx context.Context, serverID string) (*UserData, error) GetVNCUrl(ctx context.Context, serverID string) (*VNCUrl, error) ListIPv4s(ctx context.Context, serverID string, options *ListOptions) ([]IPv4, *Meta, error) ListIPv6s(ctx context.Context, serverID string, options *ListOptions) ([]IPv6, *Meta, error) Halt(ctx context.Context, serverID string) error Reboot(ctx context.Context, serverID string) error Start(ctx context.Context, serverID string) error Reinstall(ctx context.Context, serverID string) (*BareMetalServer, error) MassStart(ctx context.Context, serverList []string) error MassHalt(ctx context.Context, serverList []string) error MassReboot(ctx context.Context, serverList []string) error GetUpgrades(ctx context.Context, serverID string) (*Upgrades, error) } // BareMetalServerServiceHandler handles interaction with the Bare Metal methods for the Vultr API type BareMetalServerServiceHandler struct { client *Client } // BareMetalServer represents a Bare Metal server on Vultr type BareMetalServer struct { ID string `json:"id"` Os string `json:"os"` RAM string `json:"ram"` Disk string `json:"disk"` MainIP string `json:"main_ip"` CPUCount int `json:"cpu_count"` Region string `json:"region"` DefaultPassword string `json:"default_password"` DateCreated string `json:"date_created"` Status string `json:"status"` NetmaskV4 string `json:"netmask_v4"` GatewayV4 string `json:"gateway_v4"` Plan string `json:"plan"` V6Network string `json:"v6_network"` V6MainIP string `json:"v6_main_ip"` V6NetworkSize int `json:"v6_network_size"` MacAddress int `json:"mac_address"` Label string `json:"label"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag string `json:"tag"` OsID int `json:"os_id"` AppID int `json:"app_id"` ImageID string `json:"image_id"` Features []string `json:"features"` Tags []string `json:"tags"` } // BareMetalCreate represents the optional parameters that can be set when creating a Bare Metal server type BareMetalCreate struct { Region string `json:"region,omitempty"` Plan string `json:"plan,omitempty"` OsID int `json:"os_id,omitempty"` StartupScriptID string `json:"script_id,omitempty"` SnapshotID string `json:"snapshot_id,omitempty"` EnableIPv6 *bool `json:"enable_ipv6,omitempty"` Label string `json:"label,omitempty"` SSHKeyIDs []string `json:"sshkey_id,omitempty"` AppID int `json:"app_id,omitempty"` ImageID string `json:"image_id,omitempty"` UserData string `json:"user_data,omitempty"` ActivationEmail *bool `json:"activation_email,omitempty"` Hostname string `json:"hostname,omitempty"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag string `json:"tag,omitempty"` ReservedIPv4 string `json:"reserved_ipv4,omitempty"` PersistentPxe *bool `json:"persistent_pxe,omitempty"` Tags []string `json:"tags"` } // BareMetalUpdate represents the optional parameters that can be set when updating a Bare Metal server type BareMetalUpdate struct { OsID int `json:"os_id,omitempty"` EnableIPv6 *bool `json:"enable_ipv6,omitempty"` Label string `json:"label,omitempty"` AppID int `json:"app_id,omitempty"` ImageID string `json:"image_id,omitempty"` UserData string `json:"user_data,omitempty"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag *string `json:"tag,omitempty"` Tags []string `json:"tags"` } // BareMetalServerBandwidth represents bandwidth information for a Bare Metal server type BareMetalServerBandwidth struct { IncomingBytes int `json:"incoming_bytes"` OutgoingBytes int `json:"outgoing_bytes"` } type bareMetalsBase struct { BareMetals []BareMetalServer `json:"bare_metals"` Meta *Meta `json:"meta"` } type bareMetalBase struct { BareMetal *BareMetalServer `json:"bare_metal"` } // BMBareMetalBase ... type BMBareMetalBase struct { BareMetalBandwidth map[string]BareMetalServerBandwidth `json:"bandwidth"` } type vncBase struct { VNCUrl *VNCUrl `json:"vnc"` } // VNCUrl contains the URL for a given Bare Metals VNC type VNCUrl struct { URL string `json:"url"` } // Create a new Bare Metal server. func (b *BareMetalServerServiceHandler) Create(ctx context.Context, bmCreate *BareMetalCreate) (*BareMetalServer, error) { req, err := b.client.NewRequest(ctx, http.MethodPost, bmPath, bmCreate) if err != nil { return nil, err } bm := new(bareMetalBase) if err = b.client.DoWithContext(ctx, req, bm); err != nil { return nil, err } return bm.BareMetal, nil } // Get information for a Bare Metal instance. func (b *BareMetalServerServiceHandler) Get(ctx context.Context, serverID string) (*BareMetalServer, error) { uri := fmt.Sprintf("%s/%s", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } bms := new(bareMetalBase) if err = b.client.DoWithContext(ctx, req, bms); err != nil { return nil, err } return bms.BareMetal, nil } // Update a Bare Metal server func (b *BareMetalServerServiceHandler) Update(ctx context.Context, serverID string, bmReq *BareMetalUpdate) (*BareMetalServer, error) { uri := fmt.Sprintf("%s/%s", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPatch, uri, bmReq) if err != nil { return nil, err } bms := new(bareMetalBase) if err = b.client.DoWithContext(ctx, req, bms); err != nil { return nil, err } return bms.BareMetal, nil } // Delete a Bare Metal server. func (b *BareMetalServerServiceHandler) Delete(ctx context.Context, serverID string) error { uri := fmt.Sprintf("%s/%s", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // List all Bare Metal instances in your account. func (b *BareMetalServerServiceHandler) List(ctx context.Context, options *ListOptions) ([]BareMetalServer, *Meta, error) { req, err := b.client.NewRequest(ctx, http.MethodGet, bmPath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() bms := new(bareMetalsBase) if err = b.client.DoWithContext(ctx, req, bms); err != nil { return nil, nil, err } return bms.BareMetals, bms.Meta, nil } // GetBandwidth used by a Bare Metal server. func (b *BareMetalServerServiceHandler) GetBandwidth(ctx context.Context, serverID string) (*Bandwidth, error) { uri := fmt.Sprintf("%s/%s/bandwidth", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } bms := new(Bandwidth) if err = b.client.DoWithContext(ctx, req, &bms); err != nil { return nil, err } return bms, nil } // GetUserData for a Bare Metal server. The userdata returned will be in base64 encoding. func (b *BareMetalServerServiceHandler) GetUserData(ctx context.Context, serverID string) (*UserData, error) { uri := fmt.Sprintf("%s/%s/user-data", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } userData := new(userDataBase) if err = b.client.DoWithContext(ctx, req, userData); err != nil { return nil, err } return userData.UserData, nil } // GetVNCUrl gets the vnc url for a given Bare Metal server. func (b *BareMetalServerServiceHandler) GetVNCUrl(ctx context.Context, serverID string) (*VNCUrl, error) { uri := fmt.Sprintf("%s/%s/vnc", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } vnc := new(vncBase) if err = b.client.DoWithContext(ctx, req, vnc); err != nil { return nil, err } return vnc.VNCUrl, nil } // ListIPv4s information of a Bare Metal server. // IP information is only available for Bare Metal servers in the "active" state. func (b *BareMetalServerServiceHandler) ListIPv4s(ctx context.Context, serverID string, options *ListOptions) ([]IPv4, *Meta, error) { uri := fmt.Sprintf("%s/%s/ipv4", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() ipv4 := new(ipBase) if err = b.client.DoWithContext(ctx, req, ipv4); err != nil { return nil, nil, err } return ipv4.IPv4s, ipv4.Meta, nil } // ListIPv6s information of a Bare Metal server. // IP information is only available for Bare Metal servers in the "active" state. // If the Bare Metal server does not have IPv6 enabled, then an empty array is returned. func (b *BareMetalServerServiceHandler) ListIPv6s(ctx context.Context, serverID string, options *ListOptions) ([]IPv6, *Meta, error) { uri := fmt.Sprintf("%s/%s/ipv6", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() ipv6 := new(ipBase) if err = b.client.DoWithContext(ctx, req, ipv6); err != nil { return nil, nil, err } return ipv6.IPv6s, ipv6.Meta, nil } // Halt a Bare Metal server. // This is a hard power off, meaning that the power to the machine is severed. // The data on the machine will not be modified, and you will still be billed for the machine. func (b *BareMetalServerServiceHandler) Halt(ctx context.Context, serverID string) error { uri := fmt.Sprintf("%s/%s/halt", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // Reboot a Bare Metal server. This is a hard reboot, which means that the server is powered off, then back on. func (b *BareMetalServerServiceHandler) Reboot(ctx context.Context, serverID string) error { uri := fmt.Sprintf("%s/%s/reboot", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // Start a Bare Metal server. func (b *BareMetalServerServiceHandler) Start(ctx context.Context, serverID string) error { uri := fmt.Sprintf("%s/%s/start", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // Reinstall the operating system on a Bare Metal server. // All data will be permanently lost, but the IP address will remain the same. func (b *BareMetalServerServiceHandler) Reinstall(ctx context.Context, serverID string) (*BareMetalServer, error) { uri := fmt.Sprintf("%s/%s/reinstall", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return nil, err } bms := new(bareMetalBase) if err = b.client.DoWithContext(ctx, req, bms); err != nil { return nil, err } return bms.BareMetal, nil } // MassStart will start a list of Bare Metal servers the machine is already running, it will be restarted. func (b *BareMetalServerServiceHandler) MassStart(ctx context.Context, serverList []string) error { uri := fmt.Sprintf("%s/start", bmPath) reqBody := RequestBody{"baremetal_ids": serverList} req, err := b.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // MassHalt a list of Bare Metal servers. func (b *BareMetalServerServiceHandler) MassHalt(ctx context.Context, serverList []string) error { uri := fmt.Sprintf("%s/halt", bmPath) reqBody := RequestBody{"baremetal_ids": serverList} req, err := b.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // MassReboot a list of Bare Metal servers. func (b *BareMetalServerServiceHandler) MassReboot(ctx context.Context, serverList []string) error { uri := fmt.Sprintf("%s/reboot", bmPath) reqBody := RequestBody{"baremetal_ids": serverList} req, err := b.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // GetUpgrades that are available for a Bare Metal server. func (b *BareMetalServerServiceHandler) GetUpgrades(ctx context.Context, serverID string) (*Upgrades, error) { uri := fmt.Sprintf("%s/%s/upgrades", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } upgrades := new(upgradeBase) if err = b.client.DoWithContext(ctx, req, upgrades); err != nil { return nil, err } return upgrades.Upgrades, nil } golang-github-vultr-govultr-2.17.2/bare_metal_server_test.go000066400000000000000000000477161425211421400242660ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestBareMetalServerServiceHandler_GetServer(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/abc123", func(writer http.ResponseWriter, request *http.Request) { response := ` { "bare_metal": { "id": "abc123", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "region": "ewr", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "plan": "vbm-4c-32gb", "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64, "mac_address": 2199756823533, "label": "my label", "tags": ["my tag"], "os_id": 127, "app_id": 0 } } ` fmt.Fprint(writer, response) }) bm, err := client.BareMetalServer.Get(ctx, "abc123") if err != nil { t.Errorf("BareMetalServer.GetServer returned error: %v", err) } expected := &BareMetalServer{ ID: "abc123", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUCount: 1, Region: "ewr", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", Plan: "vbm-4c-32gb", V6Network: "2001:DB8:9000::", V6MainIP: "2001:DB8:9000::100", V6NetworkSize: 64, MacAddress: 2199756823533, Label: "my label", Tags: []string{"my tag"}, OsID: 127, AppID: 0, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.GetServer returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals", func(writer http.ResponseWriter, request *http.Request) { response := ` { "bare_metal": { "id": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "region": "ewr", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "plan": "vbm-4c-32gb", "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64, "mac_address": 0, "label": "go-bm-test", "tags": ["my tag"], "os_id": 127, "app_id": 0 } } ` fmt.Fprint(writer, response) }) options := &BareMetalCreate{ StartupScriptID: "1", Region: "ewr", Plan: "vbm-4c-32gb", SnapshotID: "1", EnableIPv6: BoolToBoolPtr(true), Label: "go-bm-test", SSHKeyIDs: []string{"6b80207b1821f"}, AppID: 1, UserData: "echo Hello World", ActivationEmail: BoolToBoolPtr(true), Hostname: "test", Tags: []string{"my tag"}, ReservedIPv4: "111.111.111.111", PersistentPxe: BoolToBoolPtr(true), } bm, err := client.BareMetalServer.Create(ctx, options) if err != nil { t.Errorf("BareMetalServer.Create returned error: %v", err) } expected := &BareMetalServer{ ID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUCount: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", Plan: "vbm-4c-32gb", V6Network: "2001:DB8:9000::", V6MainIP: "2001:DB8:9000::100", V6NetworkSize: 64, Label: "go-bm-test", Tags: []string{"my tag"}, MacAddress: 0, OsID: 127, Region: "ewr", AppID: 0, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.Create returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/dev-preview-abc123", func(writer http.ResponseWriter, request *http.Request) { response := ` { "bare_metal": { "id": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "region": "ewr", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "plan": "vbm-4c-32gb", "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64, "label": "my new label", "tags": ["my tag"], "os_id": 127, "app_id": 0 } } ` fmt.Fprint(writer, response) }) options := &BareMetalUpdate{ Label: "my new label", } bm, err := client.BareMetalServer.Update(ctx, "dev-preview-abc123", options) if err != nil { t.Errorf("BareMetal.Update returned %+v, expected %+v", err, nil) } expected := &BareMetalServer{ ID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUCount: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", Plan: "vbm-4c-32gb", V6Network: "2001:DB8:9000::", V6MainIP: "2001:DB8:9000::100", V6NetworkSize: 64, Label: "my new label", Tags: []string{"my tag"}, OsID: 127, Region: "ewr", AppID: 0, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.Update returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/900000", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.Delete(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.Delete returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals", func(writer http.ResponseWriter, request *http.Request) { response := ` { "bare_metals": [{ "id": "90000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "region": "ewr", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "plan": "vbm-4c-32gb", "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64, "mac_address": 2199756823533, "label": "my label", "tags": ["my tag"], "os_id": 127, "app_id": 0 }] } ` fmt.Fprint(writer, response) }) bm, _, err := client.BareMetalServer.List(ctx, nil) if err != nil { t.Errorf("BareMetalServer.List returned error: %v", err) } expected := []BareMetalServer{ { ID: "90000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUCount: 1, Region: "ewr", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", Plan: "vbm-4c-32gb", V6Network: "2001:DB8:9000::", V6MainIP: "2001:DB8:9000::100", V6NetworkSize: 64, MacAddress: 2199756823533, Label: "my label", Tags: []string{"my tag"}, OsID: 127, AppID: 0, }, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.List returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_GetBandwidth(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/dev-preview-abc123/bandwidth", func(writer http.ResponseWriter, request *http.Request) { response := ` { "bandwidth": { "2017-04-01": { "incoming_bytes": 91571055, "outgoing_bytes": 3084731 } } } ` fmt.Fprint(writer, response) }) bandwidth, err := client.BareMetalServer.GetBandwidth(ctx, "dev-preview-abc123") if err != nil { t.Errorf("BareMetalServer.GetBandwidth returned %+v", err) } expected := &Bandwidth{ Bandwidth: map[string]struct { IncomingBytes int `json:"incoming_bytes"` OutgoingBytes int `json:"outgoing_bytes"` }{ "2017-04-01": { IncomingBytes: 91571055, OutgoingBytes: 3084731, }, }, } if !reflect.DeepEqual(bandwidth, expected) { t.Errorf("BareMetalServer.GetBandwidth returned %+v, expected %+v", bandwidth, expected) } } func TestBareMetalServerServiceHandler_Halt(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/900000/halt", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.Halt(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.Halt returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_ListIPv4s(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/900000/ipv4", func(writer http.ResponseWriter, request *http.Request) { response := ` { "ipv4s": [ { "ip": "203.0.113.10", "netmask": "255.255.255.0", "gateway": "203.0.113.1", "type": "main_ip", "reverse": "203.0.113.10.vultr.com" } ], "meta": { "total": 1, "links": { "next": "", "prev": "" } } } ` fmt.Fprint(writer, response) }) ipv4, _, err := client.BareMetalServer.ListIPv4s(ctx, "900000", nil) if err != nil { t.Errorf("BareMetalServer.ListIPv4s returned %+v", err) } expected := []IPv4{ { IP: "203.0.113.10", Netmask: "255.255.255.0", Gateway: "203.0.113.1", Type: "main_ip", Reverse: "203.0.113.10.vultr.com", }, } if !reflect.DeepEqual(ipv4, expected) { t.Errorf("BareMetalServer.ListIPv4s returned %+v, expected %+v", ipv4, expected) } } func TestBareMetalServerServiceHandler_ListIPv6s(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/900000/ipv6", func(writer http.ResponseWriter, request *http.Request) { response := ` { "ipv6s": [ { "ip": "2001:DB8:9000::100", "network": "2001:DB8:9000::", "network_size": 64, "type": "main_ip" } ] } ` fmt.Fprint(writer, response) }) ipv6, _, err := client.BareMetalServer.ListIPv6s(ctx, "900000", nil) if err != nil { t.Errorf("BareMetalServer.IPV6Info returned %+v", err) } expected := []IPv6{ { IP: "2001:DB8:9000::100", Network: "2001:DB8:9000::", NetworkSize: 64, Type: "main_ip", }, } if !reflect.DeepEqual(ipv6, expected) { t.Errorf("BareMetalServer.ListIPv6s returned %+v, expected %+v", ipv6, expected) } } func TestBareMetalServerServiceHandler_Reboot(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/900000/reboot", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.Reboot(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.Reboot returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_Reinstall(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/900000/reinstall", func(writer http.ResponseWriter, request *http.Request) { response := ` { "bare_metal": { "id": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "region": "ewr", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "plan": "vbm-4c-32gb", "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64, "label": "go-bm-test", "tags": ["my tag"], "os_id": 127, "app_id": 0 } } ` fmt.Fprint(writer, response) }) bm, err := client.BareMetalServer.Reinstall(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.Reinstall returned %+v, expected %+v", err, nil) } expected := &BareMetalServer{ ID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUCount: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", Plan: "vbm-4c-32gb", V6Network: "2001:DB8:9000::", V6MainIP: "2001:DB8:9000::100", V6NetworkSize: 64, Label: "go-bm-test", Tags: []string{"my tag"}, OsID: 127, Region: "ewr", AppID: 0, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.Reinstall returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_Start(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/42018b7b-a4e3-4c7e-be74-663afeb142aa/start", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.Start(ctx, "42018b7b-a4e3-4c7e-be74-663afeb142aa") if err != nil { t.Errorf("BareMetalServer.Start returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_GetUserData(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/dev-preview-abc123/user-data", func(writer http.ResponseWriter, request *http.Request) { response := `{"user_data": {"data" : "ZWNobyBIZWxsbyBXb3JsZA=="}}` fmt.Fprint(writer, response) }) userData, err := client.BareMetalServer.GetUserData(ctx, "dev-preview-abc123") if err != nil { t.Errorf("BareMetalServer.GetUserData return %+v ", err) } expected := &UserData{Data: "ZWNobyBIZWxsbyBXb3JsZA=="} if !reflect.DeepEqual(userData, expected) { t.Errorf("BareMetalServer.GetUserData returned %+v, expected %+v", userData, expected) } } func TestBareMetalServerServiceHandler_GetUpgrades(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/dev-preview-abc123/upgrades", func(writer http.ResponseWriter, request *http.Request) { response := `{ "upgrades":{ "os":[ { "id":127, "name":"CentOS 6 x64", "arch":"x64", "family":"centos" } ], "applications":[ { "id":1, "name":"LEMP", "short_name":"lemp", "deploy_name":"LEMP on CentOS 6" } ], "plans":[ "vc2-2c-4gb" ] } }` fmt.Fprint(writer, response) }) server, err := client.BareMetalServer.GetUpgrades(ctx, "dev-preview-abc123") if err != nil { t.Errorf("BareMetalServer.GetUpgrades returned %+v", err) } expected := &Upgrades{ Applications: []Application{ { ID: 1, Name: "LEMP", ShortName: "lemp", DeployName: "LEMP on CentOS 6", }, }, OS: []OS{ { ID: 127, Name: "CentOS 6 x64", Arch: "x64", Family: "centos", }, }, Plans: []string{ "vc2-2c-4gb", }, } if !reflect.DeepEqual(server, expected) { t.Errorf("BareMetalServer.GetUpgrades returned %+v, expected %+v", server, expected) } } func TestBareMetalServerServiceHandler_GetVNCUrl(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/dev-preview-abc123/vnc", func(writer http.ResponseWriter, request *http.Request) { response := `{"vnc": {"url" : "https://my.vultr.com/subs/baremetal/novnc/api.php?data=djJ8U3ZUTjBYaE3HaCMy1yZ0paVUh8wOldmbw"}}` fmt.Fprint(writer, response) }) vnc, err := client.BareMetalServer.GetVNCUrl(ctx, "dev-preview-abc123") if err != nil { t.Errorf("BareMetalServer.GetVNCUrl return %+v ", err) } expected := &VNCUrl{URL: "https://my.vultr.com/subs/baremetal/novnc/api.php?data=djJ8U3ZUTjBYaE3HaCMy1yZ0paVUh8wOldmbw"} if !reflect.DeepEqual(vnc, expected) { t.Errorf("BareMetalServer.GetVNCUrl returned %+v, expected %+v", vnc, expected) } } func TestBareMetalServerServiceHandler_MassStart(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/start", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.MassStart(ctx, []string{"42018b7b-a4e3-4c7e-be74-663afeb142aa"}) if err != nil { t.Errorf("BareMetalServer.MassStart returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_MassReboot(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/reboot", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.MassReboot(ctx, []string{"42018b7b-a4e3-4c7e-be74-663afeb142aa"}) if err != nil { t.Errorf("BareMetalServer.Reboot returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_MassHalt(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals/halt", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.MassHalt(ctx, []string{"42018b7b-a4e3-4c7e-be74-663afeb142aa"}) if err != nil { t.Errorf("BareMetalServer.MassHalf returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_CreateMarketplaceImage(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/bare-metals", func(writer http.ResponseWriter, request *http.Request) { response := ` { "bare_metal": { "id": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "region": "ewr", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "plan": "vbm-4c-32gb", "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64, "mac_address": 0, "label": "go-bm-test", "tags": ["my tag"], "os_id": 127, "app_id": 0, "image_id": "test" } } ` fmt.Fprint(writer, response) }) options := &BareMetalCreate{ StartupScriptID: "1", Region: "ewr", Plan: "vbm-4c-32gb", SnapshotID: "1", EnableIPv6: BoolToBoolPtr(true), Label: "go-bm-test", SSHKeyIDs: []string{"6b80207b1821f"}, AppID: 1, UserData: "echo Hello World", ActivationEmail: BoolToBoolPtr(true), Hostname: "test", Tags: []string{"go-test"}, ReservedIPv4: "111.111.111.111", PersistentPxe: BoolToBoolPtr(true), ImageID: "test", } bm, err := client.BareMetalServer.Create(ctx, options) if err != nil { t.Errorf("BareMetalServer.Create returned error: %v", err) } expected := &BareMetalServer{ ID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUCount: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", Plan: "vbm-4c-32gb", V6Network: "2001:DB8:9000::", V6MainIP: "2001:DB8:9000::100", V6NetworkSize: 64, Label: "go-bm-test", Tags: []string{"my tag"}, MacAddress: 0, OsID: 127, Region: "ewr", AppID: 0, ImageID: "test", } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.Create returned %+v, expected %+v", bm, expected) } } golang-github-vultr-govultr-2.17.2/billing.go000066400000000000000000000107521425211421400211540ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // BillingService is the interface to interact with the billing endpoint on the Vultr API // Link : https://www.vultr.com/api/#tag/billing type BillingService interface { ListHistory(ctx context.Context, options *ListOptions) ([]History, *Meta, error) ListInvoices(ctx context.Context, options *ListOptions) ([]Invoice, *Meta, error) GetInvoice(ctx context.Context, invoiceID string) (*Invoice, error) ListInvoiceItems(ctx context.Context, invoiceID int, options *ListOptions) ([]InvoiceItem, *Meta, error) } // BillingServiceHandler handles interaction with the billing methods for the Vultr API type BillingServiceHandler struct { client *Client } // History represents a billing history item on an account type History struct { ID int `json:"id"` Date string `json:"date"` Type string `json:"type"` Description string `json:"description"` Amount float32 `json:"amount"` Balance float32 `json:"balance"` } // Invoice represents an invoice on an account type Invoice struct { ID int `json:"id"` Date string `json:"date"` Description string `json:"description"` Amount float32 `json:"amount"` Balance float32 `json:"balance"` } // InvoiceItem represents an item on an accounts invoice type InvoiceItem struct { Description string `json:"description"` Product string `json:"product"` StartDate string `json:"start_date"` EndDate string `json:"end_date"` Units int `json:"units"` UnitType string `json:"unit_type"` UnitPrice float32 `json:"unit_price"` Total float32 `json:"total"` } type billingHistoryBase struct { History []History `json:"billing_history"` Meta *Meta `json:"meta"` } type invoicesBase struct { Invoice []Invoice `json:"billing_invoices"` Meta *Meta `json:"meta"` } type invoiceBase struct { Invoice *Invoice `json:"billing_invoice"` } type invoiceItemsBase struct { InvoiceItems []InvoiceItem `json:"invoice_items"` Meta *Meta `json:"meta"` } // ListHistory retrieves a list of all billing history on the current account func (b *BillingServiceHandler) ListHistory(ctx context.Context, options *ListOptions) ([]History, *Meta, error) { uri := "/v2/billing/history" req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() invoices := new(billingHistoryBase) if err = b.client.DoWithContext(ctx, req, invoices); err != nil { return nil, nil, err } return invoices.History, invoices.Meta, nil } // ListInvoices retrieves a list of all billing invoices on the current account func (b *BillingServiceHandler) ListInvoices(ctx context.Context, options *ListOptions) ([]Invoice, *Meta, error) { uri := "/v2/billing/invoices" req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() invoices := new(invoicesBase) if err = b.client.DoWithContext(ctx, req, invoices); err != nil { return nil, nil, err } return invoices.Invoice, invoices.Meta, nil } // GetInvoice retrieves an invoice that matches the given invoiceID func (b *BillingServiceHandler) GetInvoice(ctx context.Context, invoiceID string) (*Invoice, error) { uri := fmt.Sprintf("/v2/billing/invoices/%s", invoiceID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } invoice := new(invoiceBase) if err := b.client.DoWithContext(ctx, req, invoice); err != nil { return nil, err } return invoice.Invoice, nil } // ListInvoiceItems retrieves items in an invoice that matches the given invoiceID func (b *BillingServiceHandler) ListInvoiceItems(ctx context.Context, invoiceID int, options *ListOptions) ([]InvoiceItem, *Meta, error) { uri := fmt.Sprintf("/v2/billing/invoices/%d/items", invoiceID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() invoice := new(invoiceItemsBase) if err := b.client.DoWithContext(ctx, req, invoice); err != nil { return nil, nil, err } return invoice.InvoiceItems, invoice.Meta, nil } golang-github-vultr-govultr-2.17.2/billing_test.go000066400000000000000000000145131425211421400222120ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestBillingServiceHandler_ListHistory(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/billing/history", func(w http.ResponseWriter, r *http.Request) { response := ` { "billing_history": [ { "id": 5317720, "date": "2018-04-01T00:30:05+00:00", "type": "invoice", "description": "Invoice #5317720", "amount": 2.35, "balance": -497.65 } ], "meta": { "total":1, "links": { "next":"", "prev":"" } } } ` fmt.Fprint(w, response) }) history, meta, err := client.Billing.ListHistory(ctx, nil) if err != nil { t.Errorf("Billing.ListHistory returned error: %v", err) } expected := []History{ { ID: 5317720, Date: "2018-04-01T00:30:05+00:00", Type: "invoice", Description: "Invoice #5317720", Amount: 2.35, Balance: -497.65, }, } expectedMeta := &Meta{ Total: 1, Links: &Links{}, } if !reflect.DeepEqual(history, expected) { t.Errorf("Billing.ListHistory returned %+v, expected %+v", history, expected) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Billing.ListHistory returned %+v, expected %+v", meta, expectedMeta) } } func TestBillingServiceHandler_ListInvoices(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/billing/invoices", func(w http.ResponseWriter, r *http.Request) { response := ` { "billing_invoices": [ { "id": 5317720, "date": "2018-04-01T00:30:05+00:00", "description": "Invoice #5317720", "amount": 2.35, "balance": -497.65 } ], "meta": { "total":1, "links": { "next":"", "prev":"" } } } ` fmt.Fprint(w, response) }) invoices, meta, err := client.Billing.ListInvoices(ctx, nil) if err != nil { t.Errorf("Billing.ListInvoices returned error: %v", err) } expected := []Invoice{ { ID: 5317720, Date: "2018-04-01T00:30:05+00:00", Description: "Invoice #5317720", Amount: 2.35, Balance: -497.65, }, } expectedMeta := &Meta{ Total: 1, Links: &Links{}, } if !reflect.DeepEqual(invoices, expected) { t.Errorf("Billing.ListInvoices returned %+v, expected %+v", invoices, expected) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Billing.ListInvoices returned %+v, expected %+v", meta, expectedMeta) } } func TestBillingServiceHandler_ListHistoryEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/billing/history", func(w http.ResponseWriter, r *http.Request) { response := ` { "billing_history": [], "meta": { "total":0, "links": { "next":"", "prev":"" } } } ` fmt.Fprint(w, response) }) history, meta, err := client.Billing.ListHistory(ctx, nil) if err != nil { t.Errorf("Billing.ListHistory returned error: %v", err) } expected := []History{} if !reflect.DeepEqual(history, expected) { t.Errorf("Billing.ListHistory returned %+v, expected %+v", history, expected) } expectedMeta := &Meta{ Total: 0, Links: &Links{ Next: "", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Billing.ListHistory meta returned %+v, expected %+v", meta, expectedMeta) } } func TestBillingServiceHandler_ListInvoicesEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/billing/invoices", func(w http.ResponseWriter, r *http.Request) { response := ` { "billing_invoices": [], "meta": { "total":0, "links": { "next":"", "prev":"" } } } ` fmt.Fprint(w, response) }) invoices, meta, err := client.Billing.ListInvoices(ctx, nil) if err != nil { t.Errorf("Billing.ListInvoices returned error: %v", err) } expected := []Invoice{} if !reflect.DeepEqual(invoices, expected) { t.Errorf("Billing.ListInvoices returned %+v, expected %+v", invoices, expected) } expectedMeta := &Meta{ Total: 0, Links: &Links{ Next: "", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Billing.ListInvoices meta returned %+v, expected %+v", meta, expectedMeta) } } func TestBillingServiceHandler_GetInvoice(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/billing/invoices/123456", func(w http.ResponseWriter, r *http.Request) { response := ` { "billing_invoice": { "id": 123456, "date": "2018-04-01T00:30:05+00:00", "description": "Invoice #5317782", "amount": 2.35, "balance": -497.65 } } ` fmt.Fprint(w, response) }) invoice, err := client.Billing.GetInvoice(ctx, "123456") if err != nil { t.Errorf("Billing.GetInvoice returned error: %v", err) } expected := &Invoice{ ID: 123456, Date: "2018-04-01T00:30:05+00:00", Description: "Invoice #5317782", Amount: 2.35, Balance: -497.65, } if !reflect.DeepEqual(invoice, expected) { t.Errorf("Billing.GetInvoice returned %+v, expected %+v", invoice, expected) } } func TestBillingServiceHandler_ListInvoiceItems(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/billing/invoices/123456/items", func(w http.ResponseWriter, r *http.Request) { response := ` { "invoice_items": [ { "description": "1.1.1.1 (1024 MB)", "product": "Vultr Cloud Compute", "start_date": "2018-03-18T21:57:58+00:00", "end_date": "2018-04-01T00:00:00+00:00", "units": 315, "unit_type": "hours", "unit_price": 0.0074, "total": 2.35 } ], "meta": { "total":1, "links": { "next":"", "prev":"" } } } ` fmt.Fprint(w, response) }) invoices, meta, err := client.Billing.ListInvoiceItems(ctx, 123456, nil) if err != nil { t.Errorf("Billing.ListInvoiceItems returned error: %v", err) } expected := []InvoiceItem{ { Description: "1.1.1.1 (1024 MB)", Product: "Vultr Cloud Compute", StartDate: "2018-03-18T21:57:58+00:00", EndDate: "2018-04-01T00:00:00+00:00", Units: 315, UnitType: "hours", UnitPrice: 0.0074, Total: 2.35, }, } expectedMeta := &Meta{ Total: 1, Links: &Links{}, } if !reflect.DeepEqual(invoices, expected) { t.Errorf("Billing.ListInvoiceItems returned %+v, expected %+v", invoices, expected) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Billing.ListInvoiceItems returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/block_storage.go000066400000000000000000000131461425211421400223520ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // BlockStorageService is the interface to interact with Block-Storage endpoint on the Vultr API // Link : https://www.vultr.com/api/#tag/block type BlockStorageService interface { Create(ctx context.Context, blockReq *BlockStorageCreate) (*BlockStorage, error) Get(ctx context.Context, blockID string) (*BlockStorage, error) Update(ctx context.Context, blockID string, blockReq *BlockStorageUpdate) error Delete(ctx context.Context, blockID string) error List(ctx context.Context, options *ListOptions) ([]BlockStorage, *Meta, error) Attach(ctx context.Context, blockID string, attach *BlockStorageAttach) error Detach(ctx context.Context, blockID string, detach *BlockStorageDetach) error } // BlockStorageServiceHandler handles interaction with the block-storage methods for the Vultr API type BlockStorageServiceHandler struct { client *Client } // BlockStorage represents Vultr Block-Storage type BlockStorage struct { ID string `json:"id"` Cost float32 `json:"cost"` Status string `json:"status"` SizeGB int `json:"size_gb"` Region string `json:"region"` DateCreated string `json:"date_created"` AttachedToInstance string `json:"attached_to_instance"` Label string `json:"label"` MountID string `json:"mount_id"` BlockType string `json:"block_type"` } // BlockStorageCreate struct is used for creating Block Storage. type BlockStorageCreate struct { Region string `json:"region"` SizeGB int `json:"size_gb"` Label string `json:"label,omitempty"` BlockType string `json:"block_type,omitempty"` } // BlockStorageUpdate struct is used to update Block Storage. type BlockStorageUpdate struct { SizeGB int `json:"size_gb,omitempty"` Label string `json:"label,omitempty"` } // BlockStorageAttach struct used to define if a attach should be restart the instance. type BlockStorageAttach struct { InstanceID string `json:"instance_id"` Live *bool `json:"live,omitempty"` } // BlockStorageDetach struct used to define if a detach should be restart the instance. type BlockStorageDetach struct { Live *bool `json:"live,omitempty"` } type blockStoragesBase struct { Blocks []BlockStorage `json:"blocks"` Meta *Meta `json:"meta"` } type blockStorageBase struct { Block *BlockStorage `json:"block"` } // Create builds out a block storage func (b *BlockStorageServiceHandler) Create(ctx context.Context, blockReq *BlockStorageCreate) (*BlockStorage, error) { uri := "/v2/blocks" req, err := b.client.NewRequest(ctx, http.MethodPost, uri, blockReq) if err != nil { return nil, err } block := new(blockStorageBase) if err = b.client.DoWithContext(ctx, req, block); err != nil { return nil, err } return block.Block, nil } // Get returns a single block storage instance based ony our blockID you provide from your Vultr Account func (b *BlockStorageServiceHandler) Get(ctx context.Context, blockID string) (*BlockStorage, error) { uri := fmt.Sprintf("/v2/blocks/%s", blockID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } block := new(blockStorageBase) if err = b.client.DoWithContext(ctx, req, block); err != nil { return nil, err } return block.Block, nil } // Update a block storage subscription. func (b *BlockStorageServiceHandler) Update(ctx context.Context, blockID string, blockReq *BlockStorageUpdate) error { uri := fmt.Sprintf("/v2/blocks/%s", blockID) req, err := b.client.NewRequest(ctx, http.MethodPatch, uri, blockReq) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // Delete a block storage subscription from your Vultr account func (b *BlockStorageServiceHandler) Delete(ctx context.Context, blockID string) error { uri := fmt.Sprintf("/v2/blocks/%s", blockID) req, err := b.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // List returns a list of all block storage instances on your Vultr Account func (b *BlockStorageServiceHandler) List(ctx context.Context, options *ListOptions) ([]BlockStorage, *Meta, error) { uri := "/v2/blocks" req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() blocks := new(blockStoragesBase) if err = b.client.DoWithContext(ctx, req, blocks); err != nil { return nil, nil, err } return blocks.Blocks, blocks.Meta, nil } // Attach will link a given block storage to a given Vultr instance // If Live is set to true the block storage will be attached without reloading the instance func (b *BlockStorageServiceHandler) Attach(ctx context.Context, blockID string, attach *BlockStorageAttach) error { uri := fmt.Sprintf("/v2/blocks/%s/attach", blockID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, attach) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // Detach will de-link a given block storage to the Vultr instance it is attached to // If Live is set to true the block storage will be detached without reloading the instance func (b *BlockStorageServiceHandler) Detach(ctx context.Context, blockID string, detach *BlockStorageDetach) error { uri := fmt.Sprintf("/v2/blocks/%s/detach", blockID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, detach) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } golang-github-vultr-govultr-2.17.2/block_storage_test.go000066400000000000000000000123021425211421400234020ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestBlockStorageServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/blocks", func(writer http.ResponseWriter, request *http.Request) { response := `{"block":{"id":"123456","cost":10,"status":"active","size_gb":100,"region":"ewr","attached_to_instance":"","date_created":"01-01-1960","label":"mylabel", "mount_id": "ewr-123abc", "block_type": "test"}}` fmt.Fprint(writer, response) }) blockReq := &BlockStorageCreate{ Region: "ewr", SizeGB: 100, Label: "mylabel", BlockType: "test", } blockStorage, err := client.BlockStorage.Create(ctx, blockReq) if err != nil { t.Errorf("BlockStorage.Create returned error: %v", err) } expected := &BlockStorage{ ID: "123456", Cost: 10, Status: "active", SizeGB: 100, Region: "ewr", DateCreated: "01-01-1960", AttachedToInstance: "", Label: "mylabel", MountID: "ewr-123abc", BlockType: "test", } if !reflect.DeepEqual(blockStorage, expected) { t.Errorf("BlockStorage.Create returned %+v, expected %+v", blockStorage, expected) } } func TestBlockStorageServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/blocks/123456", func(writer http.ResponseWriter, request *http.Request) { response := `{"block":{"id":"123456","cost":10,"status":"active","size_gb":100,"region":"ewr","attached_to_instance":"","date_created":"01-01-1960","label":"mylabel", "mount_id": "123abc", "block_type": "test"}}` fmt.Fprint(writer, response) }) blockStorage, err := client.BlockStorage.Get(ctx, "123456") if err != nil { t.Errorf("BlockStorage.Create returned error: %v", err) } expected := &BlockStorage{ ID: "123456", Cost: 10, Status: "active", SizeGB: 100, Region: "ewr", DateCreated: "01-01-1960", AttachedToInstance: "", Label: "mylabel", MountID: "123abc", BlockType: "test", } if !reflect.DeepEqual(blockStorage, expected) { t.Errorf("BlockStorage.Create returned %+v, expected %+v", blockStorage, expected) } } func TestBlockStorageServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/blocks/123456", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) blockUpdate := &BlockStorageUpdate{ Label: "unit-test-label-setter", } err := client.BlockStorage.Update(ctx, "123456", blockUpdate) if err != nil { t.Errorf("BlockStorage.SetLabel returned %+v, expected %+v", err, nil) } } func TestBlockStorageServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/blocks/123456", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BlockStorage.Delete(ctx, "123456") if err != nil { t.Errorf("BlockStorage.Delete returned %+v, expected %+v", err, nil) } } func TestBlockStorageServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/blocks", func(writer http.ResponseWriter, request *http.Request) { response := `{"blocks":[{"id":"123456","cost":10,"status":"active","size_gb":100,"region":"ewr","attached_to_instance":"","date_created":"01-01-1960","label":"mylabel", "mount_id": "123abc", "block_type": "test"}],"meta":{"total":1,"links":{"next":"thisismycusror","prev":""}}}` fmt.Fprint(writer, response) }) blockStorage, meta, err := client.BlockStorage.List(ctx, nil) if err != nil { t.Errorf("BlockStorage.Create returned error: %v", err) } expected := []BlockStorage{ { ID: "123456", Cost: 10, Status: "active", SizeGB: 100, Region: "ewr", DateCreated: "01-01-1960", AttachedToInstance: "", Label: "mylabel", MountID: "123abc", BlockType: "test", }, } if !reflect.DeepEqual(blockStorage, expected) { t.Errorf("BlockStorage.Create returned %+v, expected %+v", blockStorage, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("User.List meta returned %+v, expected %+v", meta, expectedMeta) } } func TestBlockStorageServiceHandler_Attach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/blocks/12345/attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) attach := &BlockStorageAttach{ InstanceID: "1234", Live: BoolToBoolPtr(true), } err := client.BlockStorage.Attach(ctx, "12345", attach) if err != nil { t.Errorf("BlockStorage.Attach returned %+v, expected %+v", err, nil) } } func TestBlockStorageServiceHandler_Detach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/blocks/123456/detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) detach := &BlockStorageDetach{Live: BoolToBoolPtr(true)} err := client.BlockStorage.Detach(ctx, "123456", detach) if err != nil { t.Errorf("BlockStorage.Detach returned %+v, expected %+v", err, nil) } } golang-github-vultr-govultr-2.17.2/domain_records.go000066400000000000000000000077531425211421400225330ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // DomainRecordService is the interface to interact with the DNS Records endpoints on the Vultr API // Link: https://www.vultr.com/api/#tag/dns type DomainRecordService interface { Create(ctx context.Context, domain string, domainRecordReq *DomainRecordReq) (*DomainRecord, error) Get(ctx context.Context, domain, recordID string) (*DomainRecord, error) Update(ctx context.Context, domain, recordID string, domainRecordReq *DomainRecordReq) error Delete(ctx context.Context, domain, recordID string) error List(ctx context.Context, domain string, options *ListOptions) ([]DomainRecord, *Meta, error) } // DomainRecordsServiceHandler handles interaction with the DNS Records methods for the Vultr API type DomainRecordsServiceHandler struct { client *Client } // DomainRecord represents a DNS record on Vultr type DomainRecord struct { ID string `json:"id,omitempty"` Type string `json:"type,omitempty"` Name string `json:"name,omitempty"` Data string `json:"data,omitempty"` Priority int `json:"priority,omitempty"` TTL int `json:"ttl,omitempty"` } // DomainRecordReq struct to use for create/update domain record calls. type DomainRecordReq struct { Name string `json:"name"` Type string `json:"type,omitempty"` Data string `json:"data,omitempty"` TTL int `json:"ttl,omitempty"` Priority *int `json:"priority,omitempty"` } type domainRecordsBase struct { Records []DomainRecord `json:"records,omitempty"` Meta *Meta `json:"meta,omitempty"` } type domainRecordBase struct { Record *DomainRecord `json:"record,omitempty"` } // Create will add a DNS record. func (d *DomainRecordsServiceHandler) Create(ctx context.Context, domain string, domainRecordReq *DomainRecordReq) (*DomainRecord, error) { req, err := d.client.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/%s/records", domainPath, domain), domainRecordReq) if err != nil { return nil, err } record := new(domainRecordBase) if err = d.client.DoWithContext(ctx, req, record); err != nil { return nil, err } return record.Record, nil } // Get record from a domain func (d *DomainRecordsServiceHandler) Get(ctx context.Context, domain, recordID string) (*DomainRecord, error) { req, err := d.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/records/%s", domainPath, domain, recordID), nil) if err != nil { return nil, err } record := new(domainRecordBase) if err = d.client.DoWithContext(ctx, req, record); err != nil { return nil, err } return record.Record, nil } // Update will update a Domain record func (d *DomainRecordsServiceHandler) Update(ctx context.Context, domain, recordID string, domainRecordReq *DomainRecordReq) error { req, err := d.client.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s/%s/records/%s", domainPath, domain, recordID), domainRecordReq) if err != nil { return err } return d.client.DoWithContext(ctx, req, nil) } // Delete will delete a domain name and all associated records. func (d *DomainRecordsServiceHandler) Delete(ctx context.Context, domain, recordID string) error { req, err := d.client.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/%s/records/%s", domainPath, domain, recordID), nil) if err != nil { return err } return d.client.DoWithContext(ctx, req, nil) } // List will list all the records associated with a particular domain on Vultr. func (d *DomainRecordsServiceHandler) List(ctx context.Context, domain string, options *ListOptions) ([]DomainRecord, *Meta, error) { req, err := d.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/records", domainPath, domain), nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() records := new(domainRecordsBase) if err = d.client.DoWithContext(ctx, req, records); err != nil { return nil, nil, err } return records.Records, records.Meta, nil } golang-github-vultr-govultr-2.17.2/domain_records_test.go000066400000000000000000000074211425211421400235620ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestDomainRecordsServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com/records", func(writer http.ResponseWriter, request *http.Request) { response := `{"record":{"id":"dev-preview-abc123","type":"A","name":"www","data":"127.0.0.1","priority":0,"ttl":300}}` fmt.Fprint(writer, response) }) p := 300 r := &DomainRecordReq{ Name: "www", Type: "A", Data: "127.0.0.1", Priority: &p, } record, err := client.DomainRecord.Create(ctx, "vultr.com", r) if err != nil { t.Errorf("DomainRecord.Create returned %+v, expected %+v", err, nil) } expected := &DomainRecord{ ID: "dev-preview-abc123", Type: "A", Name: "www", Data: "127.0.0.1", Priority: 0, TTL: 300, } if !reflect.DeepEqual(record, expected) { t.Errorf("DomainRecord.Create returned %+v, expected %+v", record, expected) } } func TestDomainRecordsServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com/records/dev-preview-abc123", func(writer http.ResponseWriter, request *http.Request) { response := `{"record":{"id":"dev-preview-abc123","type":"A","name":"www","data":"127.0.0.1","priority":0,"ttl":300}}` fmt.Fprint(writer, response) }) record, err := client.DomainRecord.Get(ctx, "vultr.com", "dev-preview-abc123") if err != nil { t.Errorf("DomainRecord.Get returned %+v, expected %+v", err, nil) } expected := &DomainRecord{ ID: "dev-preview-abc123", Type: "A", Name: "www", Data: "127.0.0.1", Priority: 0, TTL: 300, } if !reflect.DeepEqual(record, expected) { t.Errorf("DomainRecord.Get returned %+v, expected %+v", record, expected) } } func TestDomainRecordsServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com/records/abc123", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) p := 10 r := &DomainRecordReq{ Name: "*", Type: "A", Data: "127.0.0.1", TTL: 1200, Priority: &p, } err := client.DomainRecord.Update(ctx, "vultr.com", "abc123", r) if err != nil { t.Errorf("DNSRecord.Update returned %+v, expected %+v", err, nil) } } func TestDomainRecordsServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com/records/abc123", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.DomainRecord.Delete(ctx, "vultr.com", "abc123") if err != nil { t.Errorf("DomainRecord.Delete returned %+v, expected %+v", err, nil) } } func TestDomainRecordsServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com/records", func(writer http.ResponseWriter, request *http.Request) { response := `{"records":[{"id":"abc123","type":"A","name":"test","data":"127.0.0.1","priority":0,"ttl":300}],"meta":{"total":1,"links":{"next":"thisismycursor","prev":""}}}` fmt.Fprint(writer, response) }) options := &ListOptions{ PerPage: 1, } records, meta, err := client.DomainRecord.List(ctx, "vultr.com", options) if err != nil { t.Errorf("DomainRecord.List returned %+v, expected %+v", err, nil) } expectedRecords := []DomainRecord{ { ID: "abc123", Type: "A", Name: "test", Data: "127.0.0.1", Priority: 0, TTL: 300, }, } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycursor", Prev: "", }, } if !reflect.DeepEqual(records, expectedRecords) { t.Errorf("DomainRecord.List returned %+v, expected %+v", records, expectedRecords) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("DomainRecord.List meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/domains.go000066400000000000000000000122161425211421400211630ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const domainPath = "/v2/domains" // DomainService is the interface to interact with the DNS endpoints on the Vultr API // https://www.vultr.com/api/#tag/dns type DomainService interface { Create(ctx context.Context, domainReq *DomainReq) (*Domain, error) Get(ctx context.Context, domain string) (*Domain, error) Update(ctx context.Context, domain, dnsSec string) error Delete(ctx context.Context, domain string) error List(ctx context.Context, options *ListOptions) ([]Domain, *Meta, error) GetSoa(ctx context.Context, domain string) (*Soa, error) UpdateSoa(ctx context.Context, domain string, soaReq *Soa) error GetDNSSec(ctx context.Context, domain string) ([]string, error) } // DomainServiceHandler handles interaction with the DNS methods for the Vultr API type DomainServiceHandler struct { client *Client } // Domain represents a Domain entry on Vultr type Domain struct { Domain string `json:"domain,omitempty"` DateCreated string `json:"date_created,omitempty"` DNSSec string `json:"dns_sec,omitempty"` } // DomainReq is the struct to create a domain // If IP is omitted then an empty DNS entry will be created. If supplied the domain will be pre populated with entries type DomainReq struct { Domain string `json:"domain,omitempty"` IP string `json:"ip,omitempty"` DNSSec string `json:"dns_sec,omitempty"` } type domainsBase struct { Domains []Domain `json:"domains"` Meta *Meta `json:"meta"` } type domainBase struct { Domain *Domain `json:"domain"` } // Soa information for the Domain type Soa struct { NSPrimary string `json:"nsprimary,omitempty"` Email string `json:"email,omitempty"` } type soaBase struct { DNSSoa *Soa `json:"dns_soa,omitempty"` } type dnsSecBase struct { DNSSec []string `json:"dns_sec,omitempty"` } // Create a domain entry func (d *DomainServiceHandler) Create(ctx context.Context, domainReq *DomainReq) (*Domain, error) { req, err := d.client.NewRequest(ctx, http.MethodPost, domainPath, domainReq) if err != nil { return nil, err } domain := new(domainBase) if err = d.client.DoWithContext(ctx, req, domain); err != nil { return nil, err } return domain.Domain, nil } // Get a domain from your Vultr account. func (d *DomainServiceHandler) Get(ctx context.Context, domain string) (*Domain, error) { req, err := d.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s", domainPath, domain), nil) if err != nil { return nil, err } dBase := new(domainBase) if err = d.client.DoWithContext(ctx, req, dBase); err != nil { return nil, err } return dBase.Domain, nil } // Update allows you to enable or disable DNS Sec on the domain. // The two valid options for dnsSec are "enabled" or "disabled" func (d *DomainServiceHandler) Update(ctx context.Context, domain, dnsSec string) error { body := &RequestBody{"dns_sec": dnsSec} req, err := d.client.NewRequest(ctx, http.MethodPut, fmt.Sprintf("%s/%s", domainPath, domain), body) if err != nil { return err } return d.client.DoWithContext(ctx, req, nil) } // Delete a domain with all associated records. func (d *DomainServiceHandler) Delete(ctx context.Context, domain string) error { req, err := d.client.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/%s", domainPath, domain), nil) if err != nil { return err } return d.client.DoWithContext(ctx, req, nil) } // List gets all domains associated with the current Vultr account. func (d *DomainServiceHandler) List(ctx context.Context, options *ListOptions) ([]Domain, *Meta, error) { req, err := d.client.NewRequest(ctx, http.MethodGet, domainPath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() domains := new(domainsBase) err = d.client.DoWithContext(ctx, req, domains) if err != nil { return nil, nil, err } return domains.Domains, domains.Meta, nil } // GetSoa gets the SOA record information for a domain func (d *DomainServiceHandler) GetSoa(ctx context.Context, domain string) (*Soa, error) { req, err := d.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/soa", domainPath, domain), nil) if err != nil { return nil, err } soa := new(soaBase) if err = d.client.DoWithContext(ctx, req, soa); err != nil { return nil, err } return soa.DNSSoa, nil } // UpdateSoa will update the SOA record information for a domain. func (d *DomainServiceHandler) UpdateSoa(ctx context.Context, domain string, soaReq *Soa) error { req, err := d.client.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s/%s/soa", domainPath, domain), soaReq) if err != nil { return err } return d.client.DoWithContext(ctx, req, nil) } // GetDNSSec gets the DNSSec keys for a domain (if enabled) func (d *DomainServiceHandler) GetDNSSec(ctx context.Context, domain string) ([]string, error) { req, err := d.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/dnssec", domainPath, domain), nil) if err != nil { return nil, err } dnsSec := new(dnsSecBase) if err = d.client.DoWithContext(ctx, req, dnsSec); err != nil { return nil, err } return dnsSec.DNSSec, nil } golang-github-vultr-govultr-2.17.2/domains_test.go000066400000000000000000000123561425211421400222270ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestDomainServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains", func(writer http.ResponseWriter, request *http.Request) { response := `{"domain": {"domain": "vultr.com","date_created": "2020-05-08 19:09:07"}}` fmt.Fprint(writer, response) }) req := &DomainReq{ Domain: "vultr.com", } domain, err := client.Domain.Create(ctx, req) if err != nil { t.Errorf("DNSDomain.Create returned %+v, expected %+v", err, nil) } expected := &Domain{ Domain: "vultr.com", DateCreated: "2020-05-08 19:09:07", } if !reflect.DeepEqual(domain, expected) { t.Errorf("Domain.Create returned %+v, expected %+v", domain, expected) } } func TestDomainServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com", func(writer http.ResponseWriter, request *http.Request) { response := `{"domain": {"domain": "vultr.com","date_created": "2020-05-08 19:09:07"}}` fmt.Fprint(writer, response) }) domain, err := client.Domain.Get(ctx, "vultr.com") if err != nil { t.Errorf("DNSDomain.Create returned %+v, expected %+v", err, nil) } expected := &Domain{ Domain: "vultr.com", DateCreated: "2020-05-08 19:09:07", } if !reflect.DeepEqual(domain, expected) { t.Errorf("Domain.Create returned %+v, expected %+v", domain, expected) } } func TestDomainServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Domain.Update(ctx, "vultr.com", "enabled") if err != nil { t.Errorf("Domain.Update returned %+v, expected %+v", err, nil) } } func TestDomainServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/domain.com", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Domain.Delete(ctx, "domain.com") if err != nil { t.Errorf("Domain.Delete returned %+v, expected %+v", err, nil) } } func TestDNSDomainServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains", func(writer http.ResponseWriter, request *http.Request) { response := `{"domains":[{"domain":"vultr.com","date_created":"2020-05-0819:09:07"}],"meta":{"total":1,"links":{"next":"thisismycusror","prev":""}}}` fmt.Fprint(writer, response) }) options := &ListOptions{ PerPage: 1, } domains, meta, err := client.Domain.List(ctx, options) if err != nil { t.Errorf("Domain.List returned %+v, expected %+v", err, nil) } expectedDomain := []Domain{ { Domain: "vultr.com", DateCreated: "2020-05-0819:09:07", }, } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(domains, expectedDomain) { t.Errorf("Domain.List returned %+v, expected %+v", domains, expectedDomain) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Domain.List meta returned %+v, expected %+v", meta, expectedMeta) } } func TestDomainServiceHandler_GetSoa(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com/soa", func(writer http.ResponseWriter, request *http.Request) { response := `{"dns_soa":{"nsprimary":"ns1.vultr.com","email":"dnsadm@vultr.com"}}` fmt.Fprint(writer, response) }) soa, err := client.Domain.GetSoa(ctx, "vultr.com") if err != nil { t.Errorf("Domain.GetSoa returned %+v, expected %+v", err, nil) } expected := &Soa{NSPrimary: "ns1.vultr.com", Email: "dnsadm@vultr.com"} if !reflect.DeepEqual(soa, expected) { t.Errorf("DNSDomain.GetSoa returned %+v, expected %+v", soa, expected) } } func TestDNSDomainServiceHandler_UpdateSoa(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com/soa", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) r := &Soa{ NSPrimary: "ns4.vultr.com", Email: "vultr@vultr.com", } err := client.Domain.UpdateSoa(ctx, "vultr.com", r) if err != nil { t.Errorf("Domain.UpdateSoa returned %+v, expected %+v", err, nil) } } func TestDNSDomainServiceHandler_DNSSecInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/domains/vultr.com/dnssec", func(writer http.ResponseWriter, request *http.Request) { response := `{"dns_sec":[ "example.com IN DNSKEY 257 3 13 kRrxANp7YTGqVbaWtMy8hhsK0jcG4ajjICZKMb4fKv79Vx/RSn76vNjzIT7/Uo0BXil01Fk8RRQc4nWZctGJBA==", "example.com IN DS 27933 13 1 2d9ac457e5c11a104e25d971d0a6254562bddde7", "example.com IN DS 27933 13 2 8858e7b0dfb881280ce2ca1e0eafcd93d5b53687c21da284d4f8799ba82208a9" ]}` fmt.Fprint(writer, response) }) dnsSec, err := client.Domain.GetDNSSec(ctx, "vultr.com") if err != nil { t.Errorf("Domain.GetDnsSec returned %+v, expected %+v", err, nil) } expected := []string{ "example.com IN DNSKEY 257 3 13 kRrxANp7YTGqVbaWtMy8hhsK0jcG4ajjICZKMb4fKv79Vx/RSn76vNjzIT7/Uo0BXil01Fk8RRQc4nWZctGJBA==", "example.com IN DS 27933 13 1 2d9ac457e5c11a104e25d971d0a6254562bddde7", "example.com IN DS 27933 13 2 8858e7b0dfb881280ce2ca1e0eafcd93d5b53687c21da284d4f8799ba82208a9", } if !reflect.DeepEqual(dnsSec, expected) { t.Errorf("Domain.GetDnsSec returned %+v, expected %+v", dnsSec, expected) } } golang-github-vultr-govultr-2.17.2/firewall_group.go000066400000000000000000000075761425211421400225670ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // FirewallGroupService is the interface to interact with the firewall group endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/firewall type FirewallGroupService interface { Create(ctx context.Context, fwGroupReq *FirewallGroupReq) (*FirewallGroup, error) Get(ctx context.Context, groupID string) (*FirewallGroup, error) Update(ctx context.Context, fwGroupID string, fwGroupReq *FirewallGroupReq) error Delete(ctx context.Context, fwGroupID string) error List(ctx context.Context, options *ListOptions) ([]FirewallGroup, *Meta, error) } // FireWallGroupServiceHandler handles interaction with the firewall group methods for the Vultr API type FireWallGroupServiceHandler struct { client *Client } // FirewallGroup represents a Vultr firewall group type FirewallGroup struct { ID string `json:"id"` Description string `json:"description"` DateCreated string `json:"date_created"` DateModified string `json:"date_modified"` InstanceCount int `json:"instance_count"` RuleCount int `json:"rule_count"` MaxRuleCount int `json:"max_rule_count"` } // FirewallGroupReq struct is used to create and update a Firewall Group. type FirewallGroupReq struct { Description string `json:"description"` } type firewallGroupsBase struct { FirewallGroups []FirewallGroup `json:"firewall_groups"` Meta *Meta `json:"meta"` } type firewallGroupBase struct { FirewallGroup *FirewallGroup `json:"firewall_group"` } // Create will create a new firewall group on your Vultr account func (f *FireWallGroupServiceHandler) Create(ctx context.Context, fwGroupReq *FirewallGroupReq) (*FirewallGroup, error) { uri := "/v2/firewalls" req, err := f.client.NewRequest(ctx, http.MethodPost, uri, fwGroupReq) if err != nil { return nil, err } firewall := new(firewallGroupBase) if err = f.client.DoWithContext(ctx, req, firewall); err != nil { return nil, err } return firewall.FirewallGroup, nil } // Get will return a firewall group based on provided groupID from your Vultr account func (f *FireWallGroupServiceHandler) Get(ctx context.Context, fwGroupID string) (*FirewallGroup, error) { uri := fmt.Sprintf("/v2/firewalls/%s", fwGroupID) req, err := f.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } firewall := new(firewallGroupBase) if err = f.client.DoWithContext(ctx, req, firewall); err != nil { return nil, err } return firewall.FirewallGroup, nil } // Update will change the description of a firewall group func (f *FireWallGroupServiceHandler) Update(ctx context.Context, fwGroupID string, fwGroupReq *FirewallGroupReq) error { uri := fmt.Sprintf("/v2/firewalls/%s", fwGroupID) req, err := f.client.NewRequest(ctx, http.MethodPut, uri, fwGroupReq) if err != nil { return err } return f.client.DoWithContext(ctx, req, nil) } // Delete will delete a firewall group from your Vultr account func (f *FireWallGroupServiceHandler) Delete(ctx context.Context, fwGroupID string) error { uri := fmt.Sprintf("/v2/firewalls/%s", fwGroupID) req, err := f.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return f.client.DoWithContext(ctx, req, nil) } // List will return a list of all firewall groups on your Vultr account func (f *FireWallGroupServiceHandler) List(ctx context.Context, options *ListOptions) ([]FirewallGroup, *Meta, error) { uri := "/v2/firewalls" req, err := f.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() firewalls := new(firewallGroupsBase) if err = f.client.DoWithContext(ctx, req, firewalls); err != nil { return nil, nil, err } return firewalls.FirewallGroups, firewalls.Meta, nil } golang-github-vultr-govultr-2.17.2/firewall_group_test.go000066400000000000000000000076671425211421400236270ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestFireWallGroupServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls", func(writer http.ResponseWriter, request *http.Request) { response := `{"firewall_group":{"id":"44d0f934","description":"govultr test","date_created":"2020-07-0913:53:34","date_modified":"2020-07-0913:53:34","instance_count":15,"rule_count":6,"max_rule_count":999}}` fmt.Fprint(writer, response) }) group := &FirewallGroupReq{Description: "govultr test"} firewallGroup, err := client.FirewallGroup.Create(ctx, group) if err != nil { t.Errorf("FirewallGroup.Create returned error: %v", err) } expected := &FirewallGroup{ ID: "44d0f934", Description: "govultr test", DateCreated: "2020-07-0913:53:34", DateModified: "2020-07-0913:53:34", InstanceCount: 15, RuleCount: 6, MaxRuleCount: 999, } if !reflect.DeepEqual(firewallGroup, expected) { t.Errorf("FirewallGroup.Create returned %+v, expected %+v", firewallGroup, expected) } } func TestFireWallGroupServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls/44d0f934", func(writer http.ResponseWriter, request *http.Request) { response := `{"firewall_group":{"id":"44d0f934","description":"govultr test","date_created":"2020-07-0913:53:34","date_modified":"2020-07-0913:53:34","instance_count":15,"rule_count":6,"max_rule_count":999}}` fmt.Fprint(writer, response) }) firewallGroup, err := client.FirewallGroup.Get(ctx, "44d0f934") if err != nil { t.Errorf("FirewallGroup.Create returned error: %v", err) } expected := &FirewallGroup{ ID: "44d0f934", Description: "govultr test", DateCreated: "2020-07-0913:53:34", DateModified: "2020-07-0913:53:34", InstanceCount: 15, RuleCount: 6, MaxRuleCount: 999, } if !reflect.DeepEqual(firewallGroup, expected) { t.Errorf("FirewallGroup.Create returned %+v, expected %+v", firewallGroup, expected) } } func TestFireWallGroupServiceHandler_ChangeDescription(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls/abc123", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) put := &FirewallGroupReq{Description: "test"} err := client.FirewallGroup.Update(ctx, "abc123", put) if err != nil { t.Errorf("FirewallGroup.ChangeDescription returned error: %v", err) } } func TestFireWallGroupServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls/abc123", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.FirewallGroup.Delete(ctx, "abc123") if err != nil { t.Errorf("FirewallGroup.Delete returned error: %v", err) } } func TestFireWallGroupServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls", func(writer http.ResponseWriter, request *http.Request) { response := `{"firewall_groups":[{"id":"44d0f934","description":"govultr test","date_created":"2020-07-0913:53:34","date_modified":"2020-07-0913:53:34","instance_count":15,"rule_count":6,"max_rule_count":999}],"meta":{"total":5,"links":{"next":"","prev":""}}}` fmt.Fprint(writer, response) }) firewallGroup, meta, err := client.FirewallGroup.List(ctx, nil) if err != nil { t.Errorf("FirewallGroup.List returned error: %v", err) } expectedGroup := []FirewallGroup{ { ID: "44d0f934", Description: "govultr test", DateCreated: "2020-07-0913:53:34", DateModified: "2020-07-0913:53:34", InstanceCount: 15, RuleCount: 6, MaxRuleCount: 999, }, } expectedMeta := &Meta{ Total: 5, Links: &Links{}, } if !reflect.DeepEqual(firewallGroup, expectedGroup) { t.Errorf("FirewallGroup.List groups returned %+v, expected %+v", firewallGroup, expectedGroup) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("FirewallGroup.List meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/firewall_rule.go000066400000000000000000000076631425211421400223770ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // FireWallRuleService is the interface to interact with the firewall rule endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/firewall type FireWallRuleService interface { Create(ctx context.Context, fwGroupID string, fwRuleReq *FirewallRuleReq) (*FirewallRule, error) Get(ctx context.Context, fwGroupID string, fwRuleID int) (*FirewallRule, error) Delete(ctx context.Context, fwGroupID string, fwRuleID int) error List(ctx context.Context, fwGroupID string, options *ListOptions) ([]FirewallRule, *Meta, error) } // FireWallRuleServiceHandler handles interaction with the firewall rule methods for the Vultr API type FireWallRuleServiceHandler struct { client *Client } // FirewallRule represents a Vultr firewall rule type FirewallRule struct { ID int `json:"id"` Action string `json:"action"` // Deprecated: Type should no longer be used. Instead, use IPType. Type string `json:"type"` IPType string `json:"ip_type"` Protocol string `json:"protocol"` Port string `json:"port"` Subnet string `json:"subnet"` SubnetSize int `json:"subnet_size"` Source string `json:"source"` Notes string `json:"notes"` } // FirewallRuleReq struct used to create a FirewallRule. type FirewallRuleReq struct { IPType string `json:"ip_type"` Protocol string `json:"protocol"` Subnet string `json:"subnet"` SubnetSize int `json:"subnet_size"` Port string `json:"port,omitempty"` Source string `json:"source,omitempty"` Notes string `json:"notes,omitempty"` } type firewallRulesBase struct { FirewallRules []FirewallRule `json:"firewall_rules"` Meta *Meta `json:"meta"` } type firewallRuleBase struct { FirewallRule *FirewallRule `json:"firewall_rule"` } // Create will create a rule in a firewall group. func (f *FireWallRuleServiceHandler) Create(ctx context.Context, fwGroupID string, fwRuleReq *FirewallRuleReq) (*FirewallRule, error) { uri := fmt.Sprintf("/v2/firewalls/%s/rules", fwGroupID) req, err := f.client.NewRequest(ctx, http.MethodPost, uri, fwRuleReq) if err != nil { return nil, err } firewallRule := new(firewallRuleBase) if err = f.client.DoWithContext(ctx, req, firewallRule); err != nil { return nil, err } return firewallRule.FirewallRule, nil } // Get will get a rule in a firewall group. func (f *FireWallRuleServiceHandler) Get(ctx context.Context, fwGroupID string, fwRuleID int) (*FirewallRule, error) { uri := fmt.Sprintf("/v2/firewalls/%s/rules/%d", fwGroupID, fwRuleID) req, err := f.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } firewallRule := new(firewallRuleBase) if err = f.client.DoWithContext(ctx, req, firewallRule); err != nil { return nil, err } return firewallRule.FirewallRule, nil } // Delete will delete a firewall rule on your Vultr account func (f *FireWallRuleServiceHandler) Delete(ctx context.Context, fwGroupID string, fwRuleID int) error { uri := fmt.Sprintf("/v2/firewalls/%s/rules/%d", fwGroupID, fwRuleID) req, err := f.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return f.client.DoWithContext(ctx, req, nil) } // List will return both ipv4 an ipv6 firewall rules that are defined within a firewall group func (f *FireWallRuleServiceHandler) List(ctx context.Context, fwGroupID string, options *ListOptions) ([]FirewallRule, *Meta, error) { uri := fmt.Sprintf("/v2/firewalls/%s/rules", fwGroupID) req, err := f.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() firewallRule := new(firewallRulesBase) if err = f.client.DoWithContext(ctx, req, firewallRule); err != nil { return nil, nil, err } return firewallRule.FirewallRules, firewallRule.Meta, nil } golang-github-vultr-govultr-2.17.2/firewall_rule_test.go000066400000000000000000000071201425211421400234220ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestFireWallRuleServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls/abc123/rules", func(writer http.ResponseWriter, request *http.Request) { response := `{"firewall_rule":{"id":1,"type":"v4","ip_type":"v4","action":"accept","protocol":"tcp","port":"80","subnet":"127.0.0.1","subnet_size":32,"source":"","notes":"thisisanote"}}` fmt.Fprint(writer, response) }) rule := &FirewallRuleReq{ IPType: "v4", Protocol: "tcp", Subnet: "127.0.0.1", SubnetSize: 30, Port: "80", Notes: "thisisanote", } firewallRule, err := client.FirewallRule.Create(ctx, "abc123", rule) if err != nil { t.Errorf("FirewallRule.Create returned error: %v", err) } expected := &FirewallRule{ ID: 1, Action: "accept", Type: "v4", IPType: "v4", Protocol: "tcp", Port: "80", Subnet: "127.0.0.1", SubnetSize: 32, Source: "", Notes: "thisisanote", } if !reflect.DeepEqual(firewallRule, expected) { t.Errorf("FirewallRule.Create returned %+v, expected %+v", firewallRule, expected) } } func TestFireWallRuleServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls/abc123/rules/1", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.FirewallRule.Delete(ctx, "abc123", 1) if err != nil { t.Errorf("FirewallRule.Delete returned error: %v", err) } } func TestFireWallRuleServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls/abc123/rules", func(writer http.ResponseWriter, request *http.Request) { response := `{"firewall_rules":[{"id":1,"type":"v4","ip_type":"v4","action":"accept","protocol":"tcp","port":"22","subnet":"0.0.0.0","subnet_size":0,"source":"","notes":""}],"meta":{"total":5,"links":{"next":"","prev":""}}}` fmt.Fprint(writer, response) }) firewallRule, meta, err := client.FirewallRule.List(ctx, "abc123", nil) if err != nil { t.Errorf("FirewallRule.List returned error: %v", err) } expectedRule := []FirewallRule{ { ID: 1, Action: "accept", Type: "v4", IPType: "v4", Protocol: "tcp", Port: "22", Subnet: "0.0.0.0", SubnetSize: 0, Source: "", Notes: "", }, } expectedMeta := &Meta{ Total: 5, Links: &Links{}, } if !reflect.DeepEqual(firewallRule, expectedRule) { t.Errorf("FirewallRule.List rules returned %+v, expected %+v", firewallRule, expectedRule) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("FirewallRule.List meta returned %+v, expected %+v", meta, expectedMeta) } } func TestFireWallRuleServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/firewalls/abc123/rules/1", func(writer http.ResponseWriter, request *http.Request) { response := `{"firewall_rule":{"id":1,"type":"v4","ip_type":"v4","action":"accept","protocol":"tcp","port":"22","subnet":"0.0.0.0","subnet_size":0,"source":"","notes":""}}` fmt.Fprint(writer, response) }) firewallRule, err := client.FirewallRule.Get(ctx, "abc123", 1) if err != nil { t.Errorf("FirewallRule.Get returned error: %v", err) } expectedRule := &FirewallRule{ ID: 1, Action: "accept", Type: "v4", IPType: "v4", Protocol: "tcp", Port: "22", Subnet: "0.0.0.0", SubnetSize: 0, Source: "", Notes: "", } if !reflect.DeepEqual(firewallRule, expectedRule) { t.Errorf("FirewallRule.Get returned %+v, expected %+v", firewallRule, expectedRule) } } golang-github-vultr-govultr-2.17.2/go.mod000066400000000000000000000003161425211421400203060ustar00rootroot00000000000000module github.com/vultr/govultr/v2 go 1.17 require ( github.com/google/go-querystring v1.1.0 github.com/hashicorp/go-retryablehttp v0.7.1 ) require github.com/hashicorp/go-cleanhttp v0.5.1 // indirect golang-github-vultr-govultr-2.17.2/go.sum000066400000000000000000000024071425211421400203360ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang-github-vultr-govultr-2.17.2/govultr.go000066400000000000000000000161761425211421400212440ustar00rootroot00000000000000package govultr import ( "bytes" "context" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "net/url" "strings" "time" "github.com/hashicorp/go-retryablehttp" ) const ( version = "2.17.2" defaultBase = "https://api.vultr.com" userAgent = "govultr/" + version rateLimit = 500 * time.Millisecond retryLimit = 3 ) // APIKey contains a users API Key for interacting with the API type APIKey struct { // API Key key string } // RequestBody is used to create JSON bodies for one off calls type RequestBody map[string]interface{} // Client manages interaction with the Vultr V1 API type Client struct { // Http Client used to interact with the Vultr V1 API client *retryablehttp.Client // BASE URL for APIs BaseURL *url.URL // User Agent for the client UserAgent string // Services used to interact with the API Account AccountService Application ApplicationService Backup BackupService BareMetalServer BareMetalServerService Billing BillingService BlockStorage BlockStorageService 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 OS OSService Plan PlanService Region RegionService ReservedIP ReservedIPService Snapshot SnapshotService SSHKey SSHKeyService StartupScript StartupScriptService User UserService VPC VPCService // Optional function called after every successful request made to the Vultr API onRequestCompleted RequestCompletionCallback } // RequestCompletionCallback defines the type of the request callback function type RequestCompletionCallback func(*http.Request, *http.Response) // NewClient returns a Vultr API Client func NewClient(httpClient *http.Client) *Client { if httpClient == nil { httpClient = http.DefaultClient } baseURL, _ := url.Parse(defaultBase) client := &Client{ client: retryablehttp.NewClient(), BaseURL: baseURL, UserAgent: userAgent, } client.client.HTTPClient = httpClient client.client.Logger = nil client.client.ErrorHandler = client.vultrErrorHandler client.SetRetryLimit(retryLimit) client.SetRateLimit(rateLimit) client.Account = &AccountServiceHandler{client} client.Application = &ApplicationServiceHandler{client} client.Backup = &BackupServiceHandler{client} client.BareMetalServer = &BareMetalServerServiceHandler{client} client.Billing = &BillingServiceHandler{client} client.BlockStorage = &BlockStorageServiceHandler{client} client.Domain = &DomainServiceHandler{client} client.DomainRecord = &DomainRecordsServiceHandler{client} client.FirewallGroup = &FireWallGroupServiceHandler{client} client.FirewallRule = &FireWallRuleServiceHandler{client} client.Instance = &InstanceServiceHandler{client} client.ISO = &ISOServiceHandler{client} client.Kubernetes = &KubernetesHandler{client} client.LoadBalancer = &LoadBalancerHandler{client} client.Network = &NetworkServiceHandler{client} client.ObjectStorage = &ObjectStorageServiceHandler{client} client.OS = &OSServiceHandler{client} client.Plan = &PlanServiceHandler{client} client.Region = &RegionServiceHandler{client} client.ReservedIP = &ReservedIPServiceHandler{client} client.Snapshot = &SnapshotServiceHandler{client} client.SSHKey = &SSHKeyServiceHandler{client} client.StartupScript = &StartupScriptServiceHandler{client} client.User = &UserServiceHandler{client} client.VPC = &VPCServiceHandler{client} return client } // NewRequest creates an API Request func (c *Client) NewRequest(ctx context.Context, method, uri string, body interface{}) (*http.Request, error) { resolvedURL, err := c.BaseURL.Parse(uri) if err != nil { return nil, err } buf := new(bytes.Buffer) if body != nil { if err = json.NewEncoder(buf).Encode(body); err != nil { return nil, err } } req, err := http.NewRequest(method, resolvedURL.String(), buf) if err != nil { return nil, err } req.Header.Add("User-Agent", c.UserAgent) req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") return req, nil } // DoWithContext sends an API Request and returns back the response. The API response is checked to see if it was // a successful call. A successful call is then checked to see if we need to unmarshal since some resources // have their own implements of unmarshal. func (c *Client) DoWithContext(ctx context.Context, r *http.Request, data interface{}) error { rreq, err := retryablehttp.FromRequest(r) if err != nil { return err } rreq = rreq.WithContext(ctx) res, err := c.client.Do(rreq) if c.onRequestCompleted != nil { c.onRequestCompleted(r, res) } if err != nil { return err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return err } if res.StatusCode >= http.StatusOK && res.StatusCode <= http.StatusNoContent { if data != nil { if err := json.Unmarshal(body, data); err != nil { return err } } return nil } return errors.New(string(body)) } // SetBaseURL Overrides the default BaseUrl func (c *Client) SetBaseURL(baseURL string) error { updatedURL, err := url.Parse(baseURL) if err != nil { return err } c.BaseURL = updatedURL return nil } // SetRateLimit Overrides the default rateLimit. For performance, exponential // backoff is used with the minimum wait being 2/3rds the time provided. func (c *Client) SetRateLimit(time time.Duration) { c.client.RetryWaitMin = time / 3 * 2 c.client.RetryWaitMax = time } // SetUserAgent Overrides the default UserAgent func (c *Client) SetUserAgent(ua string) { c.UserAgent = ua } // OnRequestCompleted sets the API request completion callback func (c *Client) OnRequestCompleted(rc RequestCompletionCallback) { c.onRequestCompleted = rc } // SetRetryLimit overrides the default RetryLimit func (c *Client) SetRetryLimit(n int) { c.client.RetryMax = n } func (c *Client) vultrErrorHandler(resp *http.Response, err error, numTries int) (*http.Response, error) { if resp == nil { if err != nil { return nil, fmt.Errorf("gave up after %d attempts, last error : %s", numTries, err.Error()) } return nil, fmt.Errorf("gave up after %d attempts, last error unavailable (resp == nil)", numTries) } buf, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("gave up after %d attempts, last error unavailable (error reading response body: %v)", numTries, err) } return nil, fmt.Errorf("gave up after %d attempts, last error: %#v", numTries, strings.TrimSpace(string(buf))) } // BoolToBoolPtr helper function that returns a pointer from your bool value func BoolToBoolPtr(value bool) *bool { return &value } // StringToStringPtr helper function that returns a pointer from your string value func StringToStringPtr(value string) *string { return &value } // IntToIntPtr helper function that returns a pointer from your string value func IntToIntPtr(value int) *int { return &value } golang-github-vultr-govultr-2.17.2/govultr_test.go000066400000000000000000000164611425211421400223000ustar00rootroot00000000000000package govultr import ( "context" "errors" "fmt" "io/ioutil" "net/http" "net/http/httptest" "net/http/httputil" "net/url" "reflect" "strings" "testing" "time" ) var ( mux *http.ServeMux ctx = context.TODO() client *Client server *httptest.Server ) func setup() { mux = http.NewServeMux() server = httptest.NewServer(mux) client = NewClient(nil) url, _ := url.Parse(server.URL) client.BaseURL = url } func teardown() { server.Close() } func TestNewClient(t *testing.T) { setup() defer teardown() if client.BaseURL == nil || client.BaseURL.String() != server.URL { t.Errorf("NewClient BaseURL = %v, expected %v", client.BaseURL, server.URL) } //if client != "dummy-key" { // t.Errorf("NewClient ApiKey = %v, expected %v", client.APIKey.key, "dummy-key") //} if client.UserAgent != userAgent { t.Errorf("NewClient UserAgent = %v, expected %v", client.UserAgent, userAgent) } services := []string{ "Account", } copied := reflect.ValueOf(client) copyValue := reflect.Indirect(copied) for _, s := range services { if copyValue.FieldByName(s).IsNil() { t.Errorf("c.%s shouldn't be nil", s) } } } func TestClient_DoWithContext(t *testing.T) { setup() defer teardown() type vultr struct { Bird string } mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { if method := http.MethodGet; method != request.Method { t.Errorf("Request method = %v, expecting %v", request.Method, method) } fmt.Fprint(writer, `{"Bird":"vultr"}`) }) req, _ := client.NewRequest(ctx, http.MethodGet, "/", nil) data := new(vultr) err := client.DoWithContext(context.Background(), req, data) if err != nil { t.Fatalf("DoWithContext(): %v", err) } expected := &vultr{"vultr"} if !reflect.DeepEqual(data, expected) { t.Errorf("Response body = %v, expected %v", data, expected) } } func TestClient_DoWithContextFailure(t *testing.T) { setup() defer teardown() mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { if method := http.MethodGet; method != request.Method { t.Errorf("Request method = %v, expecting %v", request.Method, method) } writer.WriteHeader(500) fmt.Fprint(writer, `{Error}`) }) req, _ := client.NewRequest(ctx, http.MethodGet, "/", nil) err := client.DoWithContext(context.Background(), req, nil) if !strings.Contains(err.Error(), "gave up after") || !strings.Contains(err.Error(), "last error") { t.Fatalf("DoWithContext(): %v: expected 'gave up after ..., last error ...'", err) } } type errRoundTripper struct{} func (errRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, errors.New("fake error") } func TestClient_DoWithContextError(t *testing.T) { setup() defer teardown() client = NewClient(&http.Client{ Transport: errRoundTripper{}, }) req, _ := client.NewRequest(ctx, http.MethodGet, "/", nil) var panicked string func() { defer func() { if err := recover(); err != nil { panicked = fmt.Sprint(err) } }() client.DoWithContext(context.Background(), req, nil) }() if panicked != "" { t.Errorf("unexpected panic: %s", panicked) } } func TestClient_NewRequest(t *testing.T) { c := NewClient(nil) in := "/unit" out := defaultBase + "/unit" inRequest := RequestBody{"balance": 500} outRequest := `{"balance":500}` + "\n" req, _ := c.NewRequest(ctx, http.MethodPost, in, inRequest) if req.URL.String() != out { t.Errorf("NewRequest(%v) URL = %v, expected %v", in, req.URL, out) } body, _ := ioutil.ReadAll(req.Body) if string(body) != outRequest { t.Errorf("NewRequest(%v)Body = %v, expected %v", inRequest, string(body), outRequest) } userAgent := req.Header.Get("User-Agent") if c.UserAgent != userAgent { t.Errorf("NewRequest() User-Agent = %v, expected %v", userAgent, c.UserAgent) } contentType := req.Header.Get("Content-Type") if contentType != "application/json" { t.Errorf("NewRequest() Header Content Type = %v, expected %v", contentType, "application/x-www-form-urlencoded") } } func TestClient_SetBaseUrl(t *testing.T) { setup() defer teardown() base := "http://localhost/vultr" err := client.SetBaseURL(base) if err != nil { t.Fatalf("SetBaseUrl unexpected error: %v", err) } if client.BaseURL.String() != base { t.Errorf("NewClient BaseUrl = %v, expected %v", client.BaseURL, base) } if err := client.SetBaseURL(":"); err == nil { t.Error("Expected invalid BaseURL to fail") } } func TestClient_SetUserAgent(t *testing.T) { setup() defer teardown() ua := "vultr/testing" client.SetUserAgent(ua) if client.UserAgent != ua { t.Errorf("NewClient UserAgent = %v, expected %v", client.UserAgent, userAgent) } } func TestClient_SetRateLimit(t *testing.T) { setup() defer teardown() time := 600 * time.Millisecond client.SetRateLimit(time) if client.client.RetryWaitMax != time { t.Errorf("NewClient max RateLimit = %v, expected %v", client.client.RetryWaitMax, time) } } func TestClient_OnRequestCompleted(t *testing.T) { setup() defer teardown() var completedReq *http.Request var completedRes string type Vultr struct { Bird string } mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer, `{"Vultr":"bird"}`) }) req, _ := client.NewRequest(ctx, http.MethodGet, "/", nil) data := new(Vultr) client.OnRequestCompleted(func(request *http.Request, response *http.Response) { completedReq = req dump, err := httputil.DumpResponse(response, true) if err != nil { t.Errorf("Failed to dump response: %s", err) } completedRes = string(dump) }) err := client.DoWithContext(context.Background(), req, data) if err != nil { t.Fatalf("Do(): %v", err) } if !reflect.DeepEqual(req, completedReq) { t.Errorf("Completed request = %v, expected %v", completedReq, req) } expected := `{"Vultr":"bird"}` if !strings.Contains(completedRes, expected) { t.Errorf("expected response to contain %v, Response = %v", expected, completedRes) } } func TestClient_SetRetryLimit(t *testing.T) { setup() defer teardown() client.SetRetryLimit(4) if client.client.RetryMax != 4 { t.Errorf("NewClient RateLimit = %v, expected %v", client.client.RetryMax, 4) } } func TestNewRequest_badURI(t *testing.T) { c := NewClient(nil) _, err := c.NewRequest(ctx, http.MethodGet, ":/1.", nil) if err == nil { t.Error("expected invalid URI to fail") } } func TestNewRequest_badBody(t *testing.T) { c := NewClient(nil) _, err := c.NewRequest(ctx, http.MethodGet, "/", make(chan int)) if err == nil { t.Error("expected invalid Body to fail") } } func TestRequest_InvalidCall(t *testing.T) { setup() defer teardown() mux.HandleFunc("/wrong", func(writer http.ResponseWriter, request *http.Request) { writer.WriteHeader(http.StatusBadRequest) fmt.Fprint(writer, nil) }) req, _ := client.NewRequest(ctx, http.MethodGet, "/wrong", nil) if err := client.DoWithContext(ctx, req, nil); err == nil { t.Error("Expected invalid status code to bad request") } } func TestRequest_InvalidResponseBody(t *testing.T) { setup() defer teardown() mux.HandleFunc("/wrong", func(writer http.ResponseWriter, request *http.Request) { writer.WriteHeader(http.StatusOK) fmt.Fprint(writer, `{`) }) req, _ := client.NewRequest(ctx, http.MethodGet, "/wrong", nil) if err := client.DoWithContext(ctx, req, struct{}{}); err == nil { t.Error("Expected response body to be invalid") } } golang-github-vultr-govultr-2.17.2/instance.go000066400000000000000000000700601425211421400213360ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const instancePath = "/v2/instances" // InstanceService is the interface to interact with the instance endpoints on the Vultr API // Link: https://www.vultr.com/api/#tag/instances type InstanceService interface { Create(ctx context.Context, instanceReq *InstanceCreateReq) (*Instance, error) Get(ctx context.Context, instanceID string) (*Instance, error) Update(ctx context.Context, instanceID string, instanceReq *InstanceUpdateReq) (*Instance, error) Delete(ctx context.Context, instanceID string) error List(ctx context.Context, options *ListOptions) ([]Instance, *Meta, error) Start(ctx context.Context, instanceID string) error Halt(ctx context.Context, instanceID string) error Reboot(ctx context.Context, instanceID string) error Reinstall(ctx context.Context, instanceID string, reinstallReq *ReinstallReq) (*Instance, error) MassStart(ctx context.Context, instanceList []string) error MassHalt(ctx context.Context, instanceList []string) error MassReboot(ctx context.Context, instanceList []string) error Restore(ctx context.Context, instanceID string, restoreReq *RestoreReq) error GetBandwidth(ctx context.Context, instanceID string) (*Bandwidth, error) GetNeighbors(ctx context.Context, instanceID string) (*Neighbors, error) // Deprecated: ListPrivateNetworks should no longer be used. Instead, use ListVPCInfo. ListPrivateNetworks(ctx context.Context, instanceID string, options *ListOptions) ([]PrivateNetwork, *Meta, error) // Deprecated: AttachPrivateNetwork should no longer be used. Instead, use AttachVPC. AttachPrivateNetwork(ctx context.Context, instanceID, networkID string) error // Deprecated: DetachPrivateNetwork should no longer be used. Instead, use DetachVPC. DetachPrivateNetwork(ctx context.Context, instanceID, networkID string) error ListVPCInfo(ctx context.Context, instanceID string, options *ListOptions) ([]VPCInfo, *Meta, error) AttachVPC(ctx context.Context, instanceID, vpcID string) error DetachVPC(ctx context.Context, instanceID, vpcID string) error ISOStatus(ctx context.Context, instanceID string) (*Iso, error) AttachISO(ctx context.Context, instanceID, isoID string) error DetachISO(ctx context.Context, instanceID string) error GetBackupSchedule(ctx context.Context, instanceID string) (*BackupSchedule, error) SetBackupSchedule(ctx context.Context, instanceID string, backup *BackupScheduleReq) error CreateIPv4(ctx context.Context, instanceID string, reboot *bool) (*IPv4, error) ListIPv4(ctx context.Context, instanceID string, option *ListOptions) ([]IPv4, *Meta, error) DeleteIPv4(ctx context.Context, instanceID, ip string) error ListIPv6(ctx context.Context, instanceID string, option *ListOptions) ([]IPv6, *Meta, error) CreateReverseIPv6(ctx context.Context, instanceID string, reverseReq *ReverseIP) error ListReverseIPv6(ctx context.Context, instanceID string) ([]ReverseIP, error) DeleteReverseIPv6(ctx context.Context, instanceID, ip string) error CreateReverseIPv4(ctx context.Context, instanceID string, reverseReq *ReverseIP) error DefaultReverseIPv4(ctx context.Context, instanceID, ip string) error GetUserData(ctx context.Context, instanceID string) (*UserData, error) GetUpgrades(ctx context.Context, instanceID string) (*Upgrades, error) } // InstanceServiceHandler handles interaction with the server methods for the Vultr API type InstanceServiceHandler struct { client *Client } // Instance represents a VPS type Instance struct { ID string `json:"id"` Os string `json:"os"` RAM int `json:"ram"` Disk int `json:"disk"` Plan string `json:"plan"` MainIP string `json:"main_ip"` VCPUCount int `json:"vcpu_count"` Region string `json:"region"` DefaultPassword string `json:"default_password,omitempty"` DateCreated string `json:"date_created"` Status string `json:"status"` AllowedBandwidth int `json:"allowed_bandwidth"` NetmaskV4 string `json:"netmask_v4"` GatewayV4 string `json:"gateway_v4"` PowerStatus string `json:"power_status"` ServerStatus string `json:"server_status"` V6Network string `json:"v6_network"` V6MainIP string `json:"v6_main_ip"` V6NetworkSize int `json:"v6_network_size"` Label string `json:"label"` InternalIP string `json:"internal_ip"` KVM string `json:"kvm"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag string `json:"tag"` OsID int `json:"os_id"` AppID int `json:"app_id"` ImageID string `json:"image_id"` FirewallGroupID string `json:"firewall_group_id"` Features []string `json:"features"` Hostname string `json:"hostname"` Tags []string `json:"tags"` } type instanceBase struct { Instance *Instance `json:"instance"` } type ipv4Base struct { IPv4 *IPv4 `json:"ipv4"` } type instancesBase struct { Instances []Instance `json:"instances"` Meta *Meta `json:"meta"` } // Neighbors that might exist on the same host. type Neighbors struct { Neighbors []string `json:"neighbors"` } // Bandwidth used on a given instance. type Bandwidth struct { Bandwidth map[string]struct { IncomingBytes int `json:"incoming_bytes"` OutgoingBytes int `json:"outgoing_bytes"` } `json:"bandwidth"` } type privateNetworksBase struct { PrivateNetworks []PrivateNetwork `json:"private_networks"` Meta *Meta `json:"meta"` } // PrivateNetwork information for a given instance. // Deprecated: PrivateNetwork should no longer be used. Instead, use VPCInfo. type PrivateNetwork struct { NetworkID string `json:"network_id"` MacAddress string `json:"mac_address"` IPAddress string `json:"ip_address"` } type vpcInfoBase struct { VPCs []VPCInfo `json:"vpcs"` Meta *Meta `json:"meta"` } // VPCInfo information for a given instance. type VPCInfo struct { ID string `json:"id"` MacAddress string `json:"mac_address"` IPAddress string `json:"ip_address"` } type isoStatusBase struct { IsoStatus *Iso `json:"iso_status"` } // Iso information for a given instance. type Iso struct { State string `json:"state"` IsoID string `json:"iso_id"` } type backupScheduleBase struct { BackupSchedule *BackupSchedule `json:"backup_schedule"` } // BackupSchedule information for a given instance. type BackupSchedule struct { Enabled *bool `json:"enabled,omitempty"` Type string `json:"type,omitempty"` NextScheduleTimeUTC string `json:"next_scheduled_time_utc,omitempty"` Hour int `json:"hour,omitempty"` Dow int `json:"dow,omitempty"` Dom int `json:"dom,omitempty"` } // BackupScheduleReq struct used to create a backup schedule for an instance. type BackupScheduleReq struct { Type string `json:"type"` Hour *int `json:"hour,omitempty"` Dow *int `json:"dow,omitempty"` Dom int `json:"dom,omitempty"` } // RestoreReq struct used to supply whether a restore should be from a backup or snapshot. type RestoreReq struct { BackupID string `json:"backup_id,omitempty"` SnapshotID string `json:"snapshot_id,omitempty"` } // todo can we remove this list and return this data back in the list? type reverseIPv6sBase struct { ReverseIPv6s []ReverseIP `json:"reverse_ipv6s"` // no meta? } // ReverseIP information for a given instance. type ReverseIP struct { IP string `json:"ip"` Reverse string `json:"reverse"` } type userDataBase struct { UserData *UserData `json:"user_data"` } // UserData information for a given struct. type UserData struct { Data string `json:"data"` } type upgradeBase struct { Upgrades *Upgrades `json:"upgrades"` } // Upgrades that are available for a given Instance. type Upgrades struct { Applications []Application `json:"applications,omitempty"` OS []OS `json:"os,omitempty"` Plans []string `json:"plans,omitempty"` } // InstanceCreateReq struct used to create an instance. type InstanceCreateReq struct { Region string `json:"region,omitempty"` Plan string `json:"plan,omitempty"` Label string `json:"label,omitempty"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag string `json:"tag,omitempty"` Tags []string `json:"tags"` OsID int `json:"os_id,omitempty"` ISOID string `json:"iso_id,omitempty"` AppID int `json:"app_id,omitempty"` ImageID string `json:"image_id,omitempty"` FirewallGroupID string `json:"firewall_group_id,omitempty"` Hostname string `json:"hostname,omitempty"` IPXEChainURL string `json:"ipxe_chain_url,omitempty"` ScriptID string `json:"script_id,omitempty"` SnapshotID string `json:"snapshot_id,omitempty"` EnableIPv6 *bool `json:"enable_ipv6,omitempty"` // Deprecated: EnablePrivateNetwork should no longer be used. Instead, use EnableVPC. EnablePrivateNetwork *bool `json:"enable_private_network,omitempty"` // Deprecated: AttachPrivateNetwork should no longer be used. Instead, use AttachVPC. AttachPrivateNetwork []string `json:"attach_private_network,omitempty"` EnableVPC *bool `json:"enable_vpc,omitempty"` AttachVPC []string `json:"attach_vpc,omitempty"` SSHKeys []string `json:"sshkey_id,omitempty"` Backups string `json:"backups,omitempty"` DDOSProtection *bool `json:"ddos_protection,omitempty"` UserData string `json:"user_data,omitempty"` ReservedIPv4 string `json:"reserved_ipv4,omitempty"` ActivationEmail *bool `json:"activation_email,omitempty"` } // InstanceUpdateReq struct used to update an instance. type InstanceUpdateReq struct { Plan string `json:"plan,omitempty"` Label string `json:"label,omitempty"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag *string `json:"tag,omitempty"` Tags []string `json:"tags"` OsID int `json:"os_id,omitempty"` AppID int `json:"app_id,omitempty"` ImageID string `json:"image_id,omitempty"` EnableIPv6 *bool `json:"enable_ipv6,omitempty"` // Deprecated: EnablePrivateNetwork should no longer be used. Instead, use EnableVPC. EnablePrivateNetwork *bool `json:"enable_private_network,omitempty"` // Deprecated: AttachPrivateNetwork should no longer be used. Instead, use AttachVPC. AttachPrivateNetwork []string `json:"attach_private_network,omitempty"` // Deprecated: DetachPrivateNetwork should no longer be used. Instead, use DetachVPC. DetachPrivateNetwork []string `json:"detach_private_network,omitempty"` EnableVPC *bool `json:"enable_vpc,omitempty"` AttachVPC []string `json:"attach_vpc,omitempty"` DetachVPC []string `json:"detach_vpc,omitempty"` Backups string `json:"backups,omitempty"` DDOSProtection *bool `json:"ddos_protection"` UserData string `json:"user_data,omitempty"` FirewallGroupID string `json:"firewall_group_id,omitempty"` } // ReinstallReq struct used to allow changes during a reinstall type ReinstallReq struct { Hostname string `json:"hostname,omitempty"` } // Create will create the server with the given parameters func (i *InstanceServiceHandler) Create(ctx context.Context, instanceReq *InstanceCreateReq) (*Instance, error) { uri := fmt.Sprintf("%s", instancePath) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, instanceReq) if err != nil { return nil, err } instance := new(instanceBase) if err = i.client.DoWithContext(ctx, req, instance); err != nil { return nil, err } return instance.Instance, nil } // Get will get the server with the given instanceID func (i *InstanceServiceHandler) Get(ctx context.Context, instanceID string) (*Instance, error) { uri := fmt.Sprintf("%s/%s", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } instance := new(instanceBase) if err = i.client.DoWithContext(ctx, req, instance); err != nil { return nil, err } return instance.Instance, nil } // Update will update the server with the given parameters func (i *InstanceServiceHandler) Update(ctx context.Context, instanceID string, instanceReq *InstanceUpdateReq) (*Instance, error) { uri := fmt.Sprintf("%s/%s", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPatch, uri, instanceReq) if err != nil { return nil, err } instance := new(instanceBase) if err := i.client.DoWithContext(ctx, req, instance); err != nil { return nil, err } return instance.Instance, nil } // Delete an instance. All data will be permanently lost, and the IP address will be released func (i *InstanceServiceHandler) Delete(ctx context.Context, instanceID string) error { uri := fmt.Sprintf("%s/%s", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // List all instances on your account. func (i *InstanceServiceHandler) List(ctx context.Context, options *ListOptions) ([]Instance, *Meta, error) { req, err := i.client.NewRequest(ctx, http.MethodGet, instancePath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() instances := new(instancesBase) if err = i.client.DoWithContext(ctx, req, instances); err != nil { return nil, nil, err } return instances.Instances, instances.Meta, nil } // Start will start a vps instance the machine is already running, it will be restarted. func (i *InstanceServiceHandler) Start(ctx context.Context, instanceID string) error { uri := fmt.Sprintf("%s/%s/start", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // Halt will pause an instance. func (i *InstanceServiceHandler) Halt(ctx context.Context, instanceID string) error { uri := fmt.Sprintf("%s/%s/halt", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // Reboot an instance. func (i *InstanceServiceHandler) Reboot(ctx context.Context, instanceID string) error { uri := fmt.Sprintf("%s/%s/reboot", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // Reinstall an instance. func (i *InstanceServiceHandler) Reinstall(ctx context.Context, instanceID string, reinstallReq *ReinstallReq) (*Instance, error) { uri := fmt.Sprintf("%s/%s/reinstall", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, reinstallReq) if err != nil { return nil, err } instance := new(instanceBase) if err := i.client.DoWithContext(ctx, req, instance); err != nil { return nil, err } return instance.Instance, nil } // MassStart will start a list of instances the machine is already running, it will be restarted. func (i *InstanceServiceHandler) MassStart(ctx context.Context, instanceList []string) error { uri := fmt.Sprintf("%s/start", instancePath) reqBody := RequestBody{"instance_ids": instanceList} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // MassHalt will pause a list of instances. func (i *InstanceServiceHandler) MassHalt(ctx context.Context, instanceList []string) error { uri := fmt.Sprintf("%s/halt", instancePath) reqBody := RequestBody{"instance_ids": instanceList} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // MassReboot reboots a list of instances. func (i *InstanceServiceHandler) MassReboot(ctx context.Context, instanceList []string) error { uri := fmt.Sprintf("%s/reboot", instancePath) reqBody := RequestBody{"instance_ids": instanceList} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // Restore an instance. func (i *InstanceServiceHandler) Restore(ctx context.Context, instanceID string, restoreReq *RestoreReq) error { uri := fmt.Sprintf("%s/%s/restore", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, restoreReq) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // GetBandwidth for a given instance. func (i *InstanceServiceHandler) GetBandwidth(ctx context.Context, instanceID string) (*Bandwidth, error) { uri := fmt.Sprintf("%s/%s/bandwidth", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } bandwidth := new(Bandwidth) if err = i.client.DoWithContext(ctx, req, bandwidth); err != nil { return nil, err } return bandwidth, nil } // GetNeighbors gets a list of other instances in the same location as this Instance. func (i *InstanceServiceHandler) GetNeighbors(ctx context.Context, instanceID string) (*Neighbors, error) { uri := fmt.Sprintf("%s/%s/neighbors", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } neighbors := new(Neighbors) if err = i.client.DoWithContext(ctx, req, neighbors); err != nil { return nil, err } return neighbors, nil } // ListPrivateNetworks currently attached to an instance. // Deprecated: ListPrivateNetworks should no longer be used. Instead, use ListVPCInfo func (i *InstanceServiceHandler) ListPrivateNetworks(ctx context.Context, instanceID string, options *ListOptions) ([]PrivateNetwork, *Meta, error) { uri := fmt.Sprintf("%s/%s/private-networks", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() networks := new(privateNetworksBase) if err = i.client.DoWithContext(ctx, req, networks); err != nil { return nil, nil, err } return networks.PrivateNetworks, networks.Meta, nil } // AttachPrivateNetwork to an instance // Deprecated: AttachPrivateNetwork should no longer be used. Instead, use AttachVPC func (i *InstanceServiceHandler) AttachPrivateNetwork(ctx context.Context, instanceID, networkID string) error { uri := fmt.Sprintf("%s/%s/private-networks/attach", instancePath, instanceID) body := RequestBody{"network_id": networkID} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, body) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // DetachPrivateNetwork from an instance. // Deprecated: DetachPrivateNetwork should no longer be used. Instead, use DetachVPC func (i *InstanceServiceHandler) DetachPrivateNetwork(ctx context.Context, instanceID, networkID string) error { uri := fmt.Sprintf("%s/%s/private-networks/detach", instancePath, instanceID) body := RequestBody{"network_id": networkID} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, body) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // ListVPCInfo currently attached to an instance. func (i *InstanceServiceHandler) ListVPCInfo(ctx context.Context, instanceID string, options *ListOptions) ([]VPCInfo, *Meta, error) { uri := fmt.Sprintf("%s/%s/vpcs", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() vpcs := new(vpcInfoBase) if err = i.client.DoWithContext(ctx, req, vpcs); err != nil { return nil, nil, err } return vpcs.VPCs, vpcs.Meta, nil } // AttachVPC to an instance func (i *InstanceServiceHandler) AttachVPC(ctx context.Context, instanceID, vpcID string) error { uri := fmt.Sprintf("%s/%s/vpcs/attach", instancePath, instanceID) body := RequestBody{"vpc_id": vpcID} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, body) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // DetachVPC from an instance. func (i *InstanceServiceHandler) DetachVPC(ctx context.Context, instanceID, vpcID string) error { uri := fmt.Sprintf("%s/%s/vpcs/detach", instancePath, instanceID) body := RequestBody{"vpc_id": vpcID} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, body) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // ISOStatus retrieves the current ISO state for a given VPS. // The returned state may be one of: ready | isomounting | isomounted. func (i *InstanceServiceHandler) ISOStatus(ctx context.Context, instanceID string) (*Iso, error) { uri := fmt.Sprintf("%s/%s/iso", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } iso := new(isoStatusBase) if err = i.client.DoWithContext(ctx, req, iso); err != nil { return nil, err } return iso.IsoStatus, nil } // AttachISO will attach an ISO to the given instance and reboot it func (i *InstanceServiceHandler) AttachISO(ctx context.Context, instanceID, isoID string) error { uri := fmt.Sprintf("%s/%s/iso/attach", instancePath, instanceID) body := RequestBody{"iso_id": isoID} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, body) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // DetachISO will detach the currently mounted ISO and reboot the instance. func (i *InstanceServiceHandler) DetachISO(ctx context.Context, instanceID string) error { uri := fmt.Sprintf("%s/%s/iso/detach", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // GetBackupSchedule retrieves the backup schedule for a given instance - all time values are in UTC func (i *InstanceServiceHandler) GetBackupSchedule(ctx context.Context, instanceID string) (*BackupSchedule, error) { uri := fmt.Sprintf("%s/%s/backup-schedule", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } backup := new(backupScheduleBase) if err = i.client.DoWithContext(ctx, req, backup); err != nil { return nil, err } return backup.BackupSchedule, nil } // SetBackupSchedule sets the backup schedule for a given instance - all time values are in UTC. func (i *InstanceServiceHandler) SetBackupSchedule(ctx context.Context, instanceID string, backup *BackupScheduleReq) error { uri := fmt.Sprintf("%s/%s/backup-schedule", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, backup) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // CreateIPv4 an additional IPv4 address for given instance. func (i *InstanceServiceHandler) CreateIPv4(ctx context.Context, instanceID string, reboot *bool) (*IPv4, error) { uri := fmt.Sprintf("%s/%s/ipv4", instancePath, instanceID) body := RequestBody{"reboot": reboot} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, body) if err != nil { return nil, err } ip := new(ipv4Base) if err = i.client.DoWithContext(ctx, req, ip); err != nil { return nil, err } return ip.IPv4, nil } // ListIPv4 addresses that are currently assigned to a given instance. func (i *InstanceServiceHandler) ListIPv4(ctx context.Context, instanceID string, options *ListOptions) ([]IPv4, *Meta, error) { uri := fmt.Sprintf("%s/%s/ipv4", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() ips := new(ipBase) if err = i.client.DoWithContext(ctx, req, ips); err != nil { return nil, nil, err } return ips.IPv4s, ips.Meta, nil } // DeleteIPv4 address from a given instance. func (i *InstanceServiceHandler) DeleteIPv4(ctx context.Context, instanceID, ip string) error { uri := fmt.Sprintf("%s/%s/ipv4/%s", instancePath, instanceID, ip) req, err := i.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // ListIPv6 addresses that are currently assigned to a given instance. func (i *InstanceServiceHandler) ListIPv6(ctx context.Context, instanceID string, options *ListOptions) ([]IPv6, *Meta, error) { uri := fmt.Sprintf("%s/%s/ipv6", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() ips := new(ipBase) if err = i.client.DoWithContext(ctx, req, ips); err != nil { return nil, nil, err } return ips.IPv6s, ips.Meta, nil } // CreateReverseIPv6 for a given instance. func (i *InstanceServiceHandler) CreateReverseIPv6(ctx context.Context, instanceID string, reverseReq *ReverseIP) error { uri := fmt.Sprintf("%s/%s/ipv6/reverse", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, reverseReq) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // ListReverseIPv6 currently assigned to a given instance. func (i *InstanceServiceHandler) ListReverseIPv6(ctx context.Context, instanceID string) ([]ReverseIP, error) { uri := fmt.Sprintf("%s/%s/ipv6/reverse", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } reverse := new(reverseIPv6sBase) if err = i.client.DoWithContext(ctx, req, reverse); err != nil { return nil, err } return reverse.ReverseIPv6s, nil } // DeleteReverseIPv6 a given reverse IPv6. func (i *InstanceServiceHandler) DeleteReverseIPv6(ctx context.Context, instanceID, ip string) error { uri := fmt.Sprintf("%s/%s/ipv6/reverse/%s", instancePath, instanceID, ip) req, err := i.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // CreateReverseIPv4 for a given IP on a given instance. func (i *InstanceServiceHandler) CreateReverseIPv4(ctx context.Context, instanceID string, reverseReq *ReverseIP) error { uri := fmt.Sprintf("%s/%s/ipv4/reverse", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodPost, uri, reverseReq) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // DefaultReverseIPv4 will set the IPs reverse setting back to the original one supplied by Vultr. func (i *InstanceServiceHandler) DefaultReverseIPv4(ctx context.Context, instanceID, ip string) error { uri := fmt.Sprintf("%s/%s/ipv4/reverse/default", instancePath, instanceID) reqBody := RequestBody{"ip": ip} req, err := i.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // GetUserData from given instance. The userdata returned will be in base64 encoding. func (i *InstanceServiceHandler) GetUserData(ctx context.Context, instanceID string) (*UserData, error) { uri := fmt.Sprintf("%s/%s/user-data", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } userData := new(userDataBase) if err = i.client.DoWithContext(ctx, req, userData); err != nil { return nil, err } return userData.UserData, nil } // GetUpgrades that are available for a given instance. func (i *InstanceServiceHandler) GetUpgrades(ctx context.Context, instanceID string) (*Upgrades, error) { uri := fmt.Sprintf("%s/%s/upgrades", instancePath, instanceID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } upgrades := new(upgradeBase) if err = i.client.DoWithContext(ctx, req, upgrades); err != nil { return nil, err } return upgrades.Upgrades, nil } golang-github-vultr-govultr-2.17.2/instance_test.go000066400000000000000000001130111425211421400223670ustar00rootroot00000000000000package govultr import ( "fmt" "io/ioutil" "net/http" "reflect" "testing" ) func TestServerServiceHandler_GetBackupSchedule(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/backup-schedule", func(writer http.ResponseWriter, request *http.Request) { response := `{"backup_schedule":{"enabled": true,"type": "weekly","next_scheduled_time_utc": "2016-05-07 08:00:00","hour": 8,"dow": 6,"dom": 0}}` fmt.Fprint(writer, response) }) backup, err := client.Instance.GetBackupSchedule(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.GetBackupSchedule returned %+v, ", err) } expected := &BackupSchedule{ Enabled: BoolToBoolPtr(true), Type: "weekly", NextScheduleTimeUTC: "2016-05-07 08:00:00", Hour: 8, Dow: 6, Dom: 0, } if !reflect.DeepEqual(backup, expected) { t.Errorf("Instance.GetBackupSchedule returned %+v, expected %+v", backup, expected) } } func TestServerServiceHandler_SetBackupSchedule(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/backup-schedule", func(writer http.ResponseWriter, request *http.Request) { response := `{"backup_schedule":{"enabled": true,"type": "weekly","next_scheduled_time_utc": "2016-05-07 08:00:00","hour": 22,"dow": 2,"dom": 3}}` fmt.Fprint(writer, response) }) bs := &BackupScheduleReq{ Type: "weekly", Hour: IntToIntPtr(22), Dow: IntToIntPtr(2), Dom: 3, } if err := client.Instance.SetBackupSchedule(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", bs); err != nil { t.Errorf("Instance.SetBackupSchedule returned %+v, ", err) } } func TestServerServiceHandler_RestoreBackup(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/restore", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) restoreReq := &RestoreReq{ BackupID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", } if err := client.Instance.Restore(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", restoreReq); err != nil { t.Errorf("Instance.Restore returned %+v, ", err) } } func TestServerServiceHandler_RestoreSnapshot(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/restore", func(writer http.ResponseWriter, request *http.Request) { body, err := ioutil.ReadAll(request.Body) if err != nil || len(body) == 0 { http.Error(writer, "can't read body", http.StatusBadRequest) return } fmt.Fprint(writer) }) restoreReq := &RestoreReq{ SnapshotID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", } if err := client.Instance.Restore(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", restoreReq); err != nil { t.Errorf("Instance.Restore returned %+v, ", err) } } func TestServerServiceHandler_Neighbors(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/neighbors", func(writer http.ResponseWriter, request *http.Request) { response := `{"neighbors":["14b3e7d6-ffb5-4994-8502-57fcd9db3b33","14b3e7d6-ffb5-4994-8502-57fcd9db3b33"]}` fmt.Fprint(writer, response) }) neighbors, err := client.Instance.GetNeighbors(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.Neighbors returned %+v, ", err) } expected := &Neighbors{ Neighbors: []string{"14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"}, } if !reflect.DeepEqual(neighbors, expected) { t.Errorf("Instance.Neighbors returned %+v, expected %+v", neighbors, expected) } } func TestServerServiceHandler_ListPrivateNetworks(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/private-networks", func(writer http.ResponseWriter, request *http.Request) { response := `{"private_networks": [{"network_id": "v1-net539626f0798d7","mac_address": "5a:02:00:00:24:e9","ip_address": "10.99.0.3"}],"meta":{"total":1,"links":{"next":"thisismycusror","prev":""}}}` fmt.Fprint(writer, response) }) options := &ListOptions{ PerPage: 1, Cursor: "", } privateNetwork, meta, err := client.Instance.ListPrivateNetworks(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", options) if err != nil { t.Errorf("Instance.ListPrivateNetworks return %+v, ", err) } expected := []PrivateNetwork{ { NetworkID: "v1-net539626f0798d7", MacAddress: "5a:02:00:00:24:e9", IPAddress: "10.99.0.3", }, } if !reflect.DeepEqual(privateNetwork, expected) { t.Errorf("Instance.ListPrivateNetworks returned %+v, expected %+v", privateNetwork, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Instance.ListPrivateNetworks meta returned %+v, expected %+v", meta, expectedMeta) } } func TestServerServiceHandler_ListVPCInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/vpcs", func(writer http.ResponseWriter, request *http.Request) { response := `{"vpcs": [{"id": "v1-net539626f0798d7","mac_address": "5a:02:00:00:24:e9","ip_address": "10.99.0.3"}],"meta":{"total":1,"links":{"next":"thisismycusror","prev":""}}}` fmt.Fprint(writer, response) }) options := &ListOptions{ PerPage: 1, Cursor: "", } vpc, meta, err := client.Instance.ListVPCInfo(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", options) if err != nil { t.Errorf("Instance.ListVPCInfo returned %+v, ", err) } expected := []VPCInfo{ { ID: "v1-net539626f0798d7", MacAddress: "5a:02:00:00:24:e9", IPAddress: "10.99.0.3", }, } if !reflect.DeepEqual(vpc, expected) { t.Errorf("Instance.ListVPCInfo returned %+v, expected %+v", vpc, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Instance.ListVPCInfo meta returned %+v, expected %+v", meta, expectedMeta) } } func TestServerServiceHandler_GetUserData(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/user-data", func(writer http.ResponseWriter, request *http.Request) { response := `{"user_data": {"data" : "ZWNobyBIZWxsbyBXb3JsZA=="}}` fmt.Fprint(writer, response) }) userData, err := client.Instance.GetUserData(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.GetUserData return %+v ", err) } expected := &UserData{Data: "ZWNobyBIZWxsbyBXb3JsZA=="} if !reflect.DeepEqual(userData, expected) { t.Errorf("Instance.GetUserData returned %+v, expected %+v", userData, expected) } } func TestServerServiceHandler_ListIPv4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv4", func(writer http.ResponseWriter, request *http.Request) { response := `{ "ipv4s": [{"ip": "123.123.123.123","netmask": "255.255.255.248","gateway": "123.123.123.1","type": "main_ip","reverse": "host1.example.com"}],"meta":{"total":1,"links":{"next":"thisismycusror","prev":""}}}` fmt.Fprint(writer, response) }) ipv4, meta, err := client.Instance.ListIPv4(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", nil) if err != nil { t.Errorf("Instance.ListIPv4 returned %+v", err) } expected := []IPv4{ { IP: "123.123.123.123", Netmask: "255.255.255.248", Gateway: "123.123.123.1", Type: "main_ip", Reverse: "host1.example.com", }, } if !reflect.DeepEqual(ipv4, expected) { t.Errorf("Instance.ListIPv4 returned %+v, expected %+v", ipv4, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Instance.ListIPv4 meta returned %+v, expected %+v", meta, expectedMeta) } } func TestServerServiceHandler_ListIPv6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv6", func(writer http.ResponseWriter, request *http.Request) { response := `{"ipv6s": [{"ip": "2001:DB8:1000::100","network": "2001:DB8:1000::","network_size": 64,"type": "main_ip"}],"meta":{"total":1,"links":{"next":"thisismycusror","prev":""}}}` fmt.Fprint(writer, response) }) ipv6, meta, err := client.Instance.ListIPv6(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", nil) if err != nil { t.Errorf("Instance.ListIPv6 returned %+v", err) } expected := []IPv6{ { IP: "2001:DB8:1000::100", Network: "2001:DB8:1000::", NetworkSize: 64, Type: "main_ip", }, } if !reflect.DeepEqual(ipv6, expected) { t.Errorf("Instance.ListIPv6 returned %+v, expected %+v", ipv6, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Instance.ListIPV6 meta returned %+v, expected %+v", meta, expectedMeta) } } func TestServerServiceHandler_CreateIPv4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv4", func(writer http.ResponseWriter, request *http.Request) { response := `{ "ipv4": {"ip": "123.123.123.123","netmask": "255.255.255.248","gateway": "123.123.123.1","type": "main_ip","reverse": "host1.example.com"}}` fmt.Fprint(writer, response) }) ipv4, err := client.Instance.CreateIPv4(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", BoolToBoolPtr(false)) if err != nil { t.Errorf("Instance.CreateIPv4 returned %+v", err) } expected := &IPv4{ IP: "123.123.123.123", Netmask: "255.255.255.248", Gateway: "123.123.123.1", Type: "main_ip", Reverse: "host1.example.com", } if !reflect.DeepEqual(ipv4, expected) { t.Errorf("Instance.CreateIPv4 returned %+v, expected %+v", ipv4, expected) } } func TestServerServiceHandler_DestroyIPV4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv4/192.168.0.1", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Instance.DeleteIPv4(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "192.168.0.1") if err != nil { t.Errorf("Instance.DestroyIPV4 returned %+v", err) } } func TestServerServiceHandler_GetBandwidth(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/bandwidth", func(writer http.ResponseWriter, request *http.Request) { response := ` { "bandwidth": { "2017-04-01": { "incoming_bytes": 91571055, "outgoing_bytes": 3084731 } } } ` fmt.Fprint(writer, response) }) bandwidth, err := client.Instance.GetBandwidth(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.GetBandwidth returned %+v", err) } expected := &Bandwidth{ Bandwidth: map[string]struct { IncomingBytes int `json:"incoming_bytes"` OutgoingBytes int `json:"outgoing_bytes"` }{ "2017-04-01": { IncomingBytes: 91571055, OutgoingBytes: 3084731, }, }, } if !reflect.DeepEqual(bandwidth, expected) { t.Errorf("Instance.GetBandwidth returned %+v, expected %+v", bandwidth, expected) } } func TestServerServiceHandler_ListReverseIPv6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv6/reverse", func(writer http.ResponseWriter, request *http.Request) { response := `{"reverse_ipv6s": [{"ip": "2001:DB8:1000::101","reverse": "host1.example.com"}]}` fmt.Fprint(writer, response) }) reverseIPV6, err := client.Instance.ListReverseIPv6(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.ListReverseIPv6 returned error: %v", err) } expected := []ReverseIP{ {IP: "2001:DB8:1000::101", Reverse: "host1.example.com"}, } if !reflect.DeepEqual(reverseIPV6, expected) { t.Errorf("Instance.ListReverseIPv6 returned %+v, expected %+v", reverseIPV6, expected) } } func TestServerServiceHandler_DefaultReverseIPv4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv4/reverse/default", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.DefaultReverseIPv4(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "129.123.123.1"); err != nil { t.Errorf("Instance.DefaultReverseIPv4 returned %+v", err) } } func TestServerServiceHandler_DeleteReverseIPv6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv6/reverse/2001:19f0:8001:1480:5400:2ff:fe00:8228", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.DeleteReverseIPv6(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "2001:19f0:8001:1480:5400:2ff:fe00:8228"); err != nil { t.Errorf("Instance.DeleteReverseIPv6 returned %+v", err) } } func TestServerServiceHandler_CreateReverseIPv4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv4/reverse", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) reverseReq := &ReverseIP{ IP: "192.168.0.1", Reverse: "test.com", } if err := client.Instance.CreateReverseIPv4(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", reverseReq); err != nil { t.Errorf("Instance.CreateReverseIPv4 returned %+v", err) } } func TestServerServiceHandler_CreateReverseIPv6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/ipv6/reverse", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) reverseReq := &ReverseIP{ IP: "192.168.0.1", Reverse: "test.com", } if err := client.Instance.CreateReverseIPv6(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", reverseReq); err != nil { t.Errorf("Instance.CreateReverseIPv6 returned %+v", err) } } func TestServerServiceHandler_Halt(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/halt", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.Halt(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"); err != nil { t.Errorf("Instance.Halt returned %+v", err) } } func TestServerServiceHandler_Start(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/start", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.Start(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"); err != nil { t.Errorf("Instance.Start returned %+v", err) } } func TestServerServiceHandler_Reboot(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/reboot", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Instance.Reboot(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.Reboot returned %+v", err) } } func TestServerServiceHandler_Reinstall(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/reinstall", func(writer http.ResponseWriter, request *http.Request) { response := `{ "instance": { "id": "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "os": "CentOS SELinux 8 x64", "ram": 2048, "disk": 60, "main_ip": "123.123.123.123", "vcpu_count": 2, "region": "ewr", "plan": "vc2-1c-2gb", "date_created": "2013-12-19 14:45:41", "status": "active", "allowed_bandwidth": 2000, "netmask_v4": "255.255.255.248", "gateway_v4": "123.123.123.1", "power_status": "running", "server_status": "ok", "v6_network": "2001:DB8:1000::", "v6_main_ip": "fd11:1111:1112:1c02:0200:00ff:fe00:0000", "v6_network_size": 64, "label": "my new server", "internal_ip": "10.99.0.10", "kvm": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", "default_password" : "nreqnusibni", "tags": ["my tag"], "os_id": 362, "app_id": 0, "firewall_group_id": "1234", "features": [ "auto_backups", "ipv6" ] } }` fmt.Fprint(writer, response) }) instanceRes, err := client.Instance.Reinstall(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", nil) if err != nil { t.Errorf("Instance.Reinstall returned %+v", err) } expected := &Instance{ ID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", Os: "CentOS SELinux 8 x64", OsID: 362, RAM: 2048, Disk: 60, MainIP: "123.123.123.123", VCPUCount: 2, Region: "ewr", DefaultPassword: "nreqnusibni", DateCreated: "2013-12-19 14:45:41", Status: "active", AllowedBandwidth: 2000, NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerStatus: "ok", Plan: "vc2-1c-2gb", V6Network: "2001:DB8:1000::", V6MainIP: "fd11:1111:1112:1c02:0200:00ff:fe00:0000", V6NetworkSize: 64, Label: "my new server", InternalIP: "10.99.0.10", KVM: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", Tags: []string{"my tag"}, AppID: 0, FirewallGroupID: "1234", Features: []string{"auto_backups", "ipv6"}, } if !reflect.DeepEqual(instanceRes, expected) { t.Errorf("Instance.Create returned %+v, expected %+v", server, expected) } } func TestServerServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Instance.Delete(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.Delete returned %+v", err) } } func TestServerServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances", func(writer http.ResponseWriter, request *http.Request) { response := `{ "instance": { "id": "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "os": "CentOS SELinux 8 x64", "ram": 2048, "disk": 60, "main_ip": "123.123.123.123", "vcpu_count": 2, "region": "ewr", "plan": "vc2-1c-2gb", "date_created": "2013-12-19 14:45:41", "status": "active", "allowed_bandwidth": 2000, "netmask_v4": "255.255.255.248", "gateway_v4": "123.123.123.1", "power_status": "running", "server_status": "ok", "v6_network": "2001:DB8:1000::", "v6_main_ip": "fd11:1111:1112:1c02:0200:00ff:fe00:0000", "v6_network_size": 64, "label": "my new server", "internal_ip": "10.99.0.10", "kvm": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", "default_password" : "nreqnusibni", "tags": ["my tag"], "os_id": 362, "app_id": 0, "firewall_group_id": "1234", "features": [ "auto_backups", "ipv6" ] } }` fmt.Fprint(writer, response) }) options := &InstanceCreateReq{ IPXEChainURL: "test.org", ISOID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", ScriptID: "213", EnableIPv6: BoolToBoolPtr(true), Backups: "enabled", UserData: "dW5vLWRvcy10cmVz", ActivationEmail: BoolToBoolPtr(true), DDOSProtection: BoolToBoolPtr(true), SnapshotID: "12ab", Hostname: "hostname-3000", Tags: []string{"my tag"}, Label: "label-extreme", SSHKeys: []string{"14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "dev-preview-abc124"}, ReservedIPv4: "63.209.35.79", FirewallGroupID: "1234", AppID: 1, } server, err := client.Instance.Create(ctx, options) if err != nil { t.Errorf("Instance.Create returned %+v", err) } features := []string{"auto_backups", "ipv6"} expected := &Instance{ ID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", Os: "CentOS SELinux 8 x64", OsID: 362, RAM: 2048, Disk: 60, MainIP: "123.123.123.123", VCPUCount: 2, Region: "ewr", DefaultPassword: "nreqnusibni", DateCreated: "2013-12-19 14:45:41", Status: "active", AllowedBandwidth: 2000, NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerStatus: "ok", Plan: "vc2-1c-2gb", V6Network: "2001:DB8:1000::", V6MainIP: "fd11:1111:1112:1c02:0200:00ff:fe00:0000", V6NetworkSize: 64, Label: "my new server", InternalIP: "10.99.0.10", KVM: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", Tags: []string{"my tag"}, AppID: 0, FirewallGroupID: "1234", Features: features, } if !reflect.DeepEqual(server, expected) { t.Errorf("Instance.Create returned %+v, expected %+v", server, expected) } } func TestServerServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances", func(writer http.ResponseWriter, request *http.Request) { response := `{ "instances": [{ "id": "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "os": "CentOS SELinux 8 x64", "ram": 2048, "disk": 60, "main_ip": "123.123.123.123", "vcpu_count": 2, "region": "ewr", "plan": "vc2-1c-2gb", "date_created": "2013-12-19 14:45:41", "status": "active", "allowed_bandwidth": 2000, "netmask_v4": "255.255.255.248", "gateway_v4": "123.123.123.1", "power_status": "running", "server_status": "ok", "v6_network": "2001:DB8:1000::", "v6_main_ip": "fd11:1111:1112:1c02:0200:00ff:fe00:0000", "v6_network_size": 64, "label": "my new server", "internal_ip": "10.99.0.10", "kvm": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", "default_password" : "nreqnusibni", "tags": ["my tag"], "os_id": 362, "app_id": 0, "firewall_group_id": "", "features": [ "auto_backups" ] }], "meta":{ "total":1, "links":{ "next":"thisismycusror", "prev":"" } } }` fmt.Fprint(writer, response) }) server, meta, err := client.Instance.List(ctx, nil) if err != nil { t.Errorf("Instance.List returned %+v", err) } features := []string{"auto_backups"} expected := []Instance{ { ID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", Os: "CentOS SELinux 8 x64", OsID: 362, RAM: 2048, Disk: 60, MainIP: "123.123.123.123", VCPUCount: 2, Region: "ewr", DefaultPassword: "nreqnusibni", DateCreated: "2013-12-19 14:45:41", Status: "active", AllowedBandwidth: 2000, NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerStatus: "ok", Plan: "vc2-1c-2gb", V6Network: "2001:DB8:1000::", V6MainIP: "fd11:1111:1112:1c02:0200:00ff:fe00:0000", V6NetworkSize: 64, Label: "my new server", InternalIP: "10.99.0.10", KVM: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", Tags: []string{"my tag"}, AppID: 0, FirewallGroupID: "", Features: features, }, } if !reflect.DeepEqual(server, expected) { t.Errorf("Instance.List returned %+v, expected %+v", server, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Instance.List meta returned %+v, expected %+v", meta, expectedMeta) } } func TestServerServiceHandler_GetServer(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33", func(writer http.ResponseWriter, request *http.Request) { response := `{ "instance": { "id": "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "os": "CentOS SELinux 8 x64", "ram": 2048, "disk": 60, "main_ip": "123.123.123.123", "vcpu_count": 2, "region": "ewr", "plan": "vc2-1c-2gb", "date_created": "2013-12-19 14:45:41", "status": "active", "allowed_bandwidth": 2000, "netmask_v4": "255.255.255.248", "gateway_v4": "123.123.123.1", "power_status": "running", "server_status": "ok", "v6_network": "2001:DB8:1000::", "v6_main_ip": "fd11:1111:1112:1c02:0200:00ff:fe00:0000", "v6_network_size": 64, "label": "my new server", "internal_ip": "10.99.0.10", "kvm": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", "default_password" : "nreqnusibni", "tags": ["my tag"], "os_id": 362, "app_id": 0, "firewall_group_id": "", "features": [ "auto_backups" ] } }` fmt.Fprint(writer, response) }) server, err := client.Instance.Get(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.GetServer returned %+v", err) } features := []string{"auto_backups"} expected := &Instance{ ID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", Os: "CentOS SELinux 8 x64", OsID: 362, RAM: 2048, Disk: 60, MainIP: "123.123.123.123", VCPUCount: 2, Region: "ewr", DefaultPassword: "nreqnusibni", DateCreated: "2013-12-19 14:45:41", Status: "active", AllowedBandwidth: 2000, NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerStatus: "ok", Plan: "vc2-1c-2gb", V6Network: "2001:DB8:1000::", V6MainIP: "fd11:1111:1112:1c02:0200:00ff:fe00:0000", V6NetworkSize: 64, Label: "my new server", InternalIP: "10.99.0.10", KVM: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", Tags: []string{"my tag"}, AppID: 0, FirewallGroupID: "", Features: features, } if !reflect.DeepEqual(server, expected) { t.Errorf("Instance.GetServer returned %+v, expected %+v", server, expected) } } func TestInstanceServiceHandler_GetUpgrades(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/upgrades", func(writer http.ResponseWriter, request *http.Request) { response := `{ "upgrades":{ "os":[ { "id":127, "name":"CentOS 6 x64", "arch":"x64", "family":"centos" } ], "applications":[ { "id":1, "name":"LEMP", "short_name":"lemp", "deploy_name":"LEMP on CentOS 6" } ], "plans":[ "vc2-2c-4gb" ] } }` fmt.Fprint(writer, response) }) server, err := client.Instance.GetUpgrades(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.GetUpgrades returned %+v", err) } expected := &Upgrades{ Applications: []Application{ { ID: 1, Name: "LEMP", ShortName: "lemp", DeployName: "LEMP on CentOS 6", }, }, OS: []OS{ { ID: 127, Name: "CentOS 6 x64", Arch: "x64", Family: "centos", }, }, Plans: []string{ "vc2-2c-4gb", }, } if !reflect.DeepEqual(server, expected) { t.Errorf("Instance.GetUpgrades returned %+v, expected %+v", server, expected) } } func TestServerServiceHandler_MassStart(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/start", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.MassStart(ctx, []string{"14b3e7d6-ffb5-4994-8502-57fcd9db3b33"}); err != nil { t.Errorf("Instance.MassStart returned %+v", err) } } func TestServerServiceHandler_MassReboot(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/reboot", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.MassReboot(ctx, []string{"14b3e7d6-ffb5-4994-8502-57fcd9db3b33"}); err != nil { t.Errorf("Instance.MassReboot returned %+v", err) } } func TestServerServiceHandler_MassHalt(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/halt", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.MassHalt(ctx, []string{"14b3e7d6-ffb5-4994-8502-57fcd9db3b33"}); err != nil { t.Errorf("Instance.MassHalt returned %+v", err) } } func TestServerServiceHandler_AttachPrivateNetwork(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/private-networks/attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.AttachPrivateNetwork(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"); err != nil { t.Errorf("Instance.AttachPrivateNetwork returned %+v", err) } } func TestServerServiceHandler_DetachPrivateNetwork(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/private-networks/detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.DetachPrivateNetwork(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"); err != nil { t.Errorf("Instance.DetachPrivateNetwork returned %+v", err) } } func TestServerServiceHandler_AttachVPC(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/vpcs/attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.AttachVPC(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"); err != nil { t.Errorf("Instance.AttachVPC returned %+v", err) } } func TestServerServiceHandler_DetachVPC(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/vpcs/detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.DetachVPC(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"); err != nil { t.Errorf("Instance.DetachVPC returned %+v", err) } } func TestServerServiceHandler_ISOAttach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/iso/attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.AttachISO(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"); err != nil { t.Errorf("Instance.AttachISO returned %+v", err) } } func TestServerServiceHandler_ISODetach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/iso/detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.Instance.DetachISO(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"); err != nil { t.Errorf("Instance.DetachISO returned %+v", err) } } func TestServerServiceHandler_ISO(t *testing.T) { setup() defer teardown() ret := `{"iso_status": {"state": "ready","iso_id": "0532a75b-14e8-48b8-b27e-1ebcf382a804"}}` mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33/iso", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer, ret) }) iso, err := client.Instance.ISOStatus(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Instance.ISOStatus returned %+v", err) } expected := &Iso{ State: "ready", IsoID: "0532a75b-14e8-48b8-b27e-1ebcf382a804", } if !reflect.DeepEqual(iso, expected) { t.Errorf("Instance.ISOStatus returned %+v, expected %+v", iso, expected) } } func TestServerServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances/14b3e7d6-ffb5-4994-8502-57fcd9db3b33", func(writer http.ResponseWriter, request *http.Request) { response := `{ "instance": { "id": "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "os": "CentOS SELinux 8 x64", "ram": 2048, "disk": 60, "main_ip": "123.123.123.123", "vcpu_count": 2, "region": "ewr", "plan": "vc2-1c-2gb", "date_created": "2013-12-19 14:45:41", "status": "active", "allowed_bandwidth": 2000, "netmask_v4": "255.255.255.248", "gateway_v4": "123.123.123.1", "power_status": "running", "server_status": "ok", "v6_network": "2001:DB8:1000::", "v6_main_ip": "fd11:1111:1112:1c02:0200:00ff:fe00:0000", "v6_network_size": 64, "label": "my new server", "internal_ip": "10.99.0.10", "kvm": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", "default_password" : "nreqnusibni", "tags": ["my tag"], "os_id": 362, "app_id": 0, "firewall_group_id": "1234", "features": [ "auto_backups", "ipv6" ] } }` fmt.Fprint(writer, response) }) options := &InstanceUpdateReq{ EnableIPv6: BoolToBoolPtr(true), Backups: "enabled", UserData: "dW5vLWRvcy10cmVz", DDOSProtection: BoolToBoolPtr(true), Tags: []string{"my tag"}, Label: "label-extreme", FirewallGroupID: "1234", AppID: 1, } server, err := client.Instance.Update(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", options) if err != nil { t.Errorf("Instance.Update returned %+v", err) } expected := &Instance{ ID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", Os: "CentOS SELinux 8 x64", OsID: 362, RAM: 2048, Disk: 60, MainIP: "123.123.123.123", VCPUCount: 2, Region: "ewr", DefaultPassword: "nreqnusibni", DateCreated: "2013-12-19 14:45:41", Status: "active", AllowedBandwidth: 2000, NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerStatus: "ok", Plan: "vc2-1c-2gb", V6Network: "2001:DB8:1000::", V6MainIP: "fd11:1111:1112:1c02:0200:00ff:fe00:0000", V6NetworkSize: 64, Label: "my new server", InternalIP: "10.99.0.10", KVM: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", Tags: []string{"my tag"}, AppID: 0, FirewallGroupID: "1234", Features: []string{"auto_backups", "ipv6"}, } if !reflect.DeepEqual(server, expected) { t.Errorf("Instance.Update returned %+v, expected %+v", server, expected) } } func TestServerServiceHandler_CreateMarketplace(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/instances", func(writer http.ResponseWriter, request *http.Request) { response := `{ "instance": { "id": "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "os": "CentOS SELinux 8 x64", "ram": 2048, "disk": 60, "main_ip": "123.123.123.123", "vcpu_count": 2, "region": "ewr", "plan": "vc2-1c-2gb", "date_created": "2013-12-19 14:45:41", "status": "active", "allowed_bandwidth": 2000, "netmask_v4": "255.255.255.248", "gateway_v4": "123.123.123.1", "power_status": "running", "server_status": "ok", "v6_network": "2001:DB8:1000::", "v6_main_ip": "fd11:1111:1112:1c02:0200:00ff:fe00:0000", "v6_network_size": 64, "label": "my new server", "internal_ip": "10.99.0.10", "kvm": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", "default_password" : "nreqnusibni", "tags": ["my tag"], "os_id": 362, "image_id": "test", "app_id": 0, "firewall_group_id": "1234", "features": [ "auto_backups", "ipv6" ] } }` fmt.Fprint(writer, response) }) options := &InstanceCreateReq{ IPXEChainURL: "test.org", ISOID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", ScriptID: "213", EnableIPv6: BoolToBoolPtr(true), Backups: "enabled", UserData: "dW5vLWRvcy10cmVz", ActivationEmail: BoolToBoolPtr(true), DDOSProtection: BoolToBoolPtr(true), SnapshotID: "12ab", Hostname: "hostname-3000", Tags: []string{"tagger"}, Label: "label-extreme", SSHKeys: []string{"14b3e7d6-ffb5-4994-8502-57fcd9db3b33", "dev-preview-abc124"}, ReservedIPv4: "63.209.35.79", FirewallGroupID: "1234", ImageID: "test", } server, err := client.Instance.Create(ctx, options) if err != nil { t.Errorf("Instance.Create returned %+v", err) } features := []string{"auto_backups", "ipv6"} expected := &Instance{ ID: "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", Os: "CentOS SELinux 8 x64", OsID: 362, RAM: 2048, Disk: 60, MainIP: "123.123.123.123", VCPUCount: 2, Region: "ewr", DefaultPassword: "nreqnusibni", DateCreated: "2013-12-19 14:45:41", Status: "active", AllowedBandwidth: 2000, NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerStatus: "ok", Plan: "vc2-1c-2gb", V6Network: "2001:DB8:1000::", V6MainIP: "fd11:1111:1112:1c02:0200:00ff:fe00:0000", V6NetworkSize: 64, Label: "my new server", InternalIP: "10.99.0.10", KVM: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", Tags: []string{"my tag"}, AppID: 0, ImageID: "test", FirewallGroupID: "1234", Features: features, } if !reflect.DeepEqual(server, expected) { t.Errorf("Instance.Create returned %+v, expected %+v", server, expected) } } golang-github-vultr-govultr-2.17.2/ip.go000066400000000000000000000011441425211421400201370ustar00rootroot00000000000000package govultr // IPv4 struct type IPv4 struct { IP string `json:"ip,omitempty"` Netmask string `json:"netmask,omitempty"` Gateway string `json:"gateway,omitempty"` Type string `json:"type,omitempty"` Reverse string `json:"reverse,omitempty"` } // IPv6 struct type IPv6 struct { IP string `json:"ip,omitempty"` Network string `json:"network,omitempty"` NetworkSize int `json:"network_size,omitempty"` Type string `json:"type,omitempty"` } type ipBase struct { IPv4s []IPv4 `json:"ipv4s,omitempty"` IPv6s []IPv6 `json:"ipv6s,omitempty"` Meta *Meta `json:"meta"` } golang-github-vultr-govultr-2.17.2/iso.go000066400000000000000000000074771425211421400203400ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // ISOService is the interface to interact with the ISO endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/iso type ISOService interface { Create(ctx context.Context, isoReq *ISOReq) (*ISO, error) Get(ctx context.Context, isoID string) (*ISO, error) Delete(ctx context.Context, isoID string) error List(ctx context.Context, options *ListOptions) ([]ISO, *Meta, error) ListPublic(ctx context.Context, options *ListOptions) ([]PublicISO, *Meta, error) } // ISOServiceHandler handles interaction with the ISO methods for the Vultr API type ISOServiceHandler struct { client *Client } // ISO represents ISOs currently available on this account. type ISO struct { ID string `json:"id"` DateCreated string `json:"date_created"` FileName string `json:"filename"` Size int `json:"size,omitempty"` MD5Sum string `json:"md5sum,omitempty"` SHA512Sum string `json:"sha512sum,omitempty"` Status string `json:"status"` } // PublicISO represents public ISOs offered in the Vultr ISO library. type PublicISO struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` MD5Sum string `json:"md5sum,omitempty"` } // ISOReq is used for creating ISOs. type ISOReq struct { URL string `json:"url"` } type isosBase struct { ISOs []ISO `json:"isos"` Meta *Meta `json:"meta"` } type isoBase struct { ISO *ISO `json:"iso"` } type publicIsosBase struct { PublicIsos []PublicISO `json:"public_isos"` Meta *Meta `json:"meta"` } // Create will create a new ISO image on your account func (i *ISOServiceHandler) Create(ctx context.Context, isoReq *ISOReq) (*ISO, error) { uri := "/v2/iso" req, err := i.client.NewRequest(ctx, http.MethodPost, uri, isoReq) if err != nil { return nil, err } iso := new(isoBase) if err = i.client.DoWithContext(ctx, req, iso); err != nil { return nil, err } return iso.ISO, nil } // Get an ISO func (i *ISOServiceHandler) Get(ctx context.Context, isoID string) (*ISO, error) { uri := fmt.Sprintf("/v2/iso/%s", isoID) req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } iso := new(isoBase) if err := i.client.DoWithContext(ctx, req, iso); err != nil { return nil, err } return iso.ISO, nil } // Delete will delete an ISO image from your account func (i *ISOServiceHandler) Delete(ctx context.Context, isoID string) error { uri := fmt.Sprintf("/v2/iso/%s", isoID) req, err := i.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return i.client.DoWithContext(ctx, req, nil) } // List will list all ISOs currently available on your account func (i *ISOServiceHandler) List(ctx context.Context, options *ListOptions) ([]ISO, *Meta, error) { uri := "/v2/iso" req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() iso := new(isosBase) if err = i.client.DoWithContext(ctx, req, iso); err != nil { return nil, nil, err } return iso.ISOs, iso.Meta, nil } // ListPublic will list public ISOs offered in the Vultr ISO library. func (i *ISOServiceHandler) ListPublic(ctx context.Context, options *ListOptions) ([]PublicISO, *Meta, error) { uri := "/v2/iso-public" req, err := i.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() iso := new(publicIsosBase) if err = i.client.DoWithContext(ctx, req, iso); err != nil { return nil, nil, err } return iso.PublicIsos, iso.Meta, nil } golang-github-vultr-govultr-2.17.2/iso_test.go000066400000000000000000000076431425211421400213720ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestIsoServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/iso", func(writer http.ResponseWriter, request *http.Request) { response := `{"iso":{"id":"9931","date_created":"2020-07-0917:15:27","filename":"CentOS-8.1.1911-x86_64-dvd1.iso","status":"pending"}}` fmt.Fprint(writer, response) }) isoReq := &ISOReq{URL: "http://centos.com/CentOS-8.1.1911-x86_64-dvd1.iso"} iso, err := client.ISO.Create(ctx, isoReq) if err != nil { t.Errorf("Iso.Create returned %+v, expected %+v", err, nil) } expected := &ISO{ ID: "9931", DateCreated: "2020-07-0917:15:27", FileName: "CentOS-8.1.1911-x86_64-dvd1.iso", Size: 0, MD5Sum: "", SHA512Sum: "", Status: "pending", } if !reflect.DeepEqual(iso, expected) { t.Errorf("Iso.Create returned %+v, expected %+v", iso, expected) } } func TestIsoServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/iso/9931", func(writer http.ResponseWriter, request *http.Request) { response := `{"iso":{"id":"9931","date_created":"2020-07-0917:15:27","filename":"CentOS-8.1.1911-x86_64-dvd1.iso","status":"pending"}}` fmt.Fprint(writer, response) }) iso, err := client.ISO.Get(ctx, "9931") if err != nil { t.Errorf("Iso.Get returned %+v, expected %+v", err, nil) } expected := &ISO{ ID: "9931", DateCreated: "2020-07-0917:15:27", FileName: "CentOS-8.1.1911-x86_64-dvd1.iso", Size: 0, MD5Sum: "", SHA512Sum: "", Status: "pending", } if !reflect.DeepEqual(iso, expected) { t.Errorf("Iso.Get returned %+v, expected %+v", iso, expected) } } func TestIsoServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/iso/24", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ISO.Delete(ctx, "24") if err != nil { t.Errorf("Iso.Delete returned %+v, expected %+v", err, nil) } } func TestIsoServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/iso", func(writer http.ResponseWriter, request *http.Request) { response := `{"isos":[{"id":"9931","date_created":"2020-07-0917:15:27","filename":"CentOS-8.1.1911-x86_64-dvd1.iso","status":"pending"}],"meta":{"total":8,"links":{"next":"","prev":""}}}` fmt.Fprint(writer, response) }) iso, meta, err := client.ISO.List(ctx, nil) if err != nil { t.Errorf("Iso.List returned %+v, expected %+v", err, nil) } expectedIso := []ISO{ { ID: "9931", DateCreated: "2020-07-0917:15:27", FileName: "CentOS-8.1.1911-x86_64-dvd1.iso", Size: 0, MD5Sum: "", SHA512Sum: "", Status: "pending", }, } expectedMeta := &Meta{ Total: 8, Links: &Links{}, } if !reflect.DeepEqual(iso, expectedIso) { t.Errorf("Iso.List iso returned %+v, expected %+v", iso, expectedIso) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Iso.List returned %+v, expected %+v", meta, expectedMeta) } } func TestIsoServiceHandler_ListPublic(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/iso-public", func(writer http.ResponseWriter, request *http.Request) { response := `{"public_isos": [{"id": "204515","name": "CentOS 7","description": "7 x86_64 Minimal"}],"meta":{"total":8,"links":{"next":"","prev":""}}}` fmt.Fprint(writer, response) }) iso, meta, err := client.ISO.ListPublic(ctx, nil) if err != nil { t.Errorf("Iso.ListPublic returned %+v, expected %+v", err, nil) } expectedIso := []PublicISO{ {ID: "204515", Name: "CentOS 7", Description: "7 x86_64 Minimal"}, } expectedMeta := &Meta{ Total: 8, Links: &Links{}, } if !reflect.DeepEqual(iso, expectedIso) { t.Errorf("Iso.ListPublic iso returned %+v, expected %+v", iso, expectedIso) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Iso.ListPublic meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/kubernetes.go000066400000000000000000000303141425211421400216770ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const vkePath = "/v2/kubernetes/clusters" // KubernetesService is the interface to interact with kubernetes endpoint on the Vultr API // Link : https://www.vultr.com/api/#tag/kubernetes type KubernetesService interface { CreateCluster(ctx context.Context, createReq *ClusterReq) (*Cluster, error) GetCluster(ctx context.Context, id string) (*Cluster, error) ListClusters(ctx context.Context, options *ListOptions) ([]Cluster, *Meta, error) UpdateCluster(ctx context.Context, vkeID string, updateReq *ClusterReqUpdate) error DeleteCluster(ctx context.Context, id string) error DeleteClusterWithResources(ctx context.Context, id string) error CreateNodePool(ctx context.Context, vkeID string, nodePoolReq *NodePoolReq) (*NodePool, error) ListNodePools(ctx context.Context, vkeID string, options *ListOptions) ([]NodePool, *Meta, error) GetNodePool(ctx context.Context, vkeID, nodePoolID string) (*NodePool, error) UpdateNodePool(ctx context.Context, vkeID, nodePoolID string, updateReq *NodePoolReqUpdate) (*NodePool, error) DeleteNodePool(ctx context.Context, vkeID, nodePoolID string) error DeleteNodePoolInstance(ctx context.Context, vkeID, nodePoolID, nodeID string) error RecycleNodePoolInstance(ctx context.Context, vkeID, nodePoolID, nodeID string) error GetKubeConfig(ctx context.Context, vkeID string) (*KubeConfig, error) GetVersions(ctx context.Context) (*Versions, error) GetUpgrades(ctx context.Context, vkeID string) ([]string, error) Upgrade(ctx context.Context, vkeID string, body *ClusterUpgradeReq) error } // KubernetesHandler handles interaction with the kubernetes methods for the Vultr API type KubernetesHandler struct { client *Client } // Cluster represents a full VKE cluster type Cluster struct { ID string `json:"id"` Label string `json:"label"` DateCreated string `json:"date_created"` ClusterSubnet string `json:"cluster_subnet"` ServiceSubnet string `json:"service_subnet"` IP string `json:"ip"` Endpoint string `json:"endpoint"` Version string `json:"version"` Region string `json:"region"` Status string `json:"status"` NodePools []NodePool `json:"node_pools"` } // NodePool represents a pool of nodes that are grouped by their label and plan type type NodePool struct { ID string `json:"id"` DateCreated string `json:"date_created"` DateUpdated string `json:"date_updated"` Label string `json:"label"` Plan string `json:"plan"` Status string `json:"status"` NodeQuantity int `json:"node_quantity"` MinNodes int `json:"min_nodes"` MaxNodes int `json:"max_nodes"` AutoScaler bool `json:"auto_scaler"` Tag string `json:"tag"` Nodes []Node `json:"nodes"` } // Node represents a node that will live within a nodepool type Node struct { ID string `json:"id"` DateCreated string `json:"date_created"` Label string `json:"label"` Status string `json:"status"` } // KubeConfig will contain the kubeconfig b64 encoded type KubeConfig struct { KubeConfig string `json:"kube_config"` } // ClusterReq struct used to create a cluster type ClusterReq struct { Label string `json:"label"` Region string `json:"region"` Version string `json:"version"` NodePools []NodePoolReq `json:"node_pools"` } // ClusterReqUpdate struct used to update update a cluster type ClusterReqUpdate struct { Label string `json:"label"` } // NodePoolReq struct used to create a node pool type NodePoolReq struct { NodeQuantity int `json:"node_quantity"` Label string `json:"label"` Plan string `json:"plan"` Tag string `json:"tag"` MinNodes int `json:"min_nodes,omitempty"` MaxNodes int `json:"max_nodes,omitempty"` AutoScaler *bool `json:"auto_scaler"` } // NodePoolReqUpdate struct used to update a node pool type NodePoolReqUpdate struct { NodeQuantity int `json:"node_quantity,omitempty"` Tag *string `json:"tag,omitempty"` MinNodes int `json:"min_nodes,omitempty"` MaxNodes int `json:"max_nodes,omitempty"` AutoScaler *bool `json:"auto_scaler,omitempty"` } type vkeClustersBase struct { VKEClusters []Cluster `json:"vke_clusters"` Meta *Meta `json:"meta"` } type vkeClusterBase struct { VKECluster *Cluster `json:"vke_cluster"` } type vkeNodePoolsBase struct { NodePools []NodePool `json:"node_pools"` Meta *Meta `json:"meta"` } type vkeNodePoolBase struct { NodePool *NodePool `json:"node_pool"` } // Versions that are supported for VKE type Versions struct { Versions []string `json:"versions"` } // AvailableUpgrades for a given VKE cluster type availableUpgrades struct { AvailableUpgrades []string `json:"available_upgrades"` } // ClusterUpgradeReq struct for vke upgradse type ClusterUpgradeReq struct { UpgradeVersion string `json:"upgrade_version,omitempty"` } // CreateCluster will create a Kubernetes cluster. func (k *KubernetesHandler) CreateCluster(ctx context.Context, createReq *ClusterReq) (*Cluster, error) { req, err := k.client.NewRequest(ctx, http.MethodPost, vkePath, createReq) if err != nil { return nil, err } var k8 = new(vkeClusterBase) if err = k.client.DoWithContext(ctx, req, &k8); err != nil { return nil, err } return k8.VKECluster, nil } // GetCluster will return a Kubernetes cluster. func (k *KubernetesHandler) GetCluster(ctx context.Context, id string) (*Cluster, error) { req, err := k.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s", vkePath, id), nil) if err != nil { return nil, err } k8 := new(vkeClusterBase) if err = k.client.DoWithContext(ctx, req, &k8); err != nil { return nil, err } return k8.VKECluster, nil } // ListClusters will return all kubernetes clusters. func (k *KubernetesHandler) ListClusters(ctx context.Context, options *ListOptions) ([]Cluster, *Meta, error) { req, err := k.client.NewRequest(ctx, http.MethodGet, vkePath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() k8s := new(vkeClustersBase) if err = k.client.DoWithContext(ctx, req, &k8s); err != nil { return nil, nil, err } return k8s.VKEClusters, k8s.Meta, nil } // UpdateCluster updates label on VKE cluster func (k *KubernetesHandler) UpdateCluster(ctx context.Context, vkeID string, updateReq *ClusterReqUpdate) error { req, err := k.client.NewRequest(ctx, http.MethodPut, fmt.Sprintf("%s/%s", vkePath, vkeID), updateReq) if err != nil { return err } return k.client.DoWithContext(ctx, req, nil) } // DeleteCluster will delete a Kubernetes cluster. func (k *KubernetesHandler) DeleteCluster(ctx context.Context, id string) error { req, err := k.client.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/%s", vkePath, id), nil) if err != nil { return err } return k.client.DoWithContext(ctx, req, nil) } // DeleteClusterWithResources will delete a Kubernetes cluster and all related resources. func (k *KubernetesHandler) DeleteClusterWithResources(ctx context.Context, id string) error { req, err := k.client.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/%s/delete-with-linked-resources", vkePath, id), nil) if err != nil { return err } return k.client.DoWithContext(ctx, req, nil) } // CreateNodePool creates a nodepool on a VKE cluster func (k *KubernetesHandler) CreateNodePool(ctx context.Context, vkeID string, nodePoolReq *NodePoolReq) (*NodePool, error) { req, err := k.client.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/%s/node-pools", vkePath, vkeID), nodePoolReq) if err != nil { return nil, err } n := new(vkeNodePoolBase) err = k.client.DoWithContext(ctx, req, n) if err != nil { return nil, err } return n.NodePool, nil } // ListNodePools will return all nodepools for a given VKE cluster func (k *KubernetesHandler) ListNodePools(ctx context.Context, vkeID string, options *ListOptions) ([]NodePool, *Meta, error) { req, err := k.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/node-pools", vkePath, vkeID), nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() n := new(vkeNodePoolsBase) if err = k.client.DoWithContext(ctx, req, &n); err != nil { return nil, nil, err } return n.NodePools, n.Meta, nil } // GetNodePool will return a single nodepool func (k *KubernetesHandler) GetNodePool(ctx context.Context, vkeID, nodePoolID string) (*NodePool, error) { req, err := k.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/node-pools/%s", vkePath, vkeID, nodePoolID), nil) if err != nil { return nil, err } n := new(vkeNodePoolBase) if err = k.client.DoWithContext(ctx, req, &n); err != nil { return nil, err } return n.NodePool, nil } // UpdateNodePool will allow you change the quantity of nodes within a nodepool func (k *KubernetesHandler) UpdateNodePool(ctx context.Context, vkeID, nodePoolID string, updateReq *NodePoolReqUpdate) (*NodePool, error) { req, err := k.client.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s/%s/node-pools/%s", vkePath, vkeID, nodePoolID), updateReq) if err != nil { return nil, err } np := new(vkeNodePoolBase) if err = k.client.DoWithContext(ctx, req, np); err != nil { return nil, err } return np.NodePool, nil } // DeleteNodePool will remove a nodepool from a VKE cluster func (k *KubernetesHandler) DeleteNodePool(ctx context.Context, vkeID, nodePoolID string) error { req, err := k.client.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/%s/node-pools/%s", vkePath, vkeID, nodePoolID), nil) if err != nil { return err } return k.client.DoWithContext(ctx, req, nil) } // DeleteNodePoolInstance will remove a specified node from a nodepool func (k *KubernetesHandler) DeleteNodePoolInstance(ctx context.Context, vkeID, nodePoolID, nodeID string) error { req, err := k.client.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/%s/node-pools/%s/nodes/%s", vkePath, vkeID, nodePoolID, nodeID), nil) if err != nil { return err } return k.client.DoWithContext(ctx, req, nil) } // RecycleNodePoolInstance will recycle (destroy + redeploy) a given node on a nodepool func (k *KubernetesHandler) RecycleNodePoolInstance(ctx context.Context, vkeID, nodePoolID, nodeID string) error { req, err := k.client.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/%s/node-pools/%s/nodes/%s/recycle", vkePath, vkeID, nodePoolID, nodeID), nil) if err != nil { return err } return k.client.DoWithContext(ctx, req, nil) } // GetKubeConfig returns the kubeconfig for the specified VKE cluster func (k *KubernetesHandler) GetKubeConfig(ctx context.Context, vkeID string) (*KubeConfig, error) { req, err := k.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/config", vkePath, vkeID), nil) if err != nil { return nil, err } kc := new(KubeConfig) if err = k.client.DoWithContext(ctx, req, &kc); err != nil { return nil, err } return kc, nil } // GetVersions returns the supported kubernetes versions func (k *KubernetesHandler) GetVersions(ctx context.Context) (*Versions, error) { uri := "/v2/kubernetes/versions" req, err := k.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } versions := new(Versions) if err = k.client.DoWithContext(ctx, req, &versions); err != nil { return nil, err } return versions, nil } // GetUpgrades returns all version a VKE cluster can upgrade to func (k *KubernetesHandler) GetUpgrades(ctx context.Context, vkeID string) ([]string, error) { req, err := k.client.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s/%s/available-upgrades", vkePath, vkeID), nil) if err != nil { return nil, err } upgrades := new(availableUpgrades) if err = k.client.DoWithContext(ctx, req, &upgrades); err != nil { return nil, err } return upgrades.AvailableUpgrades, nil } // Upgrade beings a VKE cluster upgrade func (k *KubernetesHandler) Upgrade(ctx context.Context, vkeID string, body *ClusterUpgradeReq) error { req, err := k.client.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/%s/upgrades", vkePath, vkeID), body) if err != nil { return err } return k.client.DoWithContext(ctx, req, nil) } golang-github-vultr-govultr-2.17.2/kubernetes_test.go000066400000000000000000000545211425211421400227440ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "reflect" "testing" "time" ) func TestKubernetesHandler_CreateCluster(t *testing.T) { setup() defer teardown() mux.HandleFunc(vkePath, func(writer http.ResponseWriter, request *http.Request) { response := `{ "vke_cluster": { "id": "014da059-21e3-47eb-acb5-91bf697c31aa", "label": "vke", "date_created": "2021-07-13T14:20:16+00:00", "cluster_subnet": "10.244.0.0/16", "service_subnet": "10.96.0.0/12", "ip": "0.0.0.0", "endpoint": "014da059-21e3-47eb-acb5-91bf697c31aa.vultr-k8s.com", "version": "1.20", "region": "lax", "status": "pending", "node_pools": [ { "id": "e1c7a313-e42d-43bb-82ef-4f287639b303", "date_created": "2021-07-13T14:20:16+00:00", "label": "my-label-48957292", "plan": "vc2-1c-2gb", "status": "pending", "node_quantity": 1, "min_nodes": 1, "max_nodes": 2, "auto_scaler": true, "nodes": [ { "id": "38364f79-17e3-4f1f-b7df-d9494bce0e4a", "label": "my-label-48957292-fef60eda12071", "date_created": "2021-07-13T14:20:16+00:00", "status": "pending" } ] } ] } }` fmt.Fprintf(writer, response) }) createReq := &ClusterReq{ Label: "vke", Region: "lax", Version: "1.20", NodePools: nil, } vke, err := client.Kubernetes.CreateCluster(ctx, createReq) if err != nil { t.Errorf("Kubernetes.CreateCluster returned %v", err) } expected := &Cluster{ ID: "014da059-21e3-47eb-acb5-91bf697c31aa", Label: "vke", DateCreated: "2021-07-13T14:20:16+00:00", ClusterSubnet: "10.244.0.0/16", ServiceSubnet: "10.96.0.0/12", IP: "0.0.0.0", Endpoint: "014da059-21e3-47eb-acb5-91bf697c31aa.vultr-k8s.com", Version: "1.20", Region: "lax", Status: "pending", NodePools: []NodePool{ { ID: "e1c7a313-e42d-43bb-82ef-4f287639b303", DateCreated: "2021-07-13T14:20:16+00:00", Label: "my-label-48957292", Plan: "vc2-1c-2gb", Status: "pending", NodeQuantity: 1, MinNodes: 1, MaxNodes: 2, AutoScaler: true, Nodes: []Node{ { ID: "38364f79-17e3-4f1f-b7df-d9494bce0e4a", DateCreated: "2021-07-13T14:20:16+00:00", Label: "my-label-48957292-fef60eda12071", Status: "pending", }, }, }, }, } if !reflect.DeepEqual(vke, expected) { t.Errorf("Kubernetes.CreateCluster returned %+v, expected %+v", vke, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, err = client.Kubernetes.CreateCluster(c, createReq) if err == nil { t.Error("Kubernetes.CreateCluster returned nil") } } func TestKubernetesHandler_GetCluster(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s", vkePath, "014da059-21e3-47eb-acb5-91bf697c31aa"), func(writer http.ResponseWriter, request *http.Request) { response := `{ "vke_cluster": { "id": "014da059-21e3-47eb-acb5-91bf697c31aa", "label": "vke", "date_created": "2021-07-13T14:20:16+00:00", "cluster_subnet": "10.244.0.0/16", "service_subnet": "10.96.0.0/12", "ip": "0.0.0.0", "endpoint": "014da059-21e3-47eb-acb5-91bf697c31aa.vultr-k8s.com", "version": "1.20", "region": "lax", "status": "pending", "node_pools": [ { "id": "e1c7a313-e42d-43bb-82ef-4f287639b303", "date_created": "2021-07-13T14:20:16+00:00", "label": "my-label-48957292", "plan": "vc2-1c-2gb", "status": "pending", "node_quantity": 1, "min_nodes": 1, "max_nodes": 2, "auto_scaler": true, "nodes": [ { "id": "38364f79-17e3-4f1f-b7df-d9494bce0e4a", "label": "my-label-48957292-fef60eda12071", "date_created": "2021-07-13T14:20:16+00:00", "status": "pending" } ] } ] } }` fmt.Fprintf(writer, response) }) vke, err := client.Kubernetes.GetCluster(ctx, "014da059-21e3-47eb-acb5-91bf697c31aa") if err != nil { t.Errorf("Kubernetes.GetCluster returned %v", err) } expected := &Cluster{ ID: "014da059-21e3-47eb-acb5-91bf697c31aa", Label: "vke", DateCreated: "2021-07-13T14:20:16+00:00", ClusterSubnet: "10.244.0.0/16", ServiceSubnet: "10.96.0.0/12", IP: "0.0.0.0", Endpoint: "014da059-21e3-47eb-acb5-91bf697c31aa.vultr-k8s.com", Version: "1.20", Region: "lax", Status: "pending", NodePools: []NodePool{ { ID: "e1c7a313-e42d-43bb-82ef-4f287639b303", DateCreated: "2021-07-13T14:20:16+00:00", Label: "my-label-48957292", Plan: "vc2-1c-2gb", Status: "pending", NodeQuantity: 1, MinNodes: 1, MaxNodes: 2, AutoScaler: true, Nodes: []Node{ { ID: "38364f79-17e3-4f1f-b7df-d9494bce0e4a", DateCreated: "2021-07-13T14:20:16+00:00", Label: "my-label-48957292-fef60eda12071", Status: "pending", }, }, }, }, } if !reflect.DeepEqual(vke, expected) { t.Errorf("Kubernetes.GetCluster returned %+v, expected %+v", vke, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, err = client.Kubernetes.GetCluster(c, "014da059-21e3-47eb-acb5-91bf697c31aa") if err == nil { t.Error("Kubernetes.GetCluster returned nil") } } func TestKubernetesHandler_ListClusters(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s", vkePath), func(writer http.ResponseWriter, request *http.Request) { response := `{ "vke_clusters": [{ "id": "014da059-21e3-47eb-acb5-91bf697c31aa", "label": "vke", "date_created": "2021-07-13T14:20:16+00:00", "cluster_subnet": "10.244.0.0/16", "service_subnet": "10.96.0.0/12", "ip": "0.0.0.0", "endpoint": "014da059-21e3-47eb-acb5-91bf697c31aa.vultr-k8s.com", "version": "1.20", "region": "lax", "status": "pending", "node_pools": [ { "id": "e1c7a313-e42d-43bb-82ef-4f287639b303", "date_created": "2021-07-13T14:20:16+00:00", "label": "my-label-48957292", "plan": "vc2-1c-2gb", "status": "pending", "tag": "mytag", "node_quantity": 1, "min_nodes": 1, "max_nodes": 2, "auto_scaler": true, "nodes": [ { "id": "38364f79-17e3-4f1f-b7df-d9494bce0e4a", "label": "my-label-48957292-fef60eda12071", "date_created": "2021-07-13T14:20:16+00:00", "status": "pending" } ] } ] } ], "meta": { "total": 1, "links": { "next": "thisismycusror", "prev": "" } } }` fmt.Fprintf(writer, response) }) vke, meta, err := client.Kubernetes.ListClusters(ctx, nil) if err != nil { t.Errorf("Kubernetes.ListClusters returned %v", err) } expected := []Cluster{ { ID: "014da059-21e3-47eb-acb5-91bf697c31aa", Label: "vke", DateCreated: "2021-07-13T14:20:16+00:00", ClusterSubnet: "10.244.0.0/16", ServiceSubnet: "10.96.0.0/12", IP: "0.0.0.0", Endpoint: "014da059-21e3-47eb-acb5-91bf697c31aa.vultr-k8s.com", Version: "1.20", Region: "lax", Status: "pending", NodePools: []NodePool{ { ID: "e1c7a313-e42d-43bb-82ef-4f287639b303", DateCreated: "2021-07-13T14:20:16+00:00", Label: "my-label-48957292", Plan: "vc2-1c-2gb", Status: "pending", Tag: "mytag", NodeQuantity: 1, MinNodes: 1, MaxNodes: 2, AutoScaler: true, Nodes: []Node{ { ID: "38364f79-17e3-4f1f-b7df-d9494bce0e4a", DateCreated: "2021-07-13T14:20:16+00:00", Label: "my-label-48957292-fef60eda12071", Status: "pending", }, }, }, }, }, } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(vke, expected) { t.Errorf("Kubernetes.List returned %+v, expected %+v", vke, expected) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Kubernetes.List meta returned %+v, expected %+v", vke, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, _, err = client.Kubernetes.ListClusters(c, nil) if err == nil { t.Error("Kubernetes.ListClusters returned nil") } } func TestKubernetesHandler_UpdateCluster(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s", vkePath, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"), func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) update := ClusterReqUpdate{Label: "new label"} err := client.Kubernetes.UpdateCluster(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33", &update) if err != nil { t.Errorf("Kubernetes.UpdateCluster returned %+v", err) } } func TestKubernetesHandler_DeleteCluster(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s", vkePath, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"), func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Kubernetes.DeleteCluster(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Kubernetes.DeleteCluster returned %+v", err) } } func TestKubernetesHandler_DeleteClusterWithResources(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s/delete-with-linked-resources", vkePath, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33"), func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Kubernetes.DeleteClusterWithResources(ctx, "14b3e7d6-ffb5-4994-8502-57fcd9db3b33") if err != nil { t.Errorf("Kubernetes.DeleteClusterWithResources returned %+v", err) } } func TestKubernetesHandler_CreateNodePool(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s/node-pools", vkePath, "1"), func(writer http.ResponseWriter, request *http.Request) { response := `{ "node_pool": { "id": "554e7248-705a-5862-516f-4f4a6735346a", "date_created": "2021-07-13T15:42:21+00:00", "label": "nodepool-48959140", "plan": "vc2-1c-2gb", "status": "pending", "node_quantity": 1, "min_nodes": 1, "max_nodes": 2, "auto_scaler": true, "tag": "mytag", "nodes": [ { "id": "3e1ca1e0-25be-4977-907a-3dee42b9bb15", "label": "nodepool-48959140-74a60edb45de0", "date_created": "2021-07-13T15:42:21+00:00", "status": "pending" } ] } }` fmt.Fprintf(writer, response) }) createReq := &NodePoolReq{ NodeQuantity: 1, Label: "nodepool-48959140", Plan: "vc2-1c-2gb", Tag: "mytag", } np, err := client.Kubernetes.CreateNodePool(ctx, "1", createReq) if err != nil { t.Errorf("Kubernetes.CreateNodePool returned %v", err) } expected := &NodePool{ ID: "554e7248-705a-5862-516f-4f4a6735346a", DateCreated: "2021-07-13T15:42:21+00:00", Label: "nodepool-48959140", Plan: "vc2-1c-2gb", Status: "pending", NodeQuantity: 1, MinNodes: 1, MaxNodes: 2, AutoScaler: true, Tag: "mytag", Nodes: []Node{ { ID: "3e1ca1e0-25be-4977-907a-3dee42b9bb15", Label: "nodepool-48959140-74a60edb45de0", DateCreated: "2021-07-13T15:42:21+00:00", Status: "pending", }, }, } if !reflect.DeepEqual(np, expected) { t.Errorf("Kubernetes.CreateNodePool returned %+v, expected %+v", np, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, err = client.Kubernetes.CreateNodePool(c, "1", createReq) if err == nil { t.Error("Kubernetes.CreateNodePool returned nil") } } func TestKubernetesHandler_GetNodePool(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s/node-pools/%s", vkePath, "1", "2"), func(writer http.ResponseWriter, request *http.Request) { response := `{ "node_pool": { "id": "554e7248-705a-5862-516f-4f4a6735346a", "date_created": "2021-07-13T15:42:21+00:00", "label": "nodepool-48959140", "plan": "vc2-1c-2gb", "status": "pending", "node_quantity": 1, "min_nodes": 1, "max_nodes": 2, "auto_scaler": true, "tag": "mytag", "nodes": [ { "id": "3e1ca1e0-25be-4977-907a-3dee42b9bb15", "label": "nodepool-48959140-74a60edb45de0", "date_created": "2021-07-13T15:42:21+00:00", "status": "pending" } ] } }` fmt.Fprintf(writer, response) }) np, err := client.Kubernetes.GetNodePool(ctx, "1", "2") if err != nil { t.Errorf("Kubernetes.GetNodePool returned %v", err) } expected := &NodePool{ ID: "554e7248-705a-5862-516f-4f4a6735346a", DateCreated: "2021-07-13T15:42:21+00:00", Label: "nodepool-48959140", Plan: "vc2-1c-2gb", Status: "pending", Tag: "mytag", NodeQuantity: 1, MinNodes: 1, MaxNodes: 2, AutoScaler: true, Nodes: []Node{ { ID: "3e1ca1e0-25be-4977-907a-3dee42b9bb15", Label: "nodepool-48959140-74a60edb45de0", DateCreated: "2021-07-13T15:42:21+00:00", Status: "pending", }, }, } if !reflect.DeepEqual(np, expected) { t.Errorf("Kubernetes.GetNodePool returned %+v, expected %+v", np, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, err = client.Kubernetes.GetNodePool(c, "1", "2") if err == nil { t.Error("Kubernetes.GetNodePool returned nil") } } func TestKubernetesHandler_ListNodePools(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s/node-pools", vkePath, "1"), func(writer http.ResponseWriter, request *http.Request) { response := `{ "node_pools": [{ "id": "554e7248-705a-5862-516f-4f4a6735346a", "date_created": "2021-07-13T15:42:21+00:00", "label": "nodepool-48959140", "plan": "vc2-1c-2gb", "status": "pending", "node_quantity": 1, "min_nodes": 1, "max_nodes": 2, "auto_scaler": true, "tag": "mytag", "nodes": [ { "id": "3e1ca1e0-25be-4977-907a-3dee42b9bb15", "label": "nodepool-48959140-74a60edb45de0", "date_created": "2021-07-13T15:42:21+00:00", "status": "pending" } ] }], "meta": { "total": 1, "links": { "next": "thisismycusror", "prev": "" } } }` fmt.Fprintf(writer, response) }) np, meta, err := client.Kubernetes.ListNodePools(ctx, "1", nil) if err != nil { t.Errorf("Kubernetes.ListNodePools returned %v", err) } expected := []NodePool{ { ID: "554e7248-705a-5862-516f-4f4a6735346a", DateCreated: "2021-07-13T15:42:21+00:00", Label: "nodepool-48959140", Plan: "vc2-1c-2gb", Status: "pending", Tag: "mytag", NodeQuantity: 1, MinNodes: 1, MaxNodes: 2, AutoScaler: true, Nodes: []Node{ { ID: "3e1ca1e0-25be-4977-907a-3dee42b9bb15", Label: "nodepool-48959140-74a60edb45de0", DateCreated: "2021-07-13T15:42:21+00:00", Status: "pending", }, }, }, } if !reflect.DeepEqual(np, expected) { t.Errorf("Kubernetes.ListNodePools returned %+v, expected %+v", np, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Kubernetes.ListNodePools meta returned %+v, expected %+v", meta, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, _, err = client.Kubernetes.ListNodePools(c, "1", nil) if err == nil { t.Error("Kubernetes.ListNodePools returned nil") } } func TestKubernetesHandler_UpdateNodePool(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s/node-pools/%s", vkePath, "1", "2"), func(writer http.ResponseWriter, request *http.Request) { response := `{ "node_pool": { "id": "e97bdee9-2781-4f31-be03-60fc75f399ae", "date_created": "2021-07-07T23:27:08+00:00", "date_updated": "2021-07-08T12:12:44+00:00", "label": "my-label-48770703", "plan": "vc2-1c-2gb", "status": "active", "node_quantity": 1, "min_nodes": 1, "max_nodes": 2, "auto_scaler": true, "tag": "mytag", "nodes": [ { "id": "f2e11430-76e5-4dc6-a1c9-ef5682c21ddf", "label": "my-label-48770703-44060e6384c45", "date_created": "2021-07-07T23:27:08+00:00", "status": "active" } ] } }` fmt.Fprintf(writer, response) }) update := NodePoolReqUpdate{NodeQuantity: 1} response, err := client.Kubernetes.UpdateNodePool(ctx, "1", "2", &update) if err != nil { t.Errorf("Kubernetes.UpdateNodePool returned %+v", err) } expected := &NodePool{ ID: "e97bdee9-2781-4f31-be03-60fc75f399ae", DateCreated: "2021-07-07T23:27:08+00:00", DateUpdated: "2021-07-08T12:12:44+00:00", Label: "my-label-48770703", Plan: "vc2-1c-2gb", Status: "active", NodeQuantity: 1, MinNodes: 1, MaxNodes: 2, AutoScaler: true, Tag: "mytag", Nodes: []Node{ { ID: "f2e11430-76e5-4dc6-a1c9-ef5682c21ddf", DateCreated: "2021-07-07T23:27:08+00:00", Label: "my-label-48770703-44060e6384c45", Status: "active", }, }, } if !reflect.DeepEqual(response, expected) { t.Errorf("Kubernetes.UpdateNodePool meta returned %+v, expected %+v", response, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, err = client.Kubernetes.UpdateNodePool(c, "1", "2", &update) if err == nil { t.Error("Kubernetes.UpdateNodePool returned nil") } } func TestKubernetesHandler_DeleteNodePool(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s/node-pools/%s", vkePath, "1", "2"), func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Kubernetes.DeleteNodePool(ctx, "1", "2") if err != nil { t.Errorf("Kubernetes.DeleteNodePool returned %+v", err) } } func TestKubernetesHandler_DeleteNodePoolInstance(t *testing.T) { setup() defer teardown() path := fmt.Sprintf("%s/%s/node-pools/%s/nodes/%s", vkePath, "1", "2", "3") mux.HandleFunc(fmt.Sprintf(path), func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Kubernetes.DeleteNodePoolInstance(ctx, "1", "2", "3") if err != nil { t.Errorf("Kubernetes.DeleteNodePoolInstance returned %+v", err) } } func TestKubernetesHandler_RecycleNodePoolInstance(t *testing.T) { setup() defer teardown() path := fmt.Sprintf("%s/%s/node-pools/%s/nodes/%s/recycle", vkePath, "1", "2", "3") mux.HandleFunc(fmt.Sprintf(path), func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Kubernetes.RecycleNodePoolInstance(ctx, "1", "2", "3") if err != nil { t.Errorf("Kubernetes.RecycleNodePoolInstance returned %+v", err) } } func TestKubernetesHandler_GetKubeConfig(t *testing.T) { setup() defer teardown() path := fmt.Sprintf("%s/%s/config", vkePath, "1") mux.HandleFunc(fmt.Sprintf(path), func(writer http.ResponseWriter, request *http.Request) { response := `{"kube_config": "config="}` fmt.Fprint(writer, response) }) config, err := client.Kubernetes.GetKubeConfig(ctx, "1") if err != nil { t.Errorf("Kubernetes.GetKubeConfig returned %+v", err) } expected := &KubeConfig{KubeConfig: "config="} if !reflect.DeepEqual(config, expected) { t.Errorf("Kubernetes.GetKubeConfig returned %+v, expected %+v", config, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, err = client.Kubernetes.GetKubeConfig(c, "1") if err == nil { t.Error("Kubernetes.GetKubeConfig returned nil") } } func TestKubernetesHandler_GetVersions(t *testing.T) { setup() defer teardown() path := "/v2/kubernetes/versions" mux.HandleFunc(fmt.Sprintf(path), func(writer http.ResponseWriter, request *http.Request) { response := `{"versions": ["v1.20.0+1"]}` fmt.Fprint(writer, response) }) config, err := client.Kubernetes.GetVersions(ctx) if err != nil { t.Errorf("Kubernetes.GetVersions returned %+v", err) } expected := &Versions{Versions: []string{"v1.20.0+1"}} if !reflect.DeepEqual(config, expected) { t.Errorf("Kubernetes.GetVersions returned %+v, expected %+v", config, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, err = client.Kubernetes.GetVersions(c) if err == nil { t.Error("Kubernetes.GetVersions returned nil") } } func TestKubernetesHandler_GetUpgrades(t *testing.T) { setup() defer teardown() path := fmt.Sprintf("%s/%s/available-upgrades", vkePath, "1") mux.HandleFunc(fmt.Sprintf(path), func(writer http.ResponseWriter, request *http.Request) { response := `{"available_upgrades": ["v1.20.0+1"]}` fmt.Fprint(writer, response) }) config, err := client.Kubernetes.GetUpgrades(ctx, "1") if err != nil { t.Errorf("Kubernetes.GetVersions returned %+v", err) } expected := []string{"v1.20.0+1"} if !reflect.DeepEqual(config, expected) { t.Errorf("Kubernetes.GetVersions returned %+v, expected %+v", config, expected) } c, can := context.WithTimeout(ctx, 1*time.Microsecond) defer can() _, err = client.Kubernetes.GetUpgrades(c, "1") if err == nil { t.Error("Kubernetes.GetUpgradeVersions returned nil") } } func TestKubernetesHandler_Upgrade(t *testing.T) { setup() defer teardown() mux.HandleFunc(fmt.Sprintf("%s/%s/upgrades", vkePath, "1"), func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) req := &ClusterUpgradeReq{UpgradeVersion: "2"} err := client.Kubernetes.Upgrade(ctx, "1", req) if err != nil { t.Errorf("Kubernetes.StartUpgrade returned %+v", err) } } golang-github-vultr-govultr-2.17.2/listOptions.go000066400000000000000000000013611425211421400220570ustar00rootroot00000000000000package govultr // ListOptions are the available query params type ListOptions struct { // These query params are used for all list calls that support pagination PerPage int `url:"per_page,omitempty"` Cursor string `url:"cursor,omitempty"` // These three query params are currently used for the list instance call // These may be extended to other list calls // https://www.vultr.com/api/#operation/list-instances MainIP string `url:"main_ip,omitempty"` Label string `url:"label,omitempty"` Tag string `url:"tag,omitempty"` Region string `url:"region,omitempty"` // Query params that can be used on the list snapshots call // https://www.vultr.com/api/#operation/list-snapshots Description string `url:"description,omitempty"` } golang-github-vultr-govultr-2.17.2/load_balancer.go000066400000000000000000000266721425211421400223120ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const lbPath = "/v2/load-balancers" // LoadBalancerService is the interface to interact with the server endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/load-balancer type LoadBalancerService interface { Create(ctx context.Context, createReq *LoadBalancerReq) (*LoadBalancer, error) Get(ctx context.Context, ID string) (*LoadBalancer, error) Update(ctx context.Context, ID string, updateReq *LoadBalancerReq) error Delete(ctx context.Context, ID string) error List(ctx context.Context, options *ListOptions) ([]LoadBalancer, *Meta, error) CreateForwardingRule(ctx context.Context, ID string, rule *ForwardingRule) (*ForwardingRule, error) GetForwardingRule(ctx context.Context, ID string, ruleID string) (*ForwardingRule, error) DeleteForwardingRule(ctx context.Context, ID string, RuleID string) error ListForwardingRules(ctx context.Context, ID string, options *ListOptions) ([]ForwardingRule, *Meta, error) ListFirewallRules(ctx context.Context, ID string, options *ListOptions) ([]LBFirewallRule, *Meta, error) GetFirewallRule(ctx context.Context, ID string, ruleID string) (*LBFirewallRule, error) } // LoadBalancerHandler handles interaction with the server methods for the Vultr API type LoadBalancerHandler struct { client *Client } // LoadBalancer represent the structure of a load balancer type LoadBalancer struct { ID string `json:"id,omitempty"` DateCreated string `json:"date_created,omitempty"` Region string `json:"region,omitempty"` Label string `json:"label,omitempty"` Status string `json:"status,omitempty"` IPV4 string `json:"ipv4,omitempty"` IPV6 string `json:"ipv6,omitempty"` Instances []string `json:"instances,omitempty"` HealthCheck *HealthCheck `json:"health_check,omitempty"` GenericInfo *GenericInfo `json:"generic_info,omitempty"` SSLInfo *bool `json:"has_ssl,omitempty"` ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"` FirewallRules []LBFirewallRule `json:"firewall_rules,omitempty"` } // LoadBalancerReq gives options for creating or updating a load balancer type LoadBalancerReq struct { Region string `json:"region,omitempty"` Label string `json:"label,omitempty"` Instances []string `json:"instances"` HealthCheck *HealthCheck `json:"health_check,omitempty"` StickySessions *StickySessions `json:"sticky_session,omitempty"` ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"` SSL *SSL `json:"ssl,omitempty"` SSLRedirect *bool `json:"ssl_redirect,omitempty"` ProxyProtocol *bool `json:"proxy_protocol,omitempty"` BalancingAlgorithm string `json:"balancing_algorithm,omitempty"` FirewallRules []LBFirewallRule `json:"firewall_rules"` // Deprecated: PrivateNetwork should no longer be used. Instead, use VPC. PrivateNetwork *string `json:"private_network,omitempty"` VPC *string `json:"vpc,omitempty"` } // InstanceList represents instances that are attached to your load balancer type InstanceList struct { InstanceList []string } // HealthCheck represents your health check configuration for your load balancer. type HealthCheck struct { Protocol string `json:"protocol,omitempty"` Port int `json:"port,omitempty"` Path string `json:"path,omitempty"` CheckInterval int `json:"check_interval,omitempty"` ResponseTimeout int `json:"response_timeout,omitempty"` UnhealthyThreshold int `json:"unhealthy_threshold,omitempty"` HealthyThreshold int `json:"healthy_threshold,omitempty"` } // GenericInfo represents generic configuration of your load balancer type GenericInfo struct { BalancingAlgorithm string `json:"balancing_algorithm,omitempty"` SSLRedirect *bool `json:"ssl_redirect,omitempty"` StickySessions *StickySessions `json:"sticky_sessions,omitempty"` ProxyProtocol *bool `json:"proxy_protocol,omitempty"` // Deprecated: PrivateNetwork should no longer be used. Instead, use VPC. PrivateNetwork string `json:"private_network,omitempty"` VPC string `json:"vpc,omitempty"` } // StickySessions represents cookie for your load balancer type StickySessions struct { CookieName string `json:"cookie_name,omitempty"` } // ForwardingRules represent a list of forwarding rules type ForwardingRules struct { ForwardRuleList []ForwardingRule `json:"forwarding_rules,omitempty"` } // ForwardingRule represent a single forwarding rule type ForwardingRule struct { RuleID string `json:"id,omitempty"` FrontendProtocol string `json:"frontend_protocol,omitempty"` FrontendPort int `json:"frontend_port,omitempty"` BackendProtocol string `json:"backend_protocol,omitempty"` BackendPort int `json:"backend_port,omitempty"` } // LBFirewallRule represent a single firewall rule type LBFirewallRule struct { RuleID string `json:"id,omitempty"` Port int `json:"port,omitempty"` IPType string `json:"ip_type,omitempty"` Source string `json:"source,omitempty"` } // SSL represents valid SSL config type SSL struct { PrivateKey string `json:"private_key,omitempty"` Certificate string `json:"certificate,omitempty"` Chain string `json:"chain,omitempty"` } type lbsBase struct { LoadBalancers []LoadBalancer `json:"load_balancers"` Meta *Meta `json:"meta"` } type lbBase struct { LoadBalancer *LoadBalancer `json:"load_balancer"` } type lbRulesBase struct { ForwardingRules []ForwardingRule `json:"forwarding_rules"` Meta *Meta `json:"meta"` } type lbRuleBase struct { ForwardingRule *ForwardingRule `json:"forwarding_rule"` } type lbFWRulesBase struct { FirewallRules []LBFirewallRule `json:"firewall_rules"` Meta *Meta `json:"meta"` } type lbFWRuleBase struct { FirewallRule *LBFirewallRule `json:"firewall_rule"` } // Create a load balancer func (l *LoadBalancerHandler) Create(ctx context.Context, createReq *LoadBalancerReq) (*LoadBalancer, error) { req, err := l.client.NewRequest(ctx, http.MethodPost, lbPath, createReq) if err != nil { return nil, err } var lb = new(lbBase) if err = l.client.DoWithContext(ctx, req, &lb); err != nil { return nil, err } return lb.LoadBalancer, nil } // Get a load balancer func (l *LoadBalancerHandler) Get(ctx context.Context, ID string) (*LoadBalancer, error) { uri := fmt.Sprintf("%s/%s", lbPath, ID) req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } var lb = new(lbBase) if err = l.client.DoWithContext(ctx, req, lb); err != nil { return nil, err } return lb.LoadBalancer, nil } // Update updates your your load balancer func (l *LoadBalancerHandler) Update(ctx context.Context, ID string, updateReq *LoadBalancerReq) error { uri := fmt.Sprintf("%s/%s", lbPath, ID) req, err := l.client.NewRequest(ctx, http.MethodPatch, uri, updateReq) if err != nil { return err } return l.client.DoWithContext(ctx, req, nil) } // Delete a load balancer subscription. func (l *LoadBalancerHandler) Delete(ctx context.Context, ID string) error { uri := fmt.Sprintf("%s/%s", lbPath, ID) req, err := l.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return l.client.DoWithContext(ctx, req, nil) } // List all load balancer subscriptions on the current account. func (l *LoadBalancerHandler) List(ctx context.Context, options *ListOptions) ([]LoadBalancer, *Meta, error) { req, err := l.client.NewRequest(ctx, http.MethodGet, lbPath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() lbs := new(lbsBase) if err = l.client.DoWithContext(ctx, req, &lbs); err != nil { return nil, nil, err } return lbs.LoadBalancers, lbs.Meta, nil } // CreateForwardingRule will create a new forwarding rule for your load balancer subscription. // Note the RuleID will be returned in the ForwardingRule struct func (l *LoadBalancerHandler) CreateForwardingRule(ctx context.Context, ID string, rule *ForwardingRule) (*ForwardingRule, error) { uri := fmt.Sprintf("%s/%s/forwarding-rules", lbPath, ID) req, err := l.client.NewRequest(ctx, http.MethodPost, uri, rule) if err != nil { return nil, err } fwRule := new(lbRuleBase) if err = l.client.DoWithContext(ctx, req, fwRule); err != nil { return nil, err } return fwRule.ForwardingRule, nil } // GetForwardingRule will get a forwarding rule from your load balancer subscription. func (l *LoadBalancerHandler) GetForwardingRule(ctx context.Context, ID string, ruleID string) (*ForwardingRule, error) { uri := fmt.Sprintf("%s/%s/forwarding-rules/%s", lbPath, ID, ruleID) req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } fwRule := new(lbRuleBase) if err = l.client.DoWithContext(ctx, req, fwRule); err != nil { return nil, err } return fwRule.ForwardingRule, nil } // ListForwardingRules lists all forwarding rules for a load balancer subscription func (l *LoadBalancerHandler) ListForwardingRules(ctx context.Context, ID string, options *ListOptions) ([]ForwardingRule, *Meta, error) { uri := fmt.Sprintf("%s/%s/forwarding-rules", lbPath, ID) req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() fwRules := new(lbRulesBase) if err = l.client.DoWithContext(ctx, req, &fwRules); err != nil { return nil, nil, err } return fwRules.ForwardingRules, fwRules.Meta, nil } // DeleteForwardingRule removes a forwarding rule from a load balancer subscription func (l *LoadBalancerHandler) DeleteForwardingRule(ctx context.Context, ID string, RuleID string) error { uri := fmt.Sprintf("%s/%s/forwarding-rules/%s", lbPath, ID, RuleID) req, err := l.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return l.client.DoWithContext(ctx, req, nil) } // GetFirewallRule will get a firewall rule from your load balancer subscription. func (l *LoadBalancerHandler) GetFirewallRule(ctx context.Context, ID string, ruleID string) (*LBFirewallRule, error) { uri := fmt.Sprintf("%s/%s/firewall-rules/%s", lbPath, ID, ruleID) req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } fwRule := new(lbFWRuleBase) if err = l.client.DoWithContext(ctx, req, fwRule); err != nil { return nil, err } return fwRule.FirewallRule, nil } // ListFirewallRules lists all firewall rules for a load balancer subscription func (l *LoadBalancerHandler) ListFirewallRules(ctx context.Context, ID string, options *ListOptions) ([]LBFirewallRule, *Meta, error) { uri := fmt.Sprintf("%s/%s/firewall-rules", lbPath, ID) req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() fwRules := new(lbFWRulesBase) if err = l.client.DoWithContext(ctx, req, &fwRules); err != nil { return nil, nil, err } return fwRules.FirewallRules, fwRules.Meta, nil } golang-github-vultr-govultr-2.17.2/load_balancer_test.go000066400000000000000000000374501425211421400233450ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestLoadBalancerHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc(lbPath, func(writer http.ResponseWriter, request *http.Request) { response := ` { "load_balancers" : [ { "id": "1317575", "date_created": "2020-01-07 17:24:23", "region": "ewr", "label": "my label", "status": "active", "ipv4": "123.123.123.123", "ipv6": "2001:DB8:1000::100", "generic_info": { "balancing_algorithm": "roundrobin", "ssl_redirect": false, "proxy_protocol": false, "private_network": "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", "vpc": "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", "sticky_sessions": { "cookie_name": "my-cookie" } }, "health_check": { "protocol": "http", "port": 80, "path": "/", "check_interval": 15, "response_timeout": 5, "unhealthy_threshold": 5, "healthy_threshold": 5 }, "has_ssl": false, "forwarding_rules": [ { "id": "abcd12345", "frontend_protocol": "http", "frontend_port": 80, "backend_protocol": "http", "backend_port": 80 } ], "firewall_rules": [ { "id": "abcd12345", "port": 80, "source": "0.0.0.0/0", "ip_type": "v4" } ], "instances": [ "12345" ] } ], "meta": { "total":8, "links": { "next":"", "prev":"" } } } ` fmt.Fprintf(writer, response) }) list, meta, err := client.LoadBalancer.List(ctx, nil) if err != nil { t.Errorf("LoadBalancer.List returned %+v", err) } expected := []LoadBalancer{ { ID: "1317575", DateCreated: "2020-01-07 17:24:23", Label: "my label", Status: "active", Region: "ewr", IPV6: "2001:DB8:1000::100", IPV4: "123.123.123.123", SSLInfo: BoolToBoolPtr(false), ForwardingRules: []ForwardingRule{ { RuleID: "abcd12345", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }, }, GenericInfo: &GenericInfo{ BalancingAlgorithm: "roundrobin", SSLRedirect: BoolToBoolPtr(false), ProxyProtocol: BoolToBoolPtr(false), PrivateNetwork: "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", VPC: "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", StickySessions: &StickySessions{ CookieName: "my-cookie", }, }, HealthCheck: &HealthCheck{ Protocol: "http", Port: 80, Path: "/", CheckInterval: 15, ResponseTimeout: 5, UnhealthyThreshold: 5, HealthyThreshold: 5, }, Instances: []string{"12345"}, FirewallRules: []LBFirewallRule{ { RuleID: "abcd12345", Port: 80, Source: "0.0.0.0/0", IPType: "v4", }, }, }, } expectedMeta := &Meta{ Total: 8, Links: &Links{}, } if !reflect.DeepEqual(list, expected) { t.Errorf("LoadBalancer.List returned %+v, expected %+v", list, expected) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("LoadBalancer.List returned %+v, expected %+v", meta, expectedMeta) } } func TestLoadBalancerHandler_Delete(t *testing.T) { setup() defer teardown() uri := fmt.Sprintf("%s/%d", lbPath, 1317575) mux.HandleFunc(uri, func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.LoadBalancer.Delete(ctx, "1317575"); err != nil { t.Errorf("LoadBalancer.Delete returned %+v", err) } } func TestLoadBalancerHandler_Get(t *testing.T) { setup() defer teardown() uri := fmt.Sprintf("%s/%d", lbPath, 1317575) mux.HandleFunc(uri, func(writer http.ResponseWriter, request *http.Request) { response := ` { "load_balancer" : { "id": "1317575", "date_created": "2020-01-07 17:24:23", "region": "ewr", "label": "my label", "status": "active", "ipv4": "123.123.123.123", "ipv6": "2001:DB8:1000::100", "generic_info": { "balancing_algorithm": "roundrobin", "ssl_redirect": false, "proxy_protocol": false, "private_network": "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", "vpc": "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", "sticky_sessions": { "cookie_name": "my-cookie" } }, "health_check": { "protocol": "http", "port": 80, "path": "/", "check_interval": 15, "response_timeout": 5, "unhealthy_threshold": 5, "healthy_threshold": 5 }, "has_ssl": false, "forwarding_rules": [ { "id": "abcd12345", "frontend_protocol": "http", "frontend_port": 80, "backend_protocol": "http", "backend_port": 80 } ], "firewall_rules": [ { "id": "abcd12345", "port": 80, "source": "0.0.0.0/0", "ip_type": "v4" } ], "instances": [ "12345" ] } } ` fmt.Fprintf(writer, response) }) info, err := client.LoadBalancer.Get(ctx, "1317575") if err != nil { t.Errorf("LoadBalancer.Get returned %+v", err) } expected := &LoadBalancer{ ID: "1317575", DateCreated: "2020-01-07 17:24:23", Label: "my label", Status: "active", Region: "ewr", IPV6: "2001:DB8:1000::100", IPV4: "123.123.123.123", SSLInfo: BoolToBoolPtr(false), ForwardingRules: []ForwardingRule{ { RuleID: "abcd12345", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }, }, GenericInfo: &GenericInfo{ BalancingAlgorithm: "roundrobin", SSLRedirect: BoolToBoolPtr(false), ProxyProtocol: BoolToBoolPtr(false), PrivateNetwork: "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", VPC: "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", StickySessions: &StickySessions{ CookieName: "my-cookie", }, }, HealthCheck: &HealthCheck{ Protocol: "http", Port: 80, Path: "/", CheckInterval: 15, ResponseTimeout: 5, UnhealthyThreshold: 5, HealthyThreshold: 5, }, Instances: []string{"12345"}, FirewallRules: []LBFirewallRule{ { RuleID: "abcd12345", Port: 80, Source: "0.0.0.0/0", IPType: "v4", }, }, } if !reflect.DeepEqual(info, expected) { t.Errorf("LoadBalancer.Get returned %+v, expected %+v", info, expected) } } func TestLoadBalancerHandler_ListForwardingRules(t *testing.T) { setup() defer teardown() uri := fmt.Sprintf("%s/%d/forwarding-rules", lbPath, 12345) mux.HandleFunc(uri, func(writer http.ResponseWriter, request *http.Request) { response := `{ "forwarding_rules":[ { "id":"0690a322c25890bc", "frontend_protocol":"http", "frontend_port":80, "backend_protocol":"http", "backend_port":80 } ], "meta": { "total":8, "links": { "next":"", "prev":"" } } } ` fmt.Fprintf(writer, response) }) list, meta, err := client.LoadBalancer.ListForwardingRules(ctx, "12345", nil) if err != nil { t.Errorf("LoadBalancer.ListForwardingRules returned %+v", err) } expected := []ForwardingRule{ { RuleID: "0690a322c25890bc", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }, } expectedMeta := &Meta{ Total: 8, Links: &Links{}, } if !reflect.DeepEqual(list, expected) { t.Errorf("LoadBalancer.ListForwardingRules returned %+v, expected %+v", list, expected) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("LoadBalancer.ListForwardingRules returned %+v, expected %+v", meta, expectedMeta) } } func TestLoadBalancerHandler_DeleteForwardingRule(t *testing.T) { setup() defer teardown() uri := fmt.Sprintf("%s/%d/forwarding-rules/%s", lbPath, 12345, "abcde1234") mux.HandleFunc(uri, func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.LoadBalancer.DeleteForwardingRule(ctx, "12345", "abcde1234"); err != nil { t.Errorf("LoadBalancer.DeleteForwardingRule returned %+v", err) } } func TestLoadBalancerHandler_CreateForwardingRule(t *testing.T) { setup() defer teardown() uri := fmt.Sprintf("%s/%d/forwarding-rules", lbPath, 1317575) mux.HandleFunc(uri, func(writer http.ResponseWriter, request *http.Request) { response := ` { "forwarding_rule" : { "id":"0690a322c25890bc", "frontend_protocol":"http", "frontend_port":80, "backend_protocol":"http", "backend_port":80 } } ` fmt.Fprintf(writer, response) }) rule := &ForwardingRule{ RuleID: "0690a322c25890bc", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, } ruleID, err := client.LoadBalancer.CreateForwardingRule(ctx, "1317575", rule) if err != nil { t.Errorf("LoadBalancer.CreateForwardingRule returned %+v", err) } expected := &ForwardingRule{ RuleID: "0690a322c25890bc", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, } if !reflect.DeepEqual(ruleID, expected) { t.Errorf("LoadBalancer.CreateForwardingRule returned %+v, expected %+v", ruleID, expected) } } func TestLoadBalancerHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc(lbPath, func(writer http.ResponseWriter, request *http.Request) { response := ` { "load_balancer" : { "id": "1317575", "date_created": "2020-01-07 17:24:23", "region": "ewr", "label": "my label", "status": "active", "ipv4": "123.123.123.123", "ipv6": "2001:DB8:1000::100", "generic_info": { "balancing_algorithm": "roundrobin", "ssl_redirect": false, "proxy_protocol": false, "private_network": "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", "vpc": "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", "sticky_sessions": { "cookie_name": "my-cookie" } }, "health_check": { "protocol": "http", "port": 80, "path": "/", "check_interval": 15, "response_timeout": 5, "unhealthy_threshold": 5, "healthy_threshold": 5 }, "has_ssl": false, "forwarding_rules": [ { "id": "abcd12345", "frontend_protocol": "http", "frontend_port": 80, "backend_protocol": "http", "backend_port": 80 } ], "firewall_rules": [ { "id": "abcd12345", "port": 80, "source": "0.0.0.0/0", "ip_type": "v4" } ], "instances": [ "1234" ] } } ` fmt.Fprintf(writer, response) }) lbCreate := &LoadBalancerReq{ Label: "my label", Region: "ewr", ForwardingRules: []ForwardingRule{ { RuleID: "abcd12345", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }, }, BalancingAlgorithm: "roundrobin", SSLRedirect: BoolToBoolPtr(false), ProxyProtocol: BoolToBoolPtr(false), PrivateNetwork: StringToStringPtr("8d5bdbdb-3324-4d0c-b303-03e1315e1c02"), VPC: StringToStringPtr("8d5bdbdb-3324-4d0c-b303-03e1315e1c02"), HealthCheck: &HealthCheck{ Protocol: "http", Port: 80, Path: "/", CheckInterval: 15, ResponseTimeout: 5, UnhealthyThreshold: 5, HealthyThreshold: 5, }, } lb, err := client.LoadBalancer.Create(ctx, lbCreate) if err != nil { t.Errorf("LoadBalancer.Create returned %+v", err) } expected := &LoadBalancer{ ID: "1317575", DateCreated: "2020-01-07 17:24:23", Label: "my label", Status: "active", Region: "ewr", IPV6: "2001:DB8:1000::100", IPV4: "123.123.123.123", SSLInfo: BoolToBoolPtr(false), ForwardingRules: []ForwardingRule{ { RuleID: "abcd12345", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }, }, GenericInfo: &GenericInfo{ BalancingAlgorithm: "roundrobin", SSLRedirect: BoolToBoolPtr(false), ProxyProtocol: BoolToBoolPtr(false), PrivateNetwork: "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", VPC: "8d5bdbdb-3324-4d0c-b303-03e1315e1c02", StickySessions: &StickySessions{ CookieName: "my-cookie", }, }, HealthCheck: &HealthCheck{ Protocol: "http", Port: 80, Path: "/", CheckInterval: 15, ResponseTimeout: 5, UnhealthyThreshold: 5, HealthyThreshold: 5, }, Instances: []string{"1234"}, FirewallRules: []LBFirewallRule{ { RuleID: "abcd12345", Port: 80, Source: "0.0.0.0/0", IPType: "v4", }, }, } if !reflect.DeepEqual(lb, expected) { t.Errorf("LoadBalancer.Create returned %+v, expected %+v", lb, expected) } } func TestLoadBalancerHandler_Update(t *testing.T) { setup() defer teardown() uri := fmt.Sprintf("%s/%s", lbPath, "d9dbc01c-aaca-4d4b-8c4a-bbb24c946141") mux.HandleFunc(uri, func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) lbCreate := &LoadBalancerReq{ Label: "my label", Region: "ewr", ForwardingRules: []ForwardingRule{ { RuleID: "abcd12345", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }, }, BalancingAlgorithm: "roundrobin", SSLRedirect: BoolToBoolPtr(false), ProxyProtocol: BoolToBoolPtr(false), PrivateNetwork: StringToStringPtr("8d5bdbdb-3324-4d0c-b303-03e1315e1c02"), VPC: StringToStringPtr("8d5bdbdb-3324-4d0c-b303-03e1315e1c02"), HealthCheck: &HealthCheck{ Protocol: "http", Port: 80, Path: "/", CheckInterval: 15, ResponseTimeout: 5, UnhealthyThreshold: 5, HealthyThreshold: 5, }, } err := client.LoadBalancer.Update(ctx, "d9dbc01c-aaca-4d4b-8c4a-bbb24c946141", lbCreate) if err != nil { t.Errorf("LoadBalancer.Update returned %+v", err) } } func TestLoadBalancerHandler_GetFowardingRule(t *testing.T) { setup() defer teardown() uri := fmt.Sprintf("%s/%s/forwarding-rules/%s", lbPath, "d9dbc01c-aaca-4d4b-8c4a-bbb24c946141", "d42585eb85b1f69d") mux.HandleFunc(uri, func(writer http.ResponseWriter, request *http.Request) { req := `{ "forwarding_rule": { "id": "d42585eb85b1f69d", "frontend_protocol": "http", "frontend_port": 8080, "backend_protocol": "http", "backend_port": 80 } }` fmt.Fprint(writer, req) }) rule, err := client.LoadBalancer.GetForwardingRule(ctx, "d9dbc01c-aaca-4d4b-8c4a-bbb24c946141", "d42585eb85b1f69d") if err != nil { t.Errorf("LoadBalancer.GetForwardingRule returned %+v", err) } expected := &ForwardingRule{ RuleID: "d42585eb85b1f69d", FrontendProtocol: "http", FrontendPort: 8080, BackendProtocol: "http", BackendPort: 80, } if !reflect.DeepEqual(rule, expected) { t.Errorf("LoadBalancer.GetForwardingRule returned %+v, expected %+v", rule, expected) } } func TestLoadBalancerHandler_GetFirewallRule(t *testing.T) { setup() defer teardown() uri := fmt.Sprintf("%s/%s/firewall-rules/%s", lbPath, "d9dbc01c-aaca-4d4b-8c4a-bbb24c946141", "d42585eb85b1f69d") mux.HandleFunc(uri, func(writer http.ResponseWriter, request *http.Request) { req := `{ "firewall_rule": { "id": "d42585eb85b1f69d", "port": 80, "source": "0.0.0.0/0", "ip_type": "v4" } }` fmt.Fprint(writer, req) }) rule, err := client.LoadBalancer.GetFirewallRule(ctx, "d9dbc01c-aaca-4d4b-8c4a-bbb24c946141", "d42585eb85b1f69d") if err != nil { t.Errorf("LoadBalancer.GetFirewallRule returned %+v", err) } expected := &LBFirewallRule{ RuleID: "d42585eb85b1f69d", Port: 80, Source: "0.0.0.0/0", IPType: "v4", } if !reflect.DeepEqual(rule, expected) { t.Errorf("LoadBalancer.GetFirewallRule returned %+v, expected %+v", rule, expected) } } golang-github-vultr-govultr-2.17.2/meta.go000066400000000000000000000004301425211421400204520ustar00rootroot00000000000000package govultr // Meta represents the available pagination information type Meta struct { Total int `json:"total"` Links *Links } // Links represent the next/previous cursor in your pagination calls type Links struct { Next string `json:"next"` Prev string `json:"prev"` } golang-github-vultr-govultr-2.17.2/meta_test.go000066400000000000000000000011101425211421400215050ustar00rootroot00000000000000package govultr import ( "encoding/json" "testing" ) var metaBytes = []byte(` { "total": 11, "links": { "next": "bmV4dF9fMTMxOTgxNQ==", "prev": "" } } `) func TestMeta(t *testing.T) { var meta *Meta if err := json.Unmarshal(metaBytes, &meta); err != nil { t.Fatal(err) } if meta.Total != 11 { t.Fatal("Total did not equal 11") } if meta.Links.Next != "bmV4dF9fMTMxOTgxNQ==" { t.Fatal("Next cursor did not equal bmV4dF9fMTMxOTgxNQ==") } if meta.Links.Prev != "" { t.Fatal("Previous cursor was not empty") } } golang-github-vultr-govultr-2.17.2/network.go000066400000000000000000000120231425211421400212160ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const netPath = "/v2/private-networks" // NetworkService is the interface to interact with the network endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/private-Networks // Deprecated: NetworkService should no longer be used. Instead, use VPCService. type NetworkService interface { // Deprecated: NetworkService Create should no longer be used. Instead, use VPCService Create. Create(ctx context.Context, createReq *NetworkReq) (*Network, error) // Deprecated: NetworkService Get should no longer be used. Instead, use VPCService Get. Get(ctx context.Context, networkID string) (*Network, error) // Deprecated: NetworkService Update should no longer be used. Instead, use VPCService Update. Update(ctx context.Context, networkID string, description string) error // Deprecated: NetworkService Delete should no longer be used. Instead, use VPCService Delete. Delete(ctx context.Context, networkID string) error // Deprecated: NetworkService List should no longer be used. Instead, use VPCService List. List(ctx context.Context, options *ListOptions) ([]Network, *Meta, error) } // NetworkServiceHandler handles interaction with the network methods for the Vultr API // Deprecated: NetworkServiceHandler should no longer be used. Instead, use VPCServiceHandler. type NetworkServiceHandler struct { client *Client } // Network represents a Vultr private network // Deprecated: Network should no longer be used. Instead, use VPC. type Network struct { NetworkID string `json:"id"` Region string `json:"region"` Description string `json:"description"` V4Subnet string `json:"v4_subnet"` V4SubnetMask int `json:"v4_subnet_mask"` DateCreated string `json:"date_created"` } // NetworkReq represents parameters to create or update Network resource // Deprecated: NetworkReq should no longer be used. Instead, use VPCReq. type NetworkReq struct { Region string `json:"region"` Description string `json:"description"` V4Subnet string `json:"v4_subnet"` V4SubnetMask int `json:"v4_subnet_mask"` } type networksBase struct { Networks []Network `json:"networks"` Meta *Meta `json:"meta"` } type networkBase struct { Network *Network `json:"network"` } // Create a new private network. A private network can only be used at the location for which it was created. // Deprecated: NetworkServiceHandler Create should no longer be used. Instead, use VPCServiceHandler Create. func (n *NetworkServiceHandler) Create(ctx context.Context, createReq *NetworkReq) (*Network, error) { req, err := n.client.NewRequest(ctx, http.MethodPost, netPath, createReq) if err != nil { return nil, err } network := new(networkBase) if err = n.client.DoWithContext(ctx, req, network); err != nil { return nil, err } return network.Network, nil } // Get gets the private networks of the requested ID // Deprecated: NetworkServiceHandler Get should no longer be used. Instead use VPCServiceHandler Create. func (n *NetworkServiceHandler) Get(ctx context.Context, networkID string) (*Network, error) { uri := fmt.Sprintf("%s/%s", netPath, networkID) req, err := n.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } network := new(networkBase) if err = n.client.DoWithContext(ctx, req, network); err != nil { return nil, err } return network.Network, nil } // Update updates a private network // Deprecated: NetworkServiceHandler Update should no longer be used. Instead, use VPCServiceHandler Update. func (n *NetworkServiceHandler) Update(ctx context.Context, networkID string, description string) error { uri := fmt.Sprintf("%s/%s", netPath, networkID) netReq := RequestBody{"description": description} req, err := n.client.NewRequest(ctx, http.MethodPut, uri, netReq) if err != nil { return err } return n.client.DoWithContext(ctx, req, nil) } // Delete a private network. Before deleting, a network must be disabled from all instances // Deprecated: NetworkServiceHandler Delete should no longer be used. Instead, use VPCServiceHandler Delete. func (n *NetworkServiceHandler) Delete(ctx context.Context, networkID string) error { uri := fmt.Sprintf("%s/%s", netPath, networkID) req, err := n.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return n.client.DoWithContext(ctx, req, nil) } // List lists all private networks on the current account // Deprecated: NetworkServiceHandler List should no longer be used. Instead, use VPCServiceHandler List. func (n *NetworkServiceHandler) List(ctx context.Context, options *ListOptions) ([]Network, *Meta, error) { req, err := n.client.NewRequest(ctx, http.MethodGet, netPath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() networks := new(networksBase) if err = n.client.DoWithContext(ctx, req, networks); err != nil { return nil, nil, err } return networks.Networks, networks.Meta, nil } golang-github-vultr-govultr-2.17.2/network_test.go000066400000000000000000000070501425211421400222610ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestNetworkServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/private-networks", func(writer http.ResponseWriter, request *http.Request) { response := ` { "network": { "id": "net539626f0798d7", "date_created": "2017-08-25 12:23:45", "region": "ewr", "description": "test1", "v4_subnet": "10.99.0.0", "v4_subnet_mask": 24 } } ` fmt.Fprint(writer, response) }) options := &NetworkReq{ Region: "ewr", Description: "test1", } net, err := client.Network.Create(ctx, options) if err != nil { t.Errorf("Network.Create returned %+v, expected %+v", err, nil) } expected := &Network{ NetworkID: "net539626f0798d7", Region: "ewr", Description: "test1", V4Subnet: "10.99.0.0", V4SubnetMask: 24, DateCreated: "2017-08-25 12:23:45", } if !reflect.DeepEqual(net, expected) { t.Errorf("Network.Create returned %+v, expected %+v", net, expected) } } func TestNetworkServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/private-networks/net539626f0798d7", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Network.Delete(ctx, "net539626f0798d7") if err != nil { t.Errorf("Network.Delete returned %+v, expected %+v", err, nil) } } func TestNetworkServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/private-networks", func(writer http.ResponseWriter, request *http.Request) { response := ` { "networks": [{ "id": "net539626f0798d7", "date_created": "2017-08-25 12:23:45", "region": "ewr", "description": "test1", "v4_subnet": "10.99.0.0", "v4_subnet_mask": 24 }] } ` fmt.Fprintf(writer, response) }) networks, _, err := client.Network.List(ctx, nil) if err != nil { t.Errorf("Network.List returned error: %v", err) } expected := []Network{ { NetworkID: "net539626f0798d7", Region: "ewr", Description: "test1", V4Subnet: "10.99.0.0", V4SubnetMask: 24, DateCreated: "2017-08-25 12:23:45", }, } if !reflect.DeepEqual(networks, expected) { t.Errorf("Network.List returned %+v, expected %+v", networks, expected) } } func TestNetworkServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/private-networks/net539626f0798d7", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Network.Update(ctx, "net539626f0798d7", "update") if err != nil { t.Errorf("Network.Update returned %+v, expected %+v", err, nil) } } func TestNetworkServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/private-networks/net539626f0798d7", func(writer http.ResponseWriter, request *http.Request) { req := `{"network": {"id": "cb676a46-66fd-4dfb-b839-443f2e6c0b60","date_created": "2020-10-10T01:56:20+00:00","region": "ewr","description": "sample desc","v4_subnet": "10.99.0.0","v4_subnet_mask": 24}}` fmt.Fprint(writer, req) }) network, err := client.Network.Get(ctx, "net539626f0798d7") if err != nil { t.Errorf("Network.Get returned %+v, expected %+v", err, nil) } expected := &Network{ NetworkID: "cb676a46-66fd-4dfb-b839-443f2e6c0b60", Region: "ewr", Description: "sample desc", V4Subnet: "10.99.0.0", V4SubnetMask: 24, DateCreated: "2020-10-10T01:56:20+00:00", } if !reflect.DeepEqual(network, expected) { t.Errorf("Instance.Get returned %+v, expected %+v", network, expected) } } golang-github-vultr-govultr-2.17.2/object_storage.go000066400000000000000000000135611425211421400225270ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // ObjectStorageService is the interface to interact with the object storage endpoints on the Vultr API. // Link : https://www.vultr.com/api/#tag/s3 type ObjectStorageService interface { Create(ctx context.Context, clusterID int, label string) (*ObjectStorage, error) Get(ctx context.Context, id string) (*ObjectStorage, error) Update(ctx context.Context, id, label string) error Delete(ctx context.Context, id string) error List(ctx context.Context, options *ListOptions) ([]ObjectStorage, *Meta, error) ListCluster(ctx context.Context, options *ListOptions) ([]ObjectStorageCluster, *Meta, error) RegenerateKeys(ctx context.Context, id string) (*S3Keys, error) } // ObjectStorageServiceHandler handles interaction with the firewall rule methods for the Vultr API. type ObjectStorageServiceHandler struct { client *Client } // ObjectStorage represents a Vultr Object Storage subscription. type ObjectStorage struct { ID string `json:"id"` DateCreated string `json:"date_created"` ObjectStoreClusterID int `json:"cluster_id"` Region string `json:"region"` Location string `json:"location"` Label string `json:"label"` Status string `json:"status"` S3Keys } // S3Keys define your api access to your cluster type S3Keys struct { S3Hostname string `json:"s3_hostname"` S3AccessKey string `json:"s3_access_key"` S3SecretKey string `json:"s3_secret_key"` } // ObjectStorageCluster represents a Vultr Object Storage cluster. type ObjectStorageCluster struct { ID int `json:"id"` Region string `json:"region"` Hostname string `json:"hostname"` Deploy string `json:"deploy"` } type objectStoragesBase struct { ObjectStorages []ObjectStorage `json:"object_storages"` Meta *Meta `json:"meta"` } type objectStorageBase struct { ObjectStorage *ObjectStorage `json:"object_storage"` } type objectStorageClustersBase struct { Clusters []ObjectStorageCluster `json:"clusters"` Meta *Meta `json:"meta"` } type s3KeysBase struct { S3Credentials *S3Keys `json:"s3_credentials"` } //// ObjectListOptions are your optional params you have available to list data. //type ObjectListOptions struct { // IncludeS3 bool // Label string //} // Create an object storage subscription func (o *ObjectStorageServiceHandler) Create(ctx context.Context, clusterID int, label string) (*ObjectStorage, error) { uri := "/v2/object-storage" values := RequestBody{"cluster_id": clusterID, "label": label} req, err := o.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return nil, err } objectStorage := new(objectStorageBase) err = o.client.DoWithContext(ctx, req, objectStorage) if err != nil { return nil, err } return objectStorage.ObjectStorage, nil } // Get returns a specified object storage by the provided ID func (o *ObjectStorageServiceHandler) Get(ctx context.Context, id string) (*ObjectStorage, error) { uri := fmt.Sprintf("/v2/object-storage/%s", id) req, err := o.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } objectStorage := new(objectStorageBase) if err = o.client.DoWithContext(ctx, req, objectStorage); err != nil { return nil, err } return objectStorage.ObjectStorage, nil } // Update a Object Storage Subscription. func (o *ObjectStorageServiceHandler) Update(ctx context.Context, id, label string) error { uri := fmt.Sprintf("/v2/object-storage/%s", id) value := RequestBody{"label": label} req, err := o.client.NewRequest(ctx, http.MethodPut, uri, value) if err != nil { return err } return o.client.DoWithContext(ctx, req, nil) } // Delete a object storage subscription. func (o *ObjectStorageServiceHandler) Delete(ctx context.Context, id string) error { uri := fmt.Sprintf("/v2/object-storage/%s", id) req, err := o.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return o.client.DoWithContext(ctx, req, nil) } // List all object storage subscriptions on the current account. This includes both pending and active subscriptions. func (o *ObjectStorageServiceHandler) List(ctx context.Context, options *ListOptions) ([]ObjectStorage, *Meta, error) { uri := "/v2/object-storage" req, err := o.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() objectStorage := new(objectStoragesBase) if err = o.client.DoWithContext(ctx, req, objectStorage); err != nil { return nil, nil, err } return objectStorage.ObjectStorages, objectStorage.Meta, nil } // ListCluster returns back your object storage clusters. // Clusters may be removed over time. The "deploy" field can be used to determine whether or not new deployments are allowed in the cluster. func (o *ObjectStorageServiceHandler) ListCluster(ctx context.Context, options *ListOptions) ([]ObjectStorageCluster, *Meta, error) { uri := "/v2/object-storage/clusters" req, err := o.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } clusters := new(objectStorageClustersBase) if err = o.client.DoWithContext(ctx, req, clusters); err != nil { return nil, nil, err } return clusters.Clusters, clusters.Meta, nil } // RegenerateKeys of the S3 API Keys for an object storage subscription func (o *ObjectStorageServiceHandler) RegenerateKeys(ctx context.Context, id string) (*S3Keys, error) { uri := fmt.Sprintf("/v2/object-storage/%s/regenerate-keys", id) req, err := o.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return nil, err } s3Keys := new(s3KeysBase) if err = o.client.DoWithContext(ctx, req, s3Keys); err != nil { return nil, err } return s3Keys.S3Credentials, nil } golang-github-vultr-govultr-2.17.2/object_storage_test.go000066400000000000000000000136411425211421400235650ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestObjectStorageServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/object-storage", func(writer http.ResponseWriter, request *http.Request) { response := `{"object_storage":{"id":"39239784","date_created":"2020-07-1414:07:28","cluster_id":2,"region":"ewr","location":"New Jersey","label":"api-obj-storage2","status":"pending","s3_hostname":"","s3_access_key":"","s3_secret_key":""}}` fmt.Fprint(writer, response) }) objectStorage, err := client.ObjectStorage.Create(ctx, 2, "api-obj-storage2") if err != nil { t.Errorf("ObjectStorage.Create returned %+v", err) } expected := &ObjectStorage{ ID: "39239784", DateCreated: "2020-07-1414:07:28", ObjectStoreClusterID: 2, Region: "ewr", Location: "New Jersey", Label: "api-obj-storage2", Status: "pending", S3Keys: S3Keys{}, } if !reflect.DeepEqual(objectStorage, expected) { t.Errorf("ObjectStorage.Create returned %+v, expected %+v", objectStorage, expected) } } func TestObjectStorageServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/object-storage/39239784", func(writer http.ResponseWriter, request *http.Request) { response := `{"object_storage":{"id":"39239784","date_created":"2020-07-1414:07:28","cluster_id":2,"region":"ewr","label":"","status":"active","s3_hostname":"ewr1.vultrobjects.com","s3_access_key":"F123","s3_secret_key":"F1234"}}` fmt.Fprint(writer, response) }) s3, err := client.ObjectStorage.Get(ctx, "39239784") if err != nil { t.Errorf("ObjectStorage.Get returned %+v", err) } expected := &ObjectStorage{ ID: "39239784", DateCreated: "2020-07-1414:07:28", ObjectStoreClusterID: 2, Region: "ewr", Label: "", Status: "active", S3Keys: S3Keys{ S3Hostname: "ewr1.vultrobjects.com", S3AccessKey: "F123", S3SecretKey: "F1234", }, } if !reflect.DeepEqual(s3, expected) { t.Errorf("ObjectStorage.Get returned %+v, expected %+v", s3, expected) } } func TestObjectStorageServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/object-storage/1234", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ObjectStorage.Update(ctx, "1234", "s3 label") if err != nil { t.Errorf("ObjectStorage.Create returned %+v", err) } } func TestObjectStorageServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/object-storage/1234", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ObjectStorage.Delete(ctx, "1234") if err != nil { t.Errorf("ObjectStorage.Delete returned %+v", err) } } func TestObjectStorageServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/object-storage", func(writer http.ResponseWriter, request *http.Request) { response := `{"object_storages":[{"id":"39240368","date_created":"2020-07-1414:22:38","cluster_id":2,"region":"ewr","label":"govultr","status":"active","s3_hostname":"ewr1.vultrobjects.com","s3_access_key":"n1234","s3_secret_key":"b1234"}],"meta":{"total":1,"links":{"next":"","prev":""}}}` fmt.Fprint(writer, response) }) s3s, meta, err := client.ObjectStorage.List(ctx, nil) if err != nil { t.Errorf("ObjectStorage.List returned %+v", err) } expectedObject := []ObjectStorage{ { ID: "39240368", DateCreated: "2020-07-1414:22:38", ObjectStoreClusterID: 2, Region: "ewr", Label: "govultr", Status: "active", S3Keys: S3Keys{ S3Hostname: "ewr1.vultrobjects.com", S3AccessKey: "n1234", S3SecretKey: "b1234", }, }, } if !reflect.DeepEqual(s3s, expectedObject) { t.Errorf("ObjectStorage.List object returned %+v, expected %+v", s3s, expectedObject) } expectedmeta := &Meta{ Total: 1, Links: &Links{}, } if !reflect.DeepEqual(meta, expectedmeta) { t.Errorf("ObjectStorage.List meta object returned %+v, expected %+v", meta, expectedmeta) } } func TestObjectStorageServiceHandler_ListCluster(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/object-storage/clusters", func(writer http.ResponseWriter, request *http.Request) { response := `{"clusters":[{"id":2,"region":"ewr","hostname":"ewr1.vultrobjects.com","deploy":"yes"}],"meta":{"total":1,"links":{"next":"","prev":""}}}` fmt.Fprint(writer, response) }) clusters, meta, err := client.ObjectStorage.ListCluster(ctx, nil) if err != nil { t.Errorf("ObjectStorage.ListCluster returned %+v", err) } expected := []ObjectStorageCluster{ { ID: 2, Region: "ewr", Hostname: "ewr1.vultrobjects.com", Deploy: "yes", }, } if !reflect.DeepEqual(clusters, expected) { t.Errorf("ObjectStorage.ListCluster clusters returned %+v, expected %+v", clusters, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{}, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("ObjectStorage.List meta object returned %+v, expected %+v", meta, expectedMeta) } } func TestObjectStorageServiceHandler_RegenerateKeys(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/object-storage/1234/regenerate-keys", func(writer http.ResponseWriter, request *http.Request) { response := `{"s3_credentials":{"s3_hostname":"ewr1.vultrobjects.com","s3_access_key":"f1234","s3_secret_key":"g1234"}}` fmt.Fprint(writer, response) }) s3Keys, err := client.ObjectStorage.RegenerateKeys(ctx, "1234") if err != nil { t.Errorf("ObjectStorage.RegenerateKeys returned %+v", err) } expected := &S3Keys{ S3Hostname: "ewr1.vultrobjects.com", S3AccessKey: "f1234", S3SecretKey: "g1234", } if !reflect.DeepEqual(s3Keys, expected) { t.Errorf("ObjectStorage.RegenerateKeys returned %+v, expected %+v", s3Keys, expected) } } golang-github-vultr-govultr-2.17.2/os.go000066400000000000000000000024331425211421400201520ustar00rootroot00000000000000package govultr import ( "context" "net/http" "github.com/google/go-querystring/query" ) // OSService is the interface to interact with the operating system endpoint on the Vultr API // Link : https://www.vultr.com/api/#tag/os type OSService interface { List(ctx context.Context, options *ListOptions) ([]OS, *Meta, error) } // OSServiceHandler handles interaction with the operating system methods for the Vultr API type OSServiceHandler struct { client *Client } // OS represents a Vultr operating system type OS struct { ID int `json:"id"` Name string `json:"name"` Arch string `json:"arch"` Family string `json:"family"` } type osBase struct { OS []OS `json:"os"` Meta *Meta `json:"meta"` } var _ OSService = &OSServiceHandler{} // List retrieves a list of available operating systems. func (o *OSServiceHandler) List(ctx context.Context, options *ListOptions) ([]OS, *Meta, error) { uri := "/v2/os" req, err := o.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() os := new(osBase) if err = o.client.DoWithContext(ctx, req, os); err != nil { return nil, nil, err } return os.OS, os.Meta, nil } golang-github-vultr-govultr-2.17.2/os_test.go000066400000000000000000000016361425211421400212150ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestOSServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/os", func(w http.ResponseWriter, r *http.Request) { response := `{"os":[{"id":124,"name":"Windows 2012 R2 x64","arch":"x64","family":"windows"}],"meta":{"total":27,"links":{"next":"","prev":""}}}` fmt.Fprint(w, response) }) os, meta, err := client.OS.List(ctx, nil) if err != nil { t.Errorf("OS.List returned error: %v", err) } expectedOS := []OS{ { ID: 124, Name: "Windows 2012 R2 x64", Arch: "x64", Family: "windows", }, } expectedMeta := &Meta{ Total: 27, Links: &Links{}, } if !reflect.DeepEqual(os, expectedOS) { t.Errorf("OS.List os returned %+v, expected %+v", os, expectedOS) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("OS.List meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/plans.go000066400000000000000000000060251425211421400206470ustar00rootroot00000000000000package govultr import ( "context" "net/http" "github.com/google/go-querystring/query" ) // PlanService is the interface to interact with the Plans endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/plans type PlanService interface { List(ctx context.Context, planType string, options *ListOptions) ([]Plan, *Meta, error) ListBareMetal(ctx context.Context, options *ListOptions) ([]BareMetalPlan, *Meta, error) } // PlanServiceHandler handles interaction with the Plans methods for the Vultr API type PlanServiceHandler struct { client *Client } // BareMetalPlan represents bare metal plans type BareMetalPlan struct { ID string `json:"id"` CPUCount int `json:"cpu_count"` CPUModel string `json:"cpu_model"` CPUThreads int `json:"cpu_threads"` RAM int `json:"ram"` Disk int `json:"disk"` DiskCount int `json:"disk_count"` Bandwidth int `json:"bandwidth"` MonthlyCost float32 `json:"monthly_cost"` Type string `json:"type"` Locations []string `json:"locations"` } // Plan represents vc2, vdc, or vhf type Plan struct { ID string `json:"id"` VCPUCount int `json:"vcpu_count"` RAM int `json:"ram"` Disk int `json:"disk"` DiskCount int `json:"disk_count"` Bandwidth int `json:"bandwidth"` MonthlyCost float32 `json:"monthly_cost"` Type string `json:"type"` GPUVRAM int `json:"gpu_vram_gb,omitempty"` GPUType string `json:"gpu_type,omitempty"` Locations []string `json:"locations"` } type plansBase struct { Plans []Plan `json:"plans"` Meta *Meta `json:"meta"` } type bareMetalPlansBase struct { Plans []BareMetalPlan `json:"plans_metal"` Meta *Meta `json:"meta"` } // List retrieves a list of all active plans. // planType is optional - pass an empty string to get all plans func (p *PlanServiceHandler) List(ctx context.Context, planType string, options *ListOptions) ([]Plan, *Meta, error) { uri := "/v2/plans" req, err := p.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } if planType != "" { newValues.Add("type", planType) } req.URL.RawQuery = newValues.Encode() plans := new(plansBase) if err = p.client.DoWithContext(ctx, req, plans); err != nil { return nil, nil, err } return plans.Plans, plans.Meta, nil } // ListBareMetal all active bare metal plans. func (p *PlanServiceHandler) ListBareMetal(ctx context.Context, options *ListOptions) ([]BareMetalPlan, *Meta, error) { uri := "/v2/plans-metal" req, err := p.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() bmPlans := new(bareMetalPlansBase) if err = p.client.DoWithContext(ctx, req, bmPlans); err != nil { return nil, nil, err } return bmPlans.Plans, bmPlans.Meta, nil } golang-github-vultr-govultr-2.17.2/plans_test.go000066400000000000000000000047471425211421400217170ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestPlanServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/plans", func(writer http.ResponseWriter, request *http.Request) { response := `{ "plans":[{ "id": "vc2-16c-64gb", "vcpu_count": 16, "ram": 65536, "disk": 1280, "disk_count": 1, "bandwidth": 10240, "monthly_cost": 320, "type": "vc2", "locations": [ "dfw"]}], "meta": { "total": 19, "links": { "next": "", "prev": "" } }}` fmt.Fprint(writer, response) }) plans, meta, err := client.Plan.List(ctx, "vc2", nil) if err != nil { t.Errorf("Plan.List returned %+v", err) } expectedPlan := []Plan{ { ID: "vc2-16c-64gb", VCPUCount: 16, RAM: 65536, Disk: 1280, DiskCount: 1, Bandwidth: 10240, MonthlyCost: 320.00, Type: "vc2", Locations: []string{ "dfw", }, }, } expectedMeta := &Meta{ Total: 19, Links: &Links{}, } if !reflect.DeepEqual(plans, expectedPlan) { t.Errorf("Plan.List plans returned %+v, expected %+v", plans, expectedPlan) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Plan.List meta returned %+v, expected %+v", meta, expectedMeta) } } func TestPlanServiceHandler_GetBareMetalList(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/plans-metal", func(writer http.ResponseWriter, request *http.Request) { response := `{ "plans_metal":[{"id": "vbm-4c-32gb","cpu_count": 4,"cpu_threads": 8,"cpu_model": "E3-1270v6","ram": 32768,"disk": 240, "disk_count": 1, "bandwidth": 5120,"monthly_cost": 300,"type": "SSD", "locations": [ "dwf"]}], "meta": { "total": 19, "links": { "next": "", "prev": "" } }}` fmt.Fprint(writer, response) }) bareMetalPlans, meta, err := client.Plan.ListBareMetal(ctx, nil) if err != nil { t.Errorf("Plan.GetBareMetalList returned %+v", err) } expectedPlan := []BareMetalPlan{ { ID: "vbm-4c-32gb", CPUCount: 4, CPUModel: "E3-1270v6", CPUThreads: 8, RAM: 32768, Disk: 240, DiskCount: 1, Bandwidth: 5120, MonthlyCost: 300, Type: "SSD", Locations: []string{ "dwf", }, }, } expectedMeta := &Meta{ Total: 19, Links: &Links{}, } if !reflect.DeepEqual(bareMetalPlans, expectedPlan) { t.Errorf("Plan.GetBareMetalList returned %+v, expected %+v", bareMetalPlans, expectedPlan) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Plan.List meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/regions.go000066400000000000000000000044121425211421400211760ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // RegionService is the interface to interact with Region endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/region type RegionService interface { Availability(ctx context.Context, regionID string, planType string) (*PlanAvailability, error) List(ctx context.Context, options *ListOptions) ([]Region, *Meta, error) } var _ RegionService = &RegionServiceHandler{} // RegionServiceHandler handles interaction with the region methods for the Vultr API type RegionServiceHandler struct { client *Client } // Region represents a Vultr region type Region struct { ID string `json:"id"` City string `json:"city"` Country string `json:"country"` Continent string `json:"continent,omitempty"` Options []string `json:"options"` } type regionBase struct { Regions []Region `json:"regions"` Meta *Meta } // PlanAvailability contains all available plans. type PlanAvailability struct { AvailablePlans []string `json:"available_plans"` } // List returns all available regions func (r *RegionServiceHandler) List(ctx context.Context, options *ListOptions) ([]Region, *Meta, error) { uri := "/v2/regions" req, err := r.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() regions := new(regionBase) if err = r.client.DoWithContext(ctx, req, ®ions); err != nil { return nil, nil, err } return regions.Regions, regions.Meta, nil } // Availability retrieves a list of the plan IDs currently available for a given location. func (r *RegionServiceHandler) Availability(ctx context.Context, regionID string, planType string) (*PlanAvailability, error) { uri := fmt.Sprintf("/v2/regions/%s/availability", regionID) req, err := r.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } // Optional planType filter if planType != "" { q := req.URL.Query() q.Add("type", planType) req.URL.RawQuery = q.Encode() } plans := new(PlanAvailability) if err = r.client.DoWithContext(ctx, req, plans); err != nil { return nil, err } return plans, nil } golang-github-vultr-govultr-2.17.2/regions_test.go000066400000000000000000000033131425211421400222340ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestRegionServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/regions", func(writer http.ResponseWriter, request *http.Request) { response := `{"regions":[{"id":"ams","city": "test", "country":"NL","continent":"Europe","options":["ddos_protection"]}],"meta":{"total":1,"links":{"next":"","prev":""}}}` fmt.Fprint(writer, response) }) region, meta, err := client.Region.List(ctx, nil) if err != nil { t.Errorf("Region.List returned error: %v", err) } expectedRegion := []Region{ { ID: "ams", City: "test", Country: "NL", Continent: "Europe", Options: []string{"ddos_protection"}, }, } expectedMeta := &Meta{ Total: 1, Links: &Links{}, } if !reflect.DeepEqual(region, expectedRegion) { t.Errorf("Region.List region returned %+v, expected %+v", region, expectedRegion) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Region.List meta returned %+v, expected %+v", meta, expectedMeta) } } func TestRegionServiceHandler_Availability(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/regions/ewr/availability", func(writer http.ResponseWriter, request *http.Request) { response := `{"available_plans":["vc2-1c-1gb","vc2-1c-2gb","vc2-2c-4gb"]}` fmt.Fprint(writer, response) }) region, err := client.Region.Availability(ctx, "ewr", "") if err != nil { t.Errorf("Region.Availability returned error: %v", err) } expected := &PlanAvailability{AvailablePlans: []string{"vc2-1c-1gb", "vc2-1c-2gb", "vc2-2c-4gb"}} if !reflect.DeepEqual(region, expected) { t.Errorf("Region.Availability returned %+v, expected %+v", region, expected) } } golang-github-vultr-govultr-2.17.2/reserved_ip.go000066400000000000000000000131231425211421400220360ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const ripPath = "/v2/reserved-ips" // ReservedIPService is the interface to interact with the reserved IP endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/reserved-ip type ReservedIPService interface { Create(ctx context.Context, ripCreate *ReservedIPReq) (*ReservedIP, error) Update(ctx context.Context, id string, ripUpdate *ReservedIPUpdateReq) (*ReservedIP, error) Get(ctx context.Context, id string) (*ReservedIP, error) Delete(ctx context.Context, id string) error List(ctx context.Context, options *ListOptions) ([]ReservedIP, *Meta, error) Convert(ctx context.Context, ripConvert *ReservedIPConvertReq) (*ReservedIP, error) Attach(ctx context.Context, id, instance string) error Detach(ctx context.Context, id string) error } // ReservedIPServiceHandler handles interaction with the reserved IP methods for the Vultr API type ReservedIPServiceHandler struct { client *Client } // ReservedIP represents an reserved IP on Vultr type ReservedIP struct { ID string `json:"id"` Region string `json:"region"` IPType string `json:"ip_type"` Subnet string `json:"subnet"` SubnetSize int `json:"subnet_size"` Label string `json:"label"` InstanceID string `json:"instance_id"` } // ReservedIPReq represents the parameters for creating a new Reserved IP on Vultr type ReservedIPReq struct { Region string `json:"region,omitempty"` IPType string `json:"ip_type,omitempty"` IPAddress string `json:"ip_address,omitempty"` Label string `json:"label,omitempty"` InstanceID string `json:"instance_id,omitempty"` } // ReservedIPUpdateReq represents the parameters for updating a Reserved IP on Vultr type ReservedIPUpdateReq struct { Label *string `json:"label"` } type reservedIPsBase struct { ReservedIPs []ReservedIP `json:"reserved_ips"` Meta *Meta `json:"meta"` } type reservedIPBase struct { ReservedIP *ReservedIP `json:"reserved_ip"` } // ReservedIPConvertReq is the struct used for create and update calls. type ReservedIPConvertReq struct { IPAddress string `json:"ip_address,omitempty"` Label string `json:"label,omitempty"` } // Create adds the specified reserved IP to your Vultr account func (r *ReservedIPServiceHandler) Create(ctx context.Context, ripCreate *ReservedIPReq) (*ReservedIP, error) { req, err := r.client.NewRequest(ctx, http.MethodPost, ripPath, ripCreate) if err != nil { return nil, err } rip := new(reservedIPBase) if err = r.client.DoWithContext(ctx, req, rip); err != nil { return nil, err } return rip.ReservedIP, nil } // Update updates label on the Reserved IP func (r *ReservedIPServiceHandler) Update(ctx context.Context, id string, ripUpdate *ReservedIPUpdateReq) (*ReservedIP, error) { uri := fmt.Sprintf("%s/%s", ripPath, id) req, err := r.client.NewRequest(ctx, http.MethodPatch, uri, ripUpdate) if err != nil { return nil, err } rip := new(reservedIPBase) if err = r.client.DoWithContext(ctx, req, rip); err != nil { return nil, err } return rip.ReservedIP, nil } // Get gets the reserved IP associated with provided ID func (r *ReservedIPServiceHandler) Get(ctx context.Context, id string) (*ReservedIP, error) { uri := fmt.Sprintf("%s/%s", ripPath, id) req, err := r.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } rip := new(reservedIPBase) if err = r.client.DoWithContext(ctx, req, rip); err != nil { return nil, err } return rip.ReservedIP, nil } // Delete removes the specified reserved IP from your Vultr account func (r *ReservedIPServiceHandler) Delete(ctx context.Context, id string) error { uri := fmt.Sprintf("%s/%s", ripPath, id) req, err := r.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return r.client.DoWithContext(ctx, req, nil) } // List lists all the reserved IPs associated with your Vultr account func (r *ReservedIPServiceHandler) List(ctx context.Context, options *ListOptions) ([]ReservedIP, *Meta, error) { req, err := r.client.NewRequest(ctx, http.MethodGet, ripPath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() ips := new(reservedIPsBase) if err = r.client.DoWithContext(ctx, req, ips); err != nil { return nil, nil, err } return ips.ReservedIPs, ips.Meta, nil } // Convert an existing IP on a subscription to a reserved IP. func (r *ReservedIPServiceHandler) Convert(ctx context.Context, ripConvert *ReservedIPConvertReq) (*ReservedIP, error) { uri := fmt.Sprintf("%s/convert", ripPath) req, err := r.client.NewRequest(ctx, http.MethodPost, uri, ripConvert) if err != nil { return nil, err } rip := new(reservedIPBase) if err = r.client.DoWithContext(ctx, req, rip); err != nil { return nil, err } return rip.ReservedIP, nil } // Attach a reserved IP to an existing subscription func (r *ReservedIPServiceHandler) Attach(ctx context.Context, id, instance string) error { uri := fmt.Sprintf("%s/%s/attach", ripPath, id) reqBody := RequestBody{"instance_id": instance} req, err := r.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return r.client.DoWithContext(ctx, req, nil) } // Detach a reserved IP from an existing subscription. func (r *ReservedIPServiceHandler) Detach(ctx context.Context, id string) error { uri := fmt.Sprintf("%s/%s/detach", ripPath, id) req, err := r.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return r.client.DoWithContext(ctx, req, nil) } golang-github-vultr-govultr-2.17.2/reserved_ip_test.go000066400000000000000000000136441425211421400231050ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) // get test func TestReservedIPServiceHandler_Attach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/reserved-ips/12345/attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) if err := client.ReservedIP.Attach(ctx, "12345", "1234"); err != nil { t.Errorf("ReservedIP.Attach returned %+v, expected %+v", err, nil) } } func TestReservedIPServiceHandler_Convert(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/reserved-ips/convert", func(writer http.ResponseWriter, request *http.Request) { response := ` { "reserved_ip": { "id": "1312965", "region": "ewr", "ip_type": "v4", "subnet": "111.111.111.111", "subnet_size": 32, "label": "my first reserved ip", "instance_id": "1234" } } ` fmt.Fprint(writer, response) }) options := &ReservedIPConvertReq{ IPAddress: "111.111.111.111", Label: "my first reserved ip", } ip, err := client.ReservedIP.Convert(ctx, options) if err != nil { t.Errorf("ReservedIP.Convert returned %+v, expected %+v", err, nil) } expected := &ReservedIP{ ID: "1312965", Region: "ewr", IPType: "v4", Subnet: "111.111.111.111", SubnetSize: 32, Label: "my first reserved ip", InstanceID: "1234", } if !reflect.DeepEqual(ip, expected) { t.Errorf("ReservedIP.Convert returned %+v, expected %+v", ip, expected) } } func TestReservedIPServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/reserved-ips", func(writer http.ResponseWriter, request *http.Request) { response := ` { "reserved_ip": { "id": "1313044", "region": "ewr", "ip_type": "v4", "subnet": "10.234.22.53", "subnet_size": 32, "label": "my first reserved ip", "instance_id": "" } } ` fmt.Fprint(writer, response) }) options := &ReservedIPReq{ IPType: "v4", Label: "my first reserved ip", Region: "ewr", } ip, err := client.ReservedIP.Create(ctx, options) if err != nil { t.Errorf("ReservedIP.Create returned %+v, expected %+v", err, nil) } expected := &ReservedIP{ ID: "1313044", Region: "ewr", IPType: "v4", Subnet: "10.234.22.53", SubnetSize: 32, Label: "my first reserved ip", InstanceID: "", } if !reflect.DeepEqual(ip, expected) { t.Errorf("ReservedIP.Create returned %+v, expected %+v", ip, expected) } } func TestReservedIPServiceHandler_Update(t *testing.T) { setup() defer teardown() options := &ReservedIPUpdateReq{ Label: StringToStringPtr("my first reserved ip updated"), } mux.HandleFunc("/v2/reserved-ips/12345", func(writer http.ResponseWriter, request *http.Request) { response := ` { "reserved_ip": { "id": "12345", "region": "yto", "ip_type": "v4", "subnet": "10.234.22.53", "subnet_size": 32, "label": "my first reserved ip updated", "instance_id": "123456" } } ` fmt.Fprintf(writer, response) }) ip, err := client.ReservedIP.Update(ctx, "12345", options) expected := &ReservedIP{ ID: "12345", Region: "yto", IPType: "v4", Subnet: "10.234.22.53", SubnetSize: 32, Label: "my first reserved ip updated", InstanceID: "123456", } if err != nil { t.Errorf("ReservedIP.Update returned %+v, expected %+v", err, nil) } if !reflect.DeepEqual(ip, expected) { t.Errorf("ReservedIP.Update returned %+v, expected %+v", ip, expected) } } func TestReservedIPServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/reserved-ips/12345", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ReservedIP.Delete(ctx, "12345") if err != nil { t.Errorf("ReservedIP.Delete returned %+v, expected %+v", err, nil) } } func TestReservedIPServiceHandler_Detach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/reserved-ips/12345/detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ReservedIP.Detach(ctx, "12345") if err != nil { t.Errorf("ReservedIP.Detach returned %+v, expected %+v", err, nil) } } func TestReservedIPServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/reserved-ips/1313044", func(writer http.ResponseWriter, request *http.Request) { response := ` { "reserved_ip": { "id": "1313044", "region": "ewr", "ip_type": "v4", "subnet": "10.234.22.53", "subnet_size": 32, "label": "my first reserved ip", "instance_id": "123456" } } ` fmt.Fprintf(writer, response) }) ip, err := client.ReservedIP.Get(ctx, "1313044") if err != nil { t.Errorf("ReservedIP.Get returned error: %v", err) } expected := &ReservedIP{ ID: "1313044", Region: "ewr", IPType: "v4", Subnet: "10.234.22.53", SubnetSize: 32, Label: "my first reserved ip", InstanceID: "123456", } if !reflect.DeepEqual(ip, expected) { t.Errorf("ReservedIP.Get returned %+v, expected %+v", ip, expected) } } func TestReservedIPServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/reserved-ips", func(writer http.ResponseWriter, request *http.Request) { response := ` { "reserved_ips": [{ "id": "1313044", "region": "ewr", "ip_type": "v4", "subnet": "10.234.22.53", "subnet_size": 32, "label": "my first reserved ip", "instance_id": "123456" }] } ` fmt.Fprintf(writer, response) }) ips, _, err := client.ReservedIP.List(ctx, nil) if err != nil { t.Errorf("ReservedIP.List returned error: %v", err) } expected := []ReservedIP{ { ID: "1313044", Region: "ewr", IPType: "v4", Subnet: "10.234.22.53", SubnetSize: 32, Label: "my first reserved ip", InstanceID: "123456", }, } if !reflect.DeepEqual(ips, expected) { t.Errorf("ReservedIP.List returned %+v, expected %+v", ips, expected) } } golang-github-vultr-govultr-2.17.2/snapshot.go000066400000000000000000000076361425211421400214020ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // SnapshotService is the interface to interact with Snapshot endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/snapshot type SnapshotService interface { Create(ctx context.Context, snapshotReq *SnapshotReq) (*Snapshot, error) CreateFromURL(ctx context.Context, snapshotURLReq *SnapshotURLReq) (*Snapshot, error) Get(ctx context.Context, snapshotID string) (*Snapshot, error) Delete(ctx context.Context, snapshotID string) error List(ctx context.Context, options *ListOptions) ([]Snapshot, *Meta, error) } // SnapshotServiceHandler handles interaction with the snapshot methods for the Vultr API type SnapshotServiceHandler struct { client *Client } // Snapshot represents a Vultr snapshot type Snapshot struct { ID string `json:"id"` DateCreated string `json:"date_created"` Description string `json:"description"` Size int `json:"size"` CompressedSize int `json:"compressed_size"` Status string `json:"status"` OsID int `json:"os_id"` AppID int `json:"app_id"` } // SnapshotReq struct is used to create snapshots. type SnapshotReq struct { InstanceID string `json:"instance_id,omitempty"` Description string `json:"description,omitempty"` } // SnapshotURLReq struct is used to create snapshots from a URL. type SnapshotURLReq struct { URL string `json:"url"` Description string `json:"description,omitempty"` } type snapshotsBase struct { Snapshots []Snapshot `json:"snapshots"` Meta *Meta `json:"meta"` } type snapshotBase struct { Snapshot *Snapshot `json:"snapshot"` } // Create makes a snapshot of a provided server func (s *SnapshotServiceHandler) Create(ctx context.Context, snapshotReq *SnapshotReq) (*Snapshot, error) { uri := "/v2/snapshots" req, err := s.client.NewRequest(ctx, http.MethodPost, uri, snapshotReq) if err != nil { return nil, err } snapshot := new(snapshotBase) if err = s.client.DoWithContext(ctx, req, snapshot); err != nil { return nil, err } return snapshot.Snapshot, nil } // CreateFromURL will create a snapshot based on an image iso from a URL you provide func (s *SnapshotServiceHandler) CreateFromURL(ctx context.Context, snapshotURLReq *SnapshotURLReq) (*Snapshot, error) { uri := "/v2/snapshots/create-from-url" req, err := s.client.NewRequest(ctx, http.MethodPost, uri, snapshotURLReq) if err != nil { return nil, err } snapshot := new(snapshotBase) if err = s.client.DoWithContext(ctx, req, snapshot); err != nil { return nil, err } return snapshot.Snapshot, nil } // Get a specific snapshot func (s *SnapshotServiceHandler) Get(ctx context.Context, snapshotID string) (*Snapshot, error) { uri := fmt.Sprintf("/v2/snapshots/%s", snapshotID) req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } snapshot := new(snapshotBase) if err = s.client.DoWithContext(ctx, req, snapshot); err != nil { return nil, err } return snapshot.Snapshot, nil } // Delete a snapshot. func (s *SnapshotServiceHandler) Delete(ctx context.Context, snapshotID string) error { uri := fmt.Sprintf("/v2/snapshots/%s", snapshotID) req, err := s.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return s.client.DoWithContext(ctx, req, nil) } // List all available snapshots. func (s *SnapshotServiceHandler) List(ctx context.Context, options *ListOptions) ([]Snapshot, *Meta, error) { uri := "/v2/snapshots" req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() snapshots := new(snapshotsBase) if err = s.client.DoWithContext(ctx, req, snapshots); err != nil { return nil, nil, err } return snapshots.Snapshots, snapshots.Meta, nil } golang-github-vultr-govultr-2.17.2/snapshot_test.go000066400000000000000000000111731425211421400224300ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestSnapshotServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/snapshots", func(writer http.ResponseWriter, request *http.Request) { response := `{"snapshot":{"id": "5359435d28b9a","date_created": "2014-04-18 12:40:40","description": "Test snapshot","size": 42949672960,"compressed_size": 1078864689,"status": "complete","os_id": 127,"app_id": 0}}` fmt.Fprint(writer, response) }) snap := &SnapshotReq{ InstanceID: "12345", Description: "Test snapshot", } snapshot, err := client.Snapshot.Create(ctx, snap) if err != nil { t.Errorf("Snapshot.Create returned error: %v", err) } expected := &Snapshot{ ID: "5359435d28b9a", DateCreated: "2014-04-18 12:40:40", Description: "Test snapshot", Size: 42949672960, CompressedSize: 1078864689, Status: "complete", OsID: 127, AppID: 0, } if !reflect.DeepEqual(snapshot, expected) { t.Errorf("Snapshot.Create returned %+v, expected %+v", snapshot, expected) } } func TestSnapshotServiceHandler_CreateFromURL(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/snapshots/create-from-url", func(writer http.ResponseWriter, request *http.Request) { response := `{"snapshot":{"id": "5359435d28b9a","date_created": "2014-04-18 12:40:40","description": "Test snapshot","size": 42949672960,"compressed_size" : 1078864689,"status": "complete","os_id": 127,"app_id": 0}}` fmt.Fprint(writer, response) }) snap := SnapshotURLReq{URL: "http://vultr.com"} snapshot, err := client.Snapshot.CreateFromURL(ctx, &snap) if err != nil { t.Errorf("Snapshot.CreateFromURL returned error: %v", err) } expected := &Snapshot{ ID: "5359435d28b9a", DateCreated: "2014-04-18 12:40:40", Description: "Test snapshot", Size: 42949672960, CompressedSize: 1078864689, Status: "complete", OsID: 127, AppID: 0, } if !reflect.DeepEqual(snapshot, expected) { t.Errorf("Snapshot.CreateFromURL returned %+v, expected %+v", snapshot, expected) } } func TestSnapshotServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/snapshots/5359435d28b9a", func(writer http.ResponseWriter, request *http.Request) { response := `{"snapshot":{"id": "5359435d28b9a","date_created": "2014-04-18 12:40:40","description": "Test snapshot","size": 42949672960,"compressed_size": 1078864689,"status": "complete","os_id": 127,"app_id": 0}}` fmt.Fprint(writer, response) }) snapshot, err := client.Snapshot.Get(ctx, "5359435d28b9a") if err != nil { t.Errorf("Snapshot.Get returned error: %v", err) } expected := &Snapshot{ ID: "5359435d28b9a", DateCreated: "2014-04-18 12:40:40", Description: "Test snapshot", Size: 42949672960, CompressedSize: 1078864689, Status: "complete", OsID: 127, AppID: 0, } if !reflect.DeepEqual(snapshot, expected) { t.Errorf("Snapshot.Get returned %+v, expected %+v", snapshot, expected) } } func TestSnapshotServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/snapshots/7a05cbf361d98", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Snapshot.Delete(ctx, "7a05cbf361d98") if err != nil { t.Errorf("Snapshot.Delete returned %+v, expected %+v", err, nil) } } func TestSnapshotServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/snapshots", func(writer http.ResponseWriter, request *http.Request) { response := `{"snapshots": [{"id": "885ee0f4f263c","date_created": "2014-04-18 12:40:40","description": "Test snapshot","size": 42949672960,"compressed_size": 1078864689,"status": "complete","os_id": 127,"app_id": 0}],"meta": {"total": 4,"links": {"next": "","prev": ""}}}` fmt.Fprint(writer, response) }) snapshots, meta, err := client.Snapshot.List(ctx, nil) if err != nil { t.Errorf("Snapshot.List returned error: %v", err) } expectedSnap := []Snapshot{ { ID: "885ee0f4f263c", DateCreated: "2014-04-18 12:40:40", Description: "Test snapshot", Size: 42949672960, CompressedSize: 1078864689, Status: "complete", OsID: 127, AppID: 0, }, } expectedMeta := &Meta{ Total: 4, Links: &Links{}, } if !reflect.DeepEqual(snapshots, expectedSnap) { t.Errorf("Snapshot.list snapshots returned %+v, expected %+v", snapshots, expectedSnap) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("Snapshot.list meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/ssh_key.go000066400000000000000000000064171425211421400212040ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) // SSHKeyService is the interface to interact with the SSH Key endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/ssh type SSHKeyService interface { Create(ctx context.Context, sshKeyReq *SSHKeyReq) (*SSHKey, error) Get(ctx context.Context, sshKeyID string) (*SSHKey, error) Update(ctx context.Context, sshKeyID string, sshKeyReq *SSHKeyReq) error Delete(ctx context.Context, sshKeyID string) error List(ctx context.Context, options *ListOptions) ([]SSHKey, *Meta, error) } // SSHKeyServiceHandler handles interaction with the SSH Key methods for the Vultr API type SSHKeyServiceHandler struct { client *Client } // SSHKey represents an SSH Key on Vultr type SSHKey struct { ID string `json:"id"` Name string `json:"name"` SSHKey string `json:"ssh_key"` DateCreated string `json:"date_created"` } // SSHKeyReq is the ssh key struct for create and update calls type SSHKeyReq struct { Name string `json:"name,omitempty"` SSHKey string `json:"ssh_key,omitempty"` } type sshKeysBase struct { SSHKeys []SSHKey `json:"ssh_keys"` Meta *Meta `json:"meta"` } type sshKeyBase struct { SSHKey *SSHKey `json:"ssh_key"` } // Create a ssh key func (s *SSHKeyServiceHandler) Create(ctx context.Context, sshKeyReq *SSHKeyReq) (*SSHKey, error) { uri := "/v2/ssh-keys" req, err := s.client.NewRequest(ctx, http.MethodPost, uri, sshKeyReq) if err != nil { return nil, err } key := new(sshKeyBase) if err = s.client.DoWithContext(ctx, req, key); err != nil { return nil, err } return key.SSHKey, nil } // Get a specific ssh key. func (s *SSHKeyServiceHandler) Get(ctx context.Context, sshKeyID string) (*SSHKey, error) { uri := fmt.Sprintf("/v2/ssh-keys/%s", sshKeyID) req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } sshKey := new(sshKeyBase) if err = s.client.DoWithContext(ctx, req, sshKey); err != nil { return nil, err } return sshKey.SSHKey, nil } // Update will update the given SSH Key. Empty strings will be ignored. func (s *SSHKeyServiceHandler) Update(ctx context.Context, sshKeyID string, sshKeyReq *SSHKeyReq) error { uri := fmt.Sprintf("/v2/ssh-keys/%s", sshKeyID) req, err := s.client.NewRequest(ctx, http.MethodPatch, uri, sshKeyReq) if err != nil { return err } return s.client.DoWithContext(ctx, req, nil) } // Delete a specific ssh-key. func (s *SSHKeyServiceHandler) Delete(ctx context.Context, sshKeyID string) error { uri := fmt.Sprintf("/v2/ssh-keys/%s", sshKeyID) req, err := s.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return s.client.DoWithContext(ctx, req, nil) } // List all available SSH Keys. func (s *SSHKeyServiceHandler) List(ctx context.Context, options *ListOptions) ([]SSHKey, *Meta, error) { uri := "/v2/ssh-keys" req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() sshKeys := new(sshKeysBase) if err = s.client.DoWithContext(ctx, req, sshKeys); err != nil { return nil, nil, err } return sshKeys.SSHKeys, sshKeys.Meta, nil } golang-github-vultr-govultr-2.17.2/ssh_key_test.go000066400000000000000000000070441425211421400222400ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestSSHKeyServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/ssh-keys", func(writer http.ResponseWriter, request *http.Request) { response := `{"ssh_key": {"id": "5f05d5a71fe28","date_created": "2020-07-08 14:18:15","name": "api-test-ssh","ssh_key": "ssh-rsa AF+LbfYYw== test@admin.com"}}` fmt.Fprint(writer, response) }) sshKey := &SSHKeyReq{ Name: "api-test-ssh", SSHKey: "ssh-rsa AF+LbfYYw== test@admin.com", } key, err := client.SSHKey.Create(ctx, sshKey) if err != nil { t.Errorf("SSHKey.Create returned %+v, expected %+v", err, nil) } expected := &SSHKey{ ID: "5f05d5a71fe28", Name: "api-test-ssh", SSHKey: "ssh-rsa AF+LbfYYw== test@admin.com", DateCreated: "2020-07-08 14:18:15", } if !reflect.DeepEqual(key, expected) { t.Errorf("SSHKey.Create returned %+v, expected %+v", key, expected) } } func TestSSHKeyServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/ssh-keys/abc123", func(writer http.ResponseWriter, request *http.Request) { response := `{"ssh_key": {"id": "5f05d5a71fe28","date_created": "2020-07-08 14:18:15","name": "api-test-ssh","ssh_key": "ssh-rsa AF+LbfYYw== test@admin.com"}}` fmt.Fprint(writer, response) }) key, err := client.SSHKey.Get(ctx, "abc123") if err != nil { t.Errorf("SSHKey.Get returned %+v, expected %+v", err, nil) } expected := &SSHKey{ ID: "5f05d5a71fe28", Name: "api-test-ssh", SSHKey: "ssh-rsa AF+LbfYYw== test@admin.com", DateCreated: "2020-07-08 14:18:15", } if !reflect.DeepEqual(key, expected) { t.Errorf("SSHKey.Create returned %+v, expected %+v", key, expected) } } func TestSSHKeyServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/ssh-keys/abc123", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) sshKey := &SSHKeyReq{ Name: "foo", SSHKey: "ssh-rsa CCCCB3NzaC1yc your_username@hostname", } err := client.SSHKey.Update(ctx, "abc123", sshKey) if err != nil { t.Errorf("SSHKey.Update returned error: %+v", err) } } func TestSSHKeyServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/ssh-keys/abc123", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.SSHKey.Delete(ctx, "abc123") if err != nil { t.Errorf("SSHKey.Delete returned %+v, expected %+v", err, nil) } } func TestSSHKeyServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/ssh-keys", func(writer http.ResponseWriter, request *http.Request) { response := `{"ssh_keys": [{"id": "5ed139d1890db","date_created": "2020-05-29 16:35:29","name": "api-test-ssh","ssh_key": "ssh-rsa AAAAB3NzaC1ycYYw== test@admin.com"}],"meta": {"total": 8,"links": {"next": "","prev": ""}}}` fmt.Fprintf(writer, response) }) sshKeys, meta, err := client.SSHKey.List(ctx, nil) if err != nil { t.Errorf("SSHKey.List returned error: %v", err) } expectedSSH := []SSHKey{ { ID: "5ed139d1890db", Name: "api-test-ssh", SSHKey: "ssh-rsa AAAAB3NzaC1ycYYw== test@admin.com", DateCreated: "2020-05-29 16:35:29", }, } expectedMeta := &Meta{ Total: 8, Links: &Links{}, } if !reflect.DeepEqual(sshKeys, expectedSSH) { t.Errorf("SSHKey.List ssh-keys returned %+v, expected %+v", sshKeys, expectedSSH) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("SSHKey.List meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/startup_script.go000066400000000000000000000074561425211421400226310ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const scriptPath = "/v2/startup-scripts" // StartupScriptService is the interface to interact with the startup script endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/startup type StartupScriptService interface { Create(ctx context.Context, req *StartupScriptReq) (*StartupScript, error) Get(ctx context.Context, scriptID string) (*StartupScript, error) Update(ctx context.Context, scriptID string, scriptReq *StartupScriptReq) error Delete(ctx context.Context, scriptID string) error List(ctx context.Context, options *ListOptions) ([]StartupScript, *Meta, error) } // StartupScriptServiceHandler handles interaction with the startup script methods for the Vultr API type StartupScriptServiceHandler struct { client *Client } // StartupScript represents an startup script on Vultr type StartupScript struct { ID string `json:"id"` DateCreated string `json:"date_created"` DateModified string `json:"date_modified"` Name string `json:"name"` Type string `json:"type"` Script string `json:"script"` } // StartupScriptReq is the user struct for create and update calls type StartupScriptReq struct { Name string `json:"name"` Type string `json:"type"` Script string `json:"script"` } type startupScriptsBase struct { StartupScripts []StartupScript `json:"startup_scripts"` Meta *Meta `json:"meta"` } type startupScriptBase struct { StartupScript *StartupScript `json:"startup_script"` } var _ StartupScriptService = &StartupScriptServiceHandler{} // Create a startup script func (s *StartupScriptServiceHandler) Create(ctx context.Context, scriptReq *StartupScriptReq) (*StartupScript, error) { req, err := s.client.NewRequest(ctx, http.MethodPost, scriptPath, scriptReq) if err != nil { return nil, err } script := new(startupScriptBase) if err = s.client.DoWithContext(ctx, req, script); err != nil { return nil, err } return script.StartupScript, nil } // Get a single startup script func (s *StartupScriptServiceHandler) Get(ctx context.Context, scriptID string) (*StartupScript, error) { uri := fmt.Sprintf("%s/%s", scriptPath, scriptID) req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } script := new(startupScriptBase) if err = s.client.DoWithContext(ctx, req, script); err != nil { return nil, err } return script.StartupScript, nil } // Update will update the given startup script. Empty strings will be ignored. func (s *StartupScriptServiceHandler) Update(ctx context.Context, scriptID string, scriptReq *StartupScriptReq) error { uri := fmt.Sprintf("%s/%s", scriptPath, scriptID) req, err := s.client.NewRequest(ctx, http.MethodPatch, uri, scriptReq) if err != nil { return err } return s.client.DoWithContext(ctx, req, nil) } // Delete the specified startup script from your account. func (s *StartupScriptServiceHandler) Delete(ctx context.Context, scriptID string) error { uri := fmt.Sprintf("%s/%s", scriptPath, scriptID) req, err := s.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return s.client.DoWithContext(ctx, req, nil) } // List all the startup scripts associated with your Vultr account func (s *StartupScriptServiceHandler) List(ctx context.Context, options *ListOptions) ([]StartupScript, *Meta, error) { req, err := s.client.NewRequest(ctx, http.MethodGet, scriptPath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() scripts := new(startupScriptsBase) if err = s.client.DoWithContext(ctx, req, scripts); err != nil { return nil, nil, err } return scripts.StartupScripts, scripts.Meta, nil } golang-github-vultr-govultr-2.17.2/startup_script_test.go000066400000000000000000000076441425211421400236670ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "reflect" "testing" ) func TestStartupScriptServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/startup-scripts", func(writer http.ResponseWriter, request *http.Request) { response := `{"startup_script": {"id": "14356","date_created": "2020-07-07 18:52:56","date_modified": "2020-07-07 18:59:54","name": "govultr","type": "boot","script": "dGVzdGFwaXVwZGF0ZQ=="}}` fmt.Fprint(writer, response) }) script := &StartupScriptReq{ Name: "govultr", Type: "boot", Script: "dGVzdGFwaXVwZGF0ZQ==", } s, err := client.StartupScript.Create(context.Background(), script) if err != nil { t.Errorf("StartupScript.Create returned %+v, expected %+v", err, nil) } expected := &StartupScript{ ID: "14356", DateCreated: "2020-07-07 18:52:56", DateModified: "2020-07-07 18:59:54", Name: "govultr", Type: "boot", Script: "dGVzdGFwaXVwZGF0ZQ==", } if !reflect.DeepEqual(s, expected) { t.Errorf("StartupScript.Create returned %+v, expected %+v", s, expected) } } func TestStartupScriptServiceHandler_GET(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/startup-scripts/14350", func(writer http.ResponseWriter, request *http.Request) { response := `{"startup_script": {"id": "14350","date_created": "2020-06-08 17:58:10","date_modified": "2020-06-08 17:59:54","name": "govultr","type": "pxe","script": "dGVzdA=="}}` fmt.Fprintf(writer, response) }) scripts, err := client.StartupScript.Get(ctx, "14350") if err != nil { t.Errorf("StartupScript.Get returned error: %v", err) } expectedScript := &StartupScript{ ID: "14350", DateCreated: "2020-06-08 17:58:10", DateModified: "2020-06-08 17:59:54", Name: "govultr", Type: "pxe", Script: "dGVzdA==", } if !reflect.DeepEqual(scripts, expectedScript) { t.Errorf("StartupScript.Get scripts returned %+v, expected %+v", scripts, expectedScript) } } func TestStartupScriptServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/startup-scripts/1234", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) script := &StartupScriptReq{ Name: "foo", Type: "boot", Script: "dGVzdA==", } err := client.StartupScript.Update(ctx, "1234", script) if err != nil { t.Errorf("StartupScript.Update returned error: %+v", err) } } func TestStartupScriptServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/startup-scripts/1234", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.StartupScript.Delete(ctx, "1234") if err != nil { t.Errorf("StartupScript.Delete returned %+v, expected %+v", err, nil) } } func TestStartupScriptServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/startup-scripts", func(writer http.ResponseWriter, request *http.Request) { response := `{"startup_scripts": [{"id": "14350","date_created": "2020-06-08 17:58:10","date_modified": "2020-06-08 17:59:54","name": "govultr","type": "pxe","script": "dGVzdA=="}],"meta": {"total": 1,"links": {"next": "","prev": ""}}}` fmt.Fprintf(writer, response) }) scripts, meta, err := client.StartupScript.List(ctx, nil) if err != nil { t.Errorf("StartupScript.List returned error: %v", err) } expectedScript := []StartupScript{ { ID: "14350", DateCreated: "2020-06-08 17:58:10", DateModified: "2020-06-08 17:59:54", Name: "govultr", Type: "pxe", Script: "dGVzdA==", }, } expectedMeta := &Meta{ Total: 1, Links: &Links{}, } if !reflect.DeepEqual(scripts, expectedScript) { t.Errorf("StartupScript.List scripts returned %+v, expected %+v", scripts, expectedScript) } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("StartupScript.List meta returned %+v, expected %+v", meta, expectedMeta) } } golang-github-vultr-govultr-2.17.2/user.go000066400000000000000000000070421425211421400205100ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const path = "/v2/users" // UserService is the interface to interact with the user management endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/users type UserService interface { Create(ctx context.Context, userCreate *UserReq) (*User, error) Get(ctx context.Context, userID string) (*User, error) Update(ctx context.Context, userID string, userReq *UserReq) error Delete(ctx context.Context, userID string) error List(ctx context.Context, options *ListOptions) ([]User, *Meta, error) } var _ UserService = &UserServiceHandler{} // UserServiceHandler handles interaction with the user methods for the Vultr API type UserServiceHandler struct { client *Client } // User represents an user on Vultr type User struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` APIEnabled *bool `json:"api_enabled"` APIKey string `json:"api_key,omitempty"` ACL []string `json:"acls,omitempty"` } // UserReq is the user struct for create and update calls type UserReq struct { Email string `json:"email,omitempty"` Name string `json:"name,omitempty"` APIEnabled *bool `json:"api_enabled,omitempty"` ACL []string `json:"acls,omitempty"` Password string `json:"password,omitempty"` } type usersBase struct { Users []User `json:"users"` Meta *Meta `json:"meta"` } type userBase struct { User *User `json:"user"` } // Create will add the specified user to your Vultr account func (u *UserServiceHandler) Create(ctx context.Context, userCreate *UserReq) (*User, error) { req, err := u.client.NewRequest(ctx, http.MethodPost, path, userCreate) if err != nil { return nil, err } user := new(userBase) if err = u.client.DoWithContext(ctx, req, user); err != nil { return nil, err } return user.User, nil } // Get will retrieve a specific user account func (u *UserServiceHandler) Get(ctx context.Context, userID string) (*User, error) { uri := fmt.Sprintf("%s/%s", path, userID) req, err := u.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } user := new(userBase) if err = u.client.DoWithContext(ctx, req, user); err != nil { return nil, err } return user.User, nil } // Update will update the given user. Empty strings will be ignored. func (u *UserServiceHandler) Update(ctx context.Context, userID string, userReq *UserReq) error { uri := fmt.Sprintf("%s/%s", path, userID) req, err := u.client.NewRequest(ctx, http.MethodPatch, uri, userReq) if err != nil { return err } return u.client.DoWithContext(ctx, req, nil) } // Delete will remove the specified user from your Vultr account func (u *UserServiceHandler) Delete(ctx context.Context, userID string) error { uri := fmt.Sprintf("%s/%s", path, userID) req, err := u.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return u.client.DoWithContext(ctx, req, nil) } // List will list all the users associated with your Vultr account func (u *UserServiceHandler) List(ctx context.Context, options *ListOptions) ([]User, *Meta, error) { req, err := u.client.NewRequest(ctx, http.MethodGet, path, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() users := new(usersBase) if err = u.client.DoWithContext(ctx, req, &users); err != nil { return nil, nil, err } return users.Users, users.Meta, nil } golang-github-vultr-govultr-2.17.2/user_test.go000066400000000000000000000074661425211421400215610ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestUserServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/users", func(writer http.ResponseWriter, request *http.Request) { response := `{"user": {"id": "564a1a88947b4","name": "Example User","email": "example@vultr.com","api_key": "aaavvvvvvbbbbbb","api_enabled": true,"acls": []}}` fmt.Fprint(writer, response) }) api := true userReq := &UserReq{ Email: "example@vultr.com", Name: "Example User", APIEnabled: &api, Password: "password", } user, err := client.User.Create(ctx, userReq) if err != nil { t.Errorf("User.Create returned %+v, expected %+v", err, nil) } expected := &User{ ID: "564a1a88947b4", Name: "Example User", Email: "example@vultr.com", APIEnabled: BoolToBoolPtr(true), APIKey: "aaavvvvvvbbbbbb", ACL: []string{}, } if !reflect.DeepEqual(user, expected) { t.Errorf("User.Create returned %+v, expected %+v", user, expected) } } func TestUserServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/users/123abc", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.User.Delete(ctx, "123abc") if err != nil { t.Errorf("User.Delete returned %+v, expected %+v", err, nil) } } func TestUserServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/users", func(writer http.ResponseWriter, request *http.Request) { response := ` { "users": [ { "id": "f255efc9700d9", "name": "test api", "email": "newmanapi@vultr.com", "api_enabled": true, "acls": [] } ], "meta": { "total": 1, "links": { "next": "thisismycusror", "prev": "" } } } ` fmt.Fprintf(writer, response) }) options := &ListOptions{ PerPage: 1, } users, meta, err := client.User.List(ctx, options) if err != nil { t.Errorf("User.List returned error: %v", err) } expected := []User{ { ID: "f255efc9700d9", Name: "test api", Email: "newmanapi@vultr.com", APIEnabled: BoolToBoolPtr(true), ACL: []string{}, }, } if !reflect.DeepEqual(users, expected) { t.Errorf("User.List users returned %+v, expected %+v", users, expected) } expectedMeta := &Meta{ Total: 1, Links: &Links{ Next: "thisismycusror", Prev: "", }, } if !reflect.DeepEqual(meta, expectedMeta) { t.Errorf("User.List meta returned %+v, expected %+v", meta, expectedMeta) } } func TestUserServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/users/abc123", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) api := true user := &UserReq{ Name: "Example User", Password: "w1a4dcnst0n!", Email: "example@vultr.com", APIEnabled: &api, ACL: []string{"support"}, } err := client.User.Update(ctx, "abc123", user) if err != nil { t.Errorf("User.Update returned error: %+v", err) } } func TestUserServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/users/abc123", func(writer http.ResponseWriter, request *http.Request) { response := `{"user": {"id": "f255efc9c69ac","name": "Unit Test","email": "test@vultr.com","api_enabled": true,"acls": []}}` fmt.Fprint(writer, response) }) user, err := client.User.Get(ctx, "abc123") if err != nil { t.Errorf("User.Get returned error: %v", err) } expected := &User{ ID: "f255efc9c69ac", Name: "Unit Test", Email: "test@vultr.com", APIEnabled: BoolToBoolPtr(true), ACL: []string{}, } if !reflect.DeepEqual(user, expected) { t.Errorf("User.List users returned %+v, expected %+v", user, expected) } } golang-github-vultr-govultr-2.17.2/vpc.go000066400000000000000000000066341425211421400203300ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const vpcPath = "/v2/vpcs" // VPCService is the interface to interact with the VPC endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/vpcs type VPCService interface { Create(ctx context.Context, createReq *VPCReq) (*VPC, error) Get(ctx context.Context, vpcID string) (*VPC, error) Update(ctx context.Context, vpcID string, description string) error Delete(ctx context.Context, vpcID string) error List(ctx context.Context, options *ListOptions) ([]VPC, *Meta, error) } // VPCServiceHandler handles interaction with the VPC methods for the Vultr API type VPCServiceHandler struct { client *Client } // VPC represents a Vultr VPC type VPC struct { ID string `json:"id"` Region string `json:"region"` Description string `json:"description"` V4Subnet string `json:"v4_subnet"` V4SubnetMask int `json:"v4_subnet_mask"` DateCreated string `json:"date_created"` } // VPCReq represents parameters to create or update a VPC resource type VPCReq struct { Region string `json:"region"` Description string `json:"description"` V4Subnet string `json:"v4_subnet"` V4SubnetMask int `json:"v4_subnet_mask"` } type vpcsBase struct { VPCs []VPC `json:"vpcs"` Meta *Meta `json:"meta"` } type vpcBase struct { VPC *VPC `json:"vpc"` } // Create creates a new VPC. A VPC can only be used at the location for which it was created. func (n *VPCServiceHandler) Create(ctx context.Context, createReq *VPCReq) (*VPC, error) { req, err := n.client.NewRequest(ctx, http.MethodPost, vpcPath, createReq) if err != nil { return nil, err } vpc := new(vpcBase) if err = n.client.DoWithContext(ctx, req, vpc); err != nil { return nil, err } return vpc.VPC, nil } // Get gets the VPC of the requested ID func (n *VPCServiceHandler) Get(ctx context.Context, vpcID string) (*VPC, error) { uri := fmt.Sprintf("%s/%s", vpcPath, vpcID) req, err := n.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } vpc := new(vpcBase) if err = n.client.DoWithContext(ctx, req, vpc); err != nil { return nil, err } return vpc.VPC, nil } // Update updates a VPC func (n *VPCServiceHandler) Update(ctx context.Context, vpcID string, description string) error { uri := fmt.Sprintf("%s/%s", vpcPath, vpcID) vpcReq := RequestBody{"description": description} req, err := n.client.NewRequest(ctx, http.MethodPut, uri, vpcReq) if err != nil { return err } return n.client.DoWithContext(ctx, req, nil) } // Delete deletes a VPC. Before deleting, a VPC must be disabled from all instances func (n *VPCServiceHandler) Delete(ctx context.Context, vpcID string) error { uri := fmt.Sprintf("%s/%s", vpcPath, vpcID) req, err := n.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return n.client.DoWithContext(ctx, req, nil) } // List lists all VPCs on the current account func (n *VPCServiceHandler) List(ctx context.Context, options *ListOptions) ([]VPC, *Meta, error) { req, err := n.client.NewRequest(ctx, http.MethodGet, vpcPath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() vpcs := new(vpcsBase) if err = n.client.DoWithContext(ctx, req, vpcs); err != nil { return nil, nil, err } return vpcs.VPCs, vpcs.Meta, nil } golang-github-vultr-govultr-2.17.2/vpc_test.go000066400000000000000000000065561425211421400213720ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestVPCServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/vpcs", func(writer http.ResponseWriter, request *http.Request) { response := ` { "vpc": { "id": "net539626f0798d7", "date_created": "2017-08-25 12:23:45", "region": "ewr", "description": "test1", "v4_subnet": "10.99.0.0", "v4_subnet_mask": 24 } } ` fmt.Fprint(writer, response) }) options := &VPCReq{ Region: "ewr", Description: "test1", } net, err := client.VPC.Create(ctx, options) if err != nil { t.Errorf("VPC.Create returned %+v, expected %+v", err, nil) } expected := &VPC{ ID: "net539626f0798d7", Region: "ewr", Description: "test1", V4Subnet: "10.99.0.0", V4SubnetMask: 24, DateCreated: "2017-08-25 12:23:45", } if !reflect.DeepEqual(net, expected) { t.Errorf("VPC.Create returned %+v, expected %+v", net, expected) } } func TestVPCServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/vpcs/net539626f0798d7", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.VPC.Delete(ctx, "net539626f0798d7") if err != nil { t.Errorf("VPC.Delete returned %+v, expected %+v", err, nil) } } func TestVPCServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/vpcs", func(writer http.ResponseWriter, request *http.Request) { response := ` { "vpcs": [{ "id": "net539626f0798d7", "date_created": "2017-08-25 12:23:45", "region": "ewr", "description": "test1", "v4_subnet": "10.99.0.0", "v4_subnet_mask": 24 }] } ` fmt.Fprint(writer, response) }) vpcs, _, err := client.VPC.List(ctx, nil) if err != nil { t.Errorf("VPC.List returned error: %v", err) } expected := []VPC{ { ID: "net539626f0798d7", Region: "ewr", Description: "test1", V4Subnet: "10.99.0.0", V4SubnetMask: 24, DateCreated: "2017-08-25 12:23:45", }, } if !reflect.DeepEqual(vpcs, expected) { t.Errorf("VPC.List returned %+v, expected %+v", vpcs, expected) } } func TestVPCServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/vpcs/net539626f0798d7", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.VPC.Update(ctx, "net539626f0798d7", "update") if err != nil { t.Errorf("VPC.Update returned %+v, expected %+v", err, nil) } } func TestVPCServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/vpcs/net539626f0798d7", func(writer http.ResponseWriter, request *http.Request) { req := `{"vpc": {"id": "cb676a46-66fd-4dfb-b839-443f2e6c0b60","date_created": "2020-10-10T01:56:20+00:00","region": "ewr","description": "sample desc","v4_subnet": "10.99.0.0","v4_subnet_mask": 24}}` fmt.Fprint(writer, req) }) vpc, err := client.VPC.Get(ctx, "net539626f0798d7") if err != nil { t.Errorf("VPC.Get returned %+v, expected %+v", err, nil) } expected := &VPC{ ID: "cb676a46-66fd-4dfb-b839-443f2e6c0b60", Region: "ewr", Description: "sample desc", V4Subnet: "10.99.0.0", V4SubnetMask: 24, DateCreated: "2020-10-10T01:56:20+00:00", } if !reflect.DeepEqual(vpc, expected) { t.Errorf("VPC.Get returned %+v, expected %+v", vpc, expected) } }