From a2ab9dbe64f39e7c983e41805038a58ee8e10017 Mon Sep 17 00:00:00 2001 From: Michael Riley Date: Wed, 14 Feb 2024 23:13:47 -0500 Subject: [PATCH] Refactor commands into packages and finish up the base model for all commands (#398) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐧 Add Fedora installation instructions (#246) The vultr-cli package is now available on Fedora 35+: * https://src.fedoraproject.org/rpms/vultr-cli * Add block type support / Add Aliases and Examples (#249) * Update govultr dependency / add block type support * Add examples and aliases for each command * Fix lint issues * Replace example placeholders with real data * Fix detach typo * Add VPC support and deprecate networks (#251) * Add VPC support and deprecate networks This commit adds the VPC support offered by govultr 2.15.x and deprecates the private network commands. Note, this means that usage of the `vultr-cli network` commands will output a deprecation notice which may throw off output parsers. * Update VPC IDs flag and some error checking * Bump github.com/spf13/viper from 1.10.1 to 1.11.0 (#252) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.10.1 to 1.11.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.10.1...v1.11.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add VPC flag check to Load Balancers (#254) * Add VPC support and deprecate networks This commit adds the VPC support offered by govultr 2.15.x and deprecates the private network commands. Note, this means that usage of the `vultr-cli network` commands will output a deprecation notice which may throw off output parsers. * Update VPC IDs flag and some error checking * Add VPC error checking to load balancer as well * adding block type to printer response (#253) * Release v2.13.0 #minor * updated veribage for snapshots + readme (#257) * vultr-cli is now available on all flavors since 7.1 release. (#258) * Bump github.com/vultr/govultr/v2 from 2.15.1 to 2.16.0 (#260) Bumps [github.com/vultr/govultr/v2](https://github.com/vultr/govultr) from 2.15.1 to 2.16.0. - [Release notes](https://github.com/vultr/govultr/releases) - [Changelog](https://github.com/vultr/govultr/blob/master/CHANGELOG.md) - [Commits](https://github.com/vultr/govultr/compare/v2.15.1...v2.16.0) --- updated-dependencies: - dependency-name: github.com/vultr/govultr/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add tags support to bare metal and instance commands (#259) * Add tags to bare metal and instance commands * Remove unnecessary godoc * Move tags output in printers * Add firewall rule ip-type support (#262) * Add fw ip-type flags and docs; use govultr IPType * Move ip type alias to new flag * Add support for VKE auto scaler options (#261) * Add support for VKE version upgrades (#263) * WIP: add upgrade commands and printer to kubernetes * Move upgrade commands to sub-command and add docs * Inline error handling * Change upgrade command to be less redundant * Release v2.14.0 #minor * Bump github.com/spf13/viper from 1.11.0 to 1.12.0 (#266) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.11.0 to 1.12.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.11.0...v1.12.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump govultr version from 2.16.0 to 2.17.1 (#267) Bumps [github.com/vultr/govultr/v2](https://github.com/vultr/govultr) from 2.16.0 to 2.17.1. - [Release notes](https://github.com/vultr/govultr/releases) - [Changelog](https://github.com/vultr/govultr/blob/master/CHANGELOG.md) - [Commits](vultr/govultr@v2.16.0...v2.17.1) --- updated-dependencies: - dependency-name: github.com/vultr/govultr/v2 dependency-type: direct:production update-type: version-update:semver-minor ... * Update instance and kubernetes tag to str pointer (#268) * Update instance and kubernetes tag to str pointer * Fix vendoring state * Add GPU fields to plans printer (#269) * Release v2.14.1 #patch * Update govultr and add support for reserved IP label updates (#272) * Add update command to reserved IP * Fix label flag for IP update * Add docs to reserved IP commands * Bump govultr to v2.17.2 * Release v2.14.2 #patch * Bump github.com/spf13/cobra from 1.4.0 to 1.5.0 (#274) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.4.0...v1.5.0) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Remove extraneous dash from example command line (#279) * bumping go from 1.17 to 1.19 (#284) * add ARM v6 support * bumping go from 1.17 to 1.19 * Replaced golint with golangci-lint * Replaced golint with golangci-lint * Fix linting issues and unhandle error returns * Fix linter issues * Rename github workflow * Make load balancer display show VPN * Gofmt cmd/dnsRecord Co-authored-by: Michael Riley * feature add arm builds (#283) * add ARM v6 support * Add ARM builds Co-authored-by: Michael Riley * Release v2.15.0 #minor * Update goreleaser to add latest docker image tag (#287) * Bump github.com/spf13/cobra from 1.5.0 to 1.6.0 (#288) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.5.0 to 1.6.0. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.5.0...v1.6.0) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/spf13/cobra from 1.6.0 to 1.6.1 (#289) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.6.0...v1.6.1) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/spf13/viper from 1.13.0 to 1.14.0 (#290) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Make cli param examples consistently use = (#291) * Bump golang.org/x/oauth2 from 0.0.0-20221014153046-6fdb5e3db783 to 0.5.0 (#296) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.0.0-20221014153046-6fdb5e3db783 to 0.5.0. - [Release notes](https://github.com/golang/oauth2/releases) - [Commits](https://github.com/golang/oauth2/commits/v0.5.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#293) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump golang.org/x/net from 0.6.0 to 0.7.0 (#297) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add vcg options to docstrings on regions and plans (#299) * Bump golang.org/x/oauth2 from 0.5.0 to 0.6.0 (#298) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/golang/oauth2/releases) - [Commits](https://github.com/golang/oauth2/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Release v2.15.1 #patch * Update govultr to v3.0.1 (#301) * Update Go to v1.20 * Remove vendored dependencies * Ignore vendor directory * Bump go version in github releaser workflow * Update builds to Go 1.20 and remove vendor * Update to govultr v3.0.1 * Add DBaaS Support to Vultr CLI (#302) * Initial WIP commit * More WIP stuff * Implement primary DBaaS endpoints * Clean up old .gitignore stuff and README update * Add in database user management endpoints * Quick README update * Implement logical db management * Implement maintenance updates and service alerts * Implement migrations, read-only replicas, backup info, and fork/restore * Implement PostgreSQL connection pools management * Add in support for PG advanced options and version upgrades * Remove temporary local env support * Update go to 1.20 (#303) * Update govultr to v3.0.2 (#304) * Release v2.16.0 #minor * Goreleaser configuration updates (#306) * Update goreleaser configs * Replace deprecated rm-dist flag * Release v2.16.1 #patch * Quote go version in github workflow config (#308) * Quote go-version in github workflow * Release v2.16.2 #patch * Update workflows to go v1.20 (#311) * Update workflows to go v1.20 * Update golanci-lint to v1.52.2 * Bump github.com/spf13/cobra from 1.6.1 to 1.7.0 (#310) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.6.1 to 1.7.0. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.6.1...v1.7.0) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump golang.org/x/oauth2 from 0.6.0 to 0.7.0 (#312) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/golang/oauth2/releases) - [Commits](https://github.com/golang/oauth2/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix database update errors and remove db engine/version (#314) * Fix database update errors and remove db engine/version * Removed leftover debug variable print * Deleted extra space * Bump golang.org/x/oauth2 from 0.7.0 to 0.8.0 (#316) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.7.0 to 0.8.0. - [Commits](https://github.com/golang/oauth2/compare/v0.7.0...v0.8.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add support for attachdetaching VPC networks from existing VPS instances (#318) * Initial work on VPC attach/detach for instances * Add flags for VPC ID on attach/detach * Add additional help for instance VPC commands * Use succinct Homebrew command to tap-and-install (#315) * Add docker install/usage instructions (#322) * Add docker install/usage instructions * Remove extraneous whitespace * Fix README spelling (#324) * Bump golang.org/x/oauth2 from 0.8.0 to 0.9.0 (#323) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.8.0 to 0.9.0. - [Commits](https://github.com/golang/oauth2/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/spf13/viper from 1.15.0 to 1.16.0 (#319) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.15.0 to 1.16.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.15.0...v1.16.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/vultr/govultr/v3 from 3.0.2 to 3.0.3 (#320) Bumps [github.com/vultr/govultr/v3](https://github.com/vultr/govultr) from 3.0.2 to 3.0.3. - [Release notes](https://github.com/vultr/govultr/releases) - [Changelog](https://github.com/vultr/govultr/blob/master/CHANGELOG.md) - [Commits](https://github.com/vultr/govultr/compare/v3.0.2...v3.0.3) --- updated-dependencies: - dependency-name: github.com/vultr/govultr/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add another mention of default config yaml location in README (#325) * Release v2.17.0 #minor * Update govultr to v3.1.0 (#329) * Bump github.com/vultr/govultr/v3 from 3.1.0 to 3.2.0 (#330) Bumps [github.com/vultr/govultr/v3](https://github.com/vultr/govultr) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/vultr/govultr/releases) - [Changelog](https://github.com/vultr/govultr/blob/master/CHANGELOG.md) - [Commits](https://github.com/vultr/govultr/compare/v3.1.0...v3.2.0) --- updated-dependencies: - dependency-name: github.com/vultr/govultr/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump golang.org/x/oauth2 from 0.9.0 to 0.10.0 (#328) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.9.0 to 0.10.0. - [Commits](https://github.com/golang/oauth2/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add VPC support for DBaaS instances (#331) * Bump golang.org/x/oauth2 from 0.10.0 to 0.11.0 (#333) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.10.0 to 0.11.0. - [Commits](https://github.com/golang/oauth2/compare/v0.10.0...v0.11.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update govultr to v3.3.0 (#334) * Add support for VPC 2.0 (#335) * Add main vpc2 commands and printers * Add support for instance VPC2 endpoints * Add support for bare metal VPC2 endpoints * Add README reference for VPC2 * Update example description for VPC2 * Add more aliases for the apps command (#336) * cmd/instance: Fix reserved IPv4 flag docs (#337) This flag doesn't accept the IP address directly, it has to be the uuid of the IP address. * Update govultr to v3.3.1 (#338) * VPC2 Nodes Endpoints (#339) * Add VPC2 nodes endpoints * Finalize vpc2 node endpoints * Fix linting messages for vpc2 nodes endpoints * Correct error with VPC2 examples * Managed Database Nesting Refactor (#340) * Refactor DBaaS components into nested commands * Remove local testing change * Correct some line lengths for flags * Fix some duplicate descriptions * Release v2.18.0 #minor * Remove replacement and use name template in goreleaser archives (#342) * Remove replacement and use name template in goreleaser * Use name_template conditions for replacements * Release v2.18.1 #patch * Fix goreleaser archive names and remove deprecated brew command (#344) * Use repository instead of tap in brews goreleaser * Update goreleaser archive formatting * Release v2.18.2 #patch * Add project name back to the archive file names (#346) There was a discrepancy between running it locally and when running it on github. * Update to go v1.21 (#347) * Add summarize list options for VKE, VLB & database (#348) * Add summarize flag to k8s list command * Add summarize output for load balancers * Add status column in kubernetes summarize * Add instance count to load-balancer summary * Fix summarize flag description * Add summarize examples in command help * Add database list summarized option * Remove extra whitespace * Remove extraneous example for database help * Remove unnecessary Go dependency from .goreleaser (#97) Co-authored-by: Michael Riley * Remove the useless cobra init help toggle flag (#349) * Bump golang.org/x/oauth2 from 0.11.0 to 0.12.0 (#351) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.11.0 to 0.12.0. - [Commits](https://github.com/golang/oauth2/compare/v0.11.0...v0.12.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix documentation for VPC2 command options (#350) * vpc2: doc: fix typo * vpc2: doc: fix required ip type in example * Add golangci-lint config and fix linter errors (#353) * Add golangci-lint and fixes * Use consistent line breaks on AddCommand * Correct some load balancer command docs * Update workflows to use go v1.21 * Add exclude rules to printer functions The errors will be addressed in another, more complicated changeset * Remove unecessary whitespace * Remove obsolete nolint directive * Rework the printer output code (#355) * Rework printer logic for effeciency and consistency * Amend some golangci-lint rules * Resolve more linting/formatting errors on printer * Release v2.19.0 #minor * Update govultr to v3.3.2 (#362) * Add support for public/private host, cleanup summarize view (#363) * Bump golang.org/x/net from 0.15.0 to 0.17.0 (#358) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0. - [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update govultr to v3.3.3 (#365) * Adjust DBaaS VPC pointer to detect changes (#366) * Allow some commands to be run without authenticating against the API (#364) * Add root cmd logic to allow non-auth execution * Add pre-execute logic to auth-required commands * Switch to errors.New on pre-run checks * Use struct{} for context key * Fix bool comparison and whitespace linting * Add comment to ctxAuthKey type * Update govultr to v3.3.4 (#367) * Add support for the VKE HA control plane option (#368) * Add support for the VKE HA control plane option * Use more verbose printer output for HA * Fix long line * Add support for DBaaS FerretDB subscriptions (#369) * Bump golang.org/x/oauth2 from 0.12.0 to 0.13.0 (#356) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.12.0 to 0.13.0. - [Commits](https://github.com/golang/oauth2/compare/v0.12.0...v0.13.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/spf13/viper from 1.16.0 to 1.17.0 (#357) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.16.0...v1.17.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Release v2.20.0 #minor * Update the bare-metal and instance tags display to use delimiters (#372) * Add array stringify function for printer functions * Use the array stringifier for BM and Instance tags * Ignore staticcheck errors on bare-metal Tag * Bump github.com/spf13/cobra from 1.7.0 to 1.8.0 (#371) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump govultr to v3.4.0 (#374) * Support Managed Database Read Replica Promotion (#375) * Add support for promoting Managed Database read replicas * Remove unused nolints * Remove one more unused nolint * Bump golang.org/x/oauth2 from 0.13.0 to 0.14.0 (#373) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.13.0 to 0.14.0. - [Commits](https://github.com/golang/oauth2/compare/v0.13.0...v0.14.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add flag to export kubeconfig (#361) * add kubeconfig filepath flag * update file permissions * lint fix * remove default path * remove newline and arg check * decode base64 kubeconfig * update helptext * Update govultr to v3.4.1 (#376) * Add usage command for database (#378) * Bump golang.org/x/oauth2 from 0.14.0 to 0.15.0 (#379) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.14.0 to 0.15.0. - [Commits](https://github.com/golang/oauth2/compare/v0.14.0...v0.15.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add container registry commands (#380) * WIP: add initial container registry implementation * Add initial printer for container-registry * Fix the govultr hooks for container registry * Add public to container registry list * Change aliase for regions * Fix container-registry update flags * Update helptexts for container registry * Add container-registry to root command * Lint fixes * Fix spelling errors * Pass plans struct as pointer * No lint the byte number * Remove extraneous whitespace * Update readme with conatiner registry * Release v2.21.0 #minor * Bump github.com/vultr/govultr/v3 from 3.4.1 to 3.5.0 (#382) Bumps [github.com/vultr/govultr/v3](https://github.com/vultr/govultr) from 3.4.1 to 3.5.0. - [Release notes](https://github.com/vultr/govultr/releases) - [Changelog](https://github.com/vultr/govultr/blob/master/CHANGELOG.md) - [Commits](https://github.com/vultr/govultr/compare/v3.4.1...v3.5.0) --- updated-dependencies: - dependency-name: github.com/vultr/govultr/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Database: Support user access control for Redis databases (#383) * Support user access control for Redis Managed Databases * Make at least one Redis access control flag required * Bring govultr versions inline * Bump vultr-cli module to v3 * Switch go-yaml to v3 * Manual merge printer commands from rewrite * Rename printer yaml function * Manually merge in other misc legacy printer funcs * Add printers to separated package commands Still a work in progress on migrating all commands to the v3 way of handling the command data. This fixes all the go errors resulting from the somewhat scuffed merge from master. But it compiles! * Move API auth to base and use NewCLIBase * Add persistent auth check to accounts * Fix exit codes after displayNonText * Move marshalling to printer public function At least for now * Add a context to the base struct * Fix ssh key package name to conform to lang standard * Make backups command into package * Use index vs k/v on range in backups printer * Use consistent case on generic message * Add public arrayofstrings func to printer * Add info func to printer * Add error and options utility functions * Move all printer from map to slice Maps are not ordered and cannot be relied upon for ordered output when accesed with an index in a loop. * Add the bare metal command * Update root command * Add auth check to ssh keys * Add paging to plans * Flesh out the rest of bare metal * Rename operatingsystems * Add user data printer * Tidy up baremetal * Move float precistion to constant * Move billing to its own package * Remove old backup printer * Remove license/copyright and add packages godoc * Make meta json name lowercase * Move block storage to package * Rename billing options struct * Add package godoc for regions * Fix users printer godoc * Add default consts to utils This might not be the best place for them, but it'll do for now * Remove unused block storage ID option * Move container registry to its own package * WIP: Move DNS to its own package * Finish moving dns to its own package * Remove old DNS code * Fix DNS cmd comment * Remove extraneous whitespace * Move firewall to its own package * Add SilenceUsage to root command * Reformat root command AddCommand * Move ISO to own package * Make ISO package godoc better * Remove deprecated 'network' command Replaced by the VPC commands * Clean up the legacy printer files * Adjust error output whitespace on ISO * Move object-storage to own package * Move object-storage list flags to correct spot * Move firewall options struct * Move 'reserved-ip' to own package * Move `startup-script` to separate package * Fix godoc for script printer * Add firewall utils * Update utils const * Fix CR docker credentials read/write flag * Move 'snapshot' to its own package * Move `vpc` to its own package * Fix godoc * Fix godoc again * Move `vpc2` to its own package * Move `kubernetes` to its own package * Add printer for ips * Make bare metal use ip printer * Move `instance` to its own package * Remove extraneous license details * Add 'none' output when empty user data * Re-do `users` w/ new structure * Update `vpc2` list flag formatting * Use consistent format for `account` * Tidy up packages to conform with current structure * Fix the version command * Tidy up `container-registry` * Add the license NOTICE file * Upgrade govultr from v3.5.0 to v3.6.1 * Fix whitespace * Initialize data var in `applications` printer * Fix nil reference in `applications` * Add `marketplace` * Update some `marketplace` docs * Move `load-balancer` to own package * Fix load balancer printer variable * Tidy up printer functions & types * Add printer Array of int stringifier * Add printer total display * Fix k8s help text * Move `database` to own package * Make errors print just the col for now This is a thing that needs to be fleshed out to work ideally * Combine `kubernetes` delete examples * Fix `kubernetes` whitespace and linting * Ignore gocritic errors for now * Remove tests for now There's not much point in keeping these while they're not working * Fix `database` whitespace and linting * Add missing `database` maintenance commands and lint fixes * Fix lint errors on `account` * Move aliases in `applications` * Fix unused vars in `backups` * Switch `backups` to private struct * Resolve lint issues on `bare-metal` * Remove extraneous whitespace * Make `block-storage` conform with private struct * Fix linting on `container-registry` * Ignore gocyclo on `container-registry` * Move `dns` to private struct * Fix linting on `dns` * Linting fixes for `firewall` * Linting fixes on `instance` * Linting fixes on ip package * Linting fixes on `iso` * Linting fixes on `load-balancer` * Linting fixes on `martketplace` * Silence gocylco on `object-storage` * Linting fixes on `os` * Linting fixes on `plans` * Linting fixes on the printer package * Linting fixes on `reserved-ip` * Fix linting on `scripts` * Fix linting errors on `snapshot` * Silence gocyclo on `ssh-key` * Silence gocyclo on `user` * Add godoc to NewUserCmd * Fix godoc in the utils package * Fix linting on `vpc` * Fix linting on `vpc2` * Remove unused stuff from root * Remove unused printer functions * Update `plans` to match other commands * Set persisten pre run on plans * Shore up `os` * Add persistent pre execute on non-auth routes Otherwise JSON and other --output will not work --------- Signed-off-by: dependabot[bot] Co-authored-by: Major Hayden Co-authored-by: Glenn Dobson <102540704+AFatalErrror@users.noreply.github.com> Co-authored-by: Michael Riley Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: David Dymko Co-authored-by: Adriano Barbosa Co-authored-by: Nicholas Chambers Co-authored-by: cdragon <35617339+mondragonfx@users.noreply.github.com> Co-authored-by: happytreees <110687499+happytreees@users.noreply.github.com> Co-authored-by: Chris Morse <120681681+christhemorse@users.noreply.github.com> Co-authored-by: ELLIOTTCABLE Co-authored-by: Anmol Sethi Co-authored-by: 0az <30874884+0az@users.noreply.github.com> Co-authored-by: René Moser --- .github/dependabot.yml | 7 + .github/workflows/coverage.yml | 52 + .github/workflows/gochecks.yml | 21 +- .github/workflows/notify-issue.yml | 18 + .github/workflows/notify-release.yml | 21 - .github/workflows/releaser.yml | 95 + .gitignore | 3 +- .golangci.yaml | 125 + .goreleaser.yml | 42 +- .travis.yml | 22 - CHANGELOG.md | 384 +- Dockerfile | 4 +- Makefile | 7 +- .../mousetrap/LICENSE => NOTICE | 5 +- README.md | 185 +- cmd/account/account.go | 67 +- cmd/account/account_test.go | 75 - cmd/account/printer.go | 52 +- cmd/applications/applications.go | 88 +- cmd/applications/applications_test.go | 91 - cmd/applications/printer.go | 66 + cmd/backups.go | 77 - cmd/backups/backups.go | 110 + cmd/backups/printer.go | 99 + cmd/bareMetal.go | 406 - cmd/bareMetalApp.go | 89 - cmd/bareMetalOS.go | 88 - cmd/bareMetalUserData.go | 95 - cmd/baremetal/baremetal.go | 1232 +++ cmd/baremetal/printer.go | 262 + cmd/billing/billing.go | 275 + cmd/billing/printer.go | 221 + cmd/blockStorage.go | 268 - cmd/blockstorage/blockstorage.go | 473 + cmd/blockstorage/printer.go | 125 + cmd/containerregistry/containerregistry.go | 696 ++ cmd/containerregistry/printer.go | 376 + cmd/database/database.go | 2612 ++++++ cmd/database/printer.go | 1318 +++ cmd/dns.go | 32 - cmd/dns/dns.go | 645 ++ cmd/dns/printer.go | 279 + cmd/dnsDomain.go | 229 - cmd/dnsRecord.go | 223 - cmd/firewall.go | 33 - cmd/firewall/firewall.go | 538 ++ cmd/firewall/printer.go | 223 + cmd/firewallGroup.go | 147 - cmd/firewallRule.go | 162 - cmd/instance.go | 1153 --- cmd/instance/instance.go | 1777 ++++ cmd/instance/printer.go | 513 ++ cmd/ip/printer.go | 103 + cmd/iso.go | 144 - cmd/iso/iso.go | 190 + cmd/iso/printer.go | 158 + cmd/kubernetes/kubernetes.go | 1143 +++ cmd/kubernetes/printer.go | 504 ++ cmd/loadBalancer.go | 538 -- cmd/loadbalancer/loadbalancer.go | 1086 +++ cmd/loadbalancer/printer.go | 491 ++ cmd/marketplace/marketplace.go | 99 + cmd/marketplace/printer.go | 55 + cmd/network.go | 137 - cmd/objectStorage.go | 198 - cmd/objectstorage/objectstorage.go | 272 + cmd/objectstorage/printer.go | 212 + cmd/operatingSystems/operatingSystems.go | 109 - cmd/operatingSystems/operatingSystems_test.go | 87 - cmd/operatingsystems/operatingsystems.go | 90 + cmd/operatingsystems/printer.go | 60 + cmd/plans/plans.go | 157 +- cmd/plans/plans_test.go | 165 - cmd/plans/printer.go | 161 + cmd/printer/application.go | 52 - cmd/printer/backup.go | 23 - cmd/printer/bareMetal.go | 60 - cmd/printer/blockStorage.go | 27 - cmd/printer/dnsDomain.go | 38 - cmd/printer/dnsRecord.go | 22 - cmd/printer/error.go | 13 +- cmd/printer/firewallGroup.go | 24 - cmd/printer/firewallRule.go | 24 - cmd/printer/generic.go | 22 +- cmd/printer/info.go | 6 + cmd/printer/instance.go | 128 - cmd/printer/iso.go | 32 - cmd/printer/loadBalancer.go | 88 - cmd/printer/network.go | 21 - cmd/printer/objectStorage.go | 39 - cmd/printer/os.go | 50 - cmd/printer/plans.go | 96 - cmd/printer/printer.go | 206 +- cmd/printer/regions.go | 88 - cmd/printer/reservedIP.go | 22 - cmd/printer/script.go | 22 - cmd/printer/snapshot.go | 23 - cmd/printer/user.go | 23 - cmd/printer/userData.go | 20 - cmd/printer/version.go | 44 - cmd/regions/printer.go | 111 + cmd/regions/regions.go | 127 +- cmd/regions/regions_test.go | 102 - cmd/reservedIP.go | 210 - cmd/reservedip/printer.go | 114 + cmd/reservedip/reservedip.go | 449 + cmd/root.go | 200 +- cmd/script.go | 183 - cmd/script/printer.go | 97 + cmd/script/script.go | 255 + cmd/snapshot.go | 157 - cmd/snapshot/printer.go | 116 + cmd/snapshot/snapshot.go | 208 + cmd/sshkeys/printer.go | 90 +- cmd/sshkeys/sshKeys.go | 278 +- cmd/sshkeys/sshKeys_test.go | 196 - cmd/userdata/printer.go | 53 + cmd/users/printer.go | 99 + cmd/users/user.go | 414 +- cmd/users/users_test.go | 67 - cmd/utils/errors.go | 11 + cmd/utils/paging.go | 2 +- cmd/utils/utils.go | 38 + cmd/version/printer.go | 35 + cmd/version/version.go | 58 +- cmd/version/version_test.go | 45 - cmd/vpc/printer.go | 108 + cmd/vpc/vpc.go | 290 + cmd/vpc2/printer.go | 164 + cmd/vpc2/vpc2.go | 426 + go.mod | 50 +- go.sum | 360 +- main.go | 17 +- pkg/cli/cli.go | 41 +- scripts/entrypoint.sh | 6 + .../fsnotify/fsnotify/.editorconfig | 5 - .../github.com/fsnotify/fsnotify/.gitignore | 6 - .../github.com/fsnotify/fsnotify/.travis.yml | 30 - vendor/github.com/fsnotify/fsnotify/AUTHORS | 52 - .../github.com/fsnotify/fsnotify/CHANGELOG.md | 317 - .../fsnotify/fsnotify/CONTRIBUTING.md | 77 - vendor/github.com/fsnotify/fsnotify/LICENSE | 28 - vendor/github.com/fsnotify/fsnotify/README.md | 79 - vendor/github.com/fsnotify/fsnotify/fen.go | 37 - .../github.com/fsnotify/fsnotify/fsnotify.go | 66 - .../github.com/fsnotify/fsnotify/inotify.go | 337 - .../fsnotify/fsnotify/inotify_poller.go | 187 - vendor/github.com/fsnotify/fsnotify/kqueue.go | 521 -- .../fsnotify/fsnotify/open_mode_bsd.go | 11 - .../fsnotify/fsnotify/open_mode_darwin.go | 12 - .../github.com/fsnotify/fsnotify/windows.go | 561 -- vendor/github.com/go-yaml/yaml/.travis.yml | 12 - vendor/github.com/go-yaml/yaml/LICENSE | 201 - .../github.com/go-yaml/yaml/LICENSE.libyaml | 31 - vendor/github.com/go-yaml/yaml/NOTICE | 13 - vendor/github.com/go-yaml/yaml/README.md | 135 - vendor/github.com/go-yaml/yaml/apic.go | 739 -- vendor/github.com/go-yaml/yaml/decode.go | 764 -- vendor/github.com/go-yaml/yaml/emitterc.go | 1685 ---- vendor/github.com/go-yaml/yaml/encode.go | 358 - vendor/github.com/go-yaml/yaml/parserc.go | 1095 --- vendor/github.com/go-yaml/yaml/readerc.go | 394 - vendor/github.com/go-yaml/yaml/resolve.go | 245 - vendor/github.com/go-yaml/yaml/scannerc.go | 2702 ------ vendor/github.com/go-yaml/yaml/sorter.go | 104 - vendor/github.com/go-yaml/yaml/writerc.go | 26 - vendor/github.com/go-yaml/yaml/yaml.go | 466 - vendor/github.com/go-yaml/yaml/yamlh.go | 738 -- .../github.com/go-yaml/yaml/yamlprivateh.go | 173 - vendor/github.com/golang/protobuf/AUTHORS | 3 - .../github.com/golang/protobuf/CONTRIBUTORS | 3 - vendor/github.com/golang/protobuf/LICENSE | 28 - .../golang/protobuf/proto/buffer.go | 324 - .../golang/protobuf/proto/defaults.go | 63 - .../golang/protobuf/proto/deprecated.go | 113 - .../golang/protobuf/proto/discard.go | 58 - .../golang/protobuf/proto/extensions.go | 356 - .../golang/protobuf/proto/properties.go | 306 - .../github.com/golang/protobuf/proto/proto.go | 167 - .../golang/protobuf/proto/registry.go | 323 - .../golang/protobuf/proto/text_decode.go | 801 -- .../golang/protobuf/proto/text_encode.go | 560 -- .../github.com/golang/protobuf/proto/wire.go | 78 - .../golang/protobuf/proto/wrappers.go | 34 - .../github.com/google/go-querystring/LICENSE | 27 - .../google/go-querystring/query/encode.go | 320 - .../github.com/hashicorp/go-cleanhttp/LICENSE | 363 - .../hashicorp/go-cleanhttp/README.md | 30 - .../hashicorp/go-cleanhttp/cleanhttp.go | 57 - .../github.com/hashicorp/go-cleanhttp/doc.go | 20 - .../github.com/hashicorp/go-cleanhttp/go.mod | 1 - .../hashicorp/go-cleanhttp/handlers.go | 48 - .../hashicorp/go-retryablehttp/.gitignore | 4 - .../hashicorp/go-retryablehttp/LICENSE | 363 - .../hashicorp/go-retryablehttp/Makefile | 11 - .../hashicorp/go-retryablehttp/README.md | 62 - .../hashicorp/go-retryablehttp/client.go | 774 -- .../hashicorp/go-retryablehttp/go.mod | 8 - .../hashicorp/go-retryablehttp/go.sum | 10 - .../go-retryablehttp/roundtripper.go | 52 - vendor/github.com/hashicorp/hcl/.gitignore | 9 - vendor/github.com/hashicorp/hcl/.travis.yml | 13 - vendor/github.com/hashicorp/hcl/LICENSE | 354 - vendor/github.com/hashicorp/hcl/Makefile | 18 - vendor/github.com/hashicorp/hcl/README.md | 125 - vendor/github.com/hashicorp/hcl/appveyor.yml | 19 - vendor/github.com/hashicorp/hcl/decoder.go | 729 -- vendor/github.com/hashicorp/hcl/go.mod | 3 - vendor/github.com/hashicorp/hcl/go.sum | 2 - vendor/github.com/hashicorp/hcl/hcl.go | 11 - .../github.com/hashicorp/hcl/hcl/ast/ast.go | 219 - .../github.com/hashicorp/hcl/hcl/ast/walk.go | 52 - .../hashicorp/hcl/hcl/parser/error.go | 17 - .../hashicorp/hcl/hcl/parser/parser.go | 532 -- .../hashicorp/hcl/hcl/printer/nodes.go | 789 -- .../hashicorp/hcl/hcl/printer/printer.go | 66 - .../hashicorp/hcl/hcl/scanner/scanner.go | 652 -- .../hashicorp/hcl/hcl/strconv/quote.go | 241 - .../hashicorp/hcl/hcl/token/position.go | 46 - .../hashicorp/hcl/hcl/token/token.go | 219 - .../hashicorp/hcl/json/parser/flatten.go | 117 - .../hashicorp/hcl/json/parser/parser.go | 313 - .../hashicorp/hcl/json/scanner/scanner.go | 451 - .../hashicorp/hcl/json/token/position.go | 46 - .../hashicorp/hcl/json/token/token.go | 118 - vendor/github.com/hashicorp/hcl/lex.go | 38 - vendor/github.com/hashicorp/hcl/parse.go | 39 - .../inconshreveable/mousetrap/README.md | 23 - .../inconshreveable/mousetrap/trap_others.go | 15 - .../inconshreveable/mousetrap/trap_windows.go | 98 - .../mousetrap/trap_windows_1.4.go | 46 - .../magiconair/properties/.gitignore | 6 - .../magiconair/properties/.travis.yml | 12 - .../magiconair/properties/CHANGELOG.md | 139 - .../github.com/magiconair/properties/LICENSE | 25 - .../magiconair/properties/README.md | 129 - .../magiconair/properties/decode.go | 289 - .../github.com/magiconair/properties/doc.go | 156 - .../github.com/magiconair/properties/go.mod | 1 - .../magiconair/properties/integrate.go | 34 - .../github.com/magiconair/properties/lex.go | 407 - .../github.com/magiconair/properties/load.go | 292 - .../magiconair/properties/parser.go | 95 - .../magiconair/properties/properties.go | 833 -- .../magiconair/properties/rangecheck.go | 31 - .../mitchellh/mapstructure/.travis.yml | 8 - .../mitchellh/mapstructure/CHANGELOG.md | 21 - .../github.com/mitchellh/mapstructure/LICENSE | 21 - .../mitchellh/mapstructure/README.md | 46 - .../mitchellh/mapstructure/decode_hooks.go | 217 - .../mitchellh/mapstructure/error.go | 50 - .../github.com/mitchellh/mapstructure/go.mod | 1 - .../mitchellh/mapstructure/mapstructure.go | 1149 --- .../pelletier/go-toml/.dockerignore | 2 - .../github.com/pelletier/go-toml/.gitignore | 5 - .../pelletier/go-toml/CONTRIBUTING.md | 132 - .../github.com/pelletier/go-toml/Dockerfile | 11 - vendor/github.com/pelletier/go-toml/LICENSE | 21 - vendor/github.com/pelletier/go-toml/Makefile | 29 - .../go-toml/PULL_REQUEST_TEMPLATE.md | 5 - vendor/github.com/pelletier/go-toml/README.md | 151 - .../pelletier/go-toml/azure-pipelines.yml | 230 - .../github.com/pelletier/go-toml/benchmark.sh | 35 - vendor/github.com/pelletier/go-toml/doc.go | 23 - .../pelletier/go-toml/example-crlf.toml | 30 - .../github.com/pelletier/go-toml/example.toml | 30 - vendor/github.com/pelletier/go-toml/fuzz.go | 31 - vendor/github.com/pelletier/go-toml/fuzz.sh | 15 - vendor/github.com/pelletier/go-toml/fuzzit.sh | 26 - vendor/github.com/pelletier/go-toml/go.mod | 5 - vendor/github.com/pelletier/go-toml/go.sum | 19 - .../pelletier/go-toml/keysparsing.go | 112 - vendor/github.com/pelletier/go-toml/lexer.go | 807 -- .../github.com/pelletier/go-toml/localtime.go | 281 - .../github.com/pelletier/go-toml/marshal.go | 1269 --- .../go-toml/marshal_OrderPreserve_test.toml | 39 - .../pelletier/go-toml/marshal_test.toml | 39 - vendor/github.com/pelletier/go-toml/parser.go | 493 -- .../github.com/pelletier/go-toml/position.go | 29 - vendor/github.com/pelletier/go-toml/token.go | 134 - vendor/github.com/pelletier/go-toml/toml.go | 529 -- .../pelletier/go-toml/tomltree_create.go | 155 - .../pelletier/go-toml/tomltree_write.go | 517 -- vendor/github.com/spf13/afero/.gitignore | 2 - vendor/github.com/spf13/afero/.travis.yml | 25 - vendor/github.com/spf13/afero/LICENSE.txt | 174 - vendor/github.com/spf13/afero/README.md | 430 - vendor/github.com/spf13/afero/afero.go | 111 - vendor/github.com/spf13/afero/appveyor.yml | 15 - vendor/github.com/spf13/afero/basepath.go | 211 - .../github.com/spf13/afero/cacheOnReadFs.go | 311 - vendor/github.com/spf13/afero/const_bsds.go | 22 - .../github.com/spf13/afero/const_win_unix.go | 26 - .../github.com/spf13/afero/copyOnWriteFs.go | 326 - vendor/github.com/spf13/afero/go.mod | 9 - vendor/github.com/spf13/afero/go.sum | 29 - vendor/github.com/spf13/afero/httpFs.go | 114 - vendor/github.com/spf13/afero/ioutil.go | 240 - vendor/github.com/spf13/afero/lstater.go | 27 - vendor/github.com/spf13/afero/match.go | 110 - vendor/github.com/spf13/afero/mem/dir.go | 37 - vendor/github.com/spf13/afero/mem/dirmap.go | 43 - vendor/github.com/spf13/afero/mem/file.go | 338 - vendor/github.com/spf13/afero/memmap.go | 404 - vendor/github.com/spf13/afero/os.go | 113 - vendor/github.com/spf13/afero/path.go | 106 - vendor/github.com/spf13/afero/readonlyfs.go | 96 - vendor/github.com/spf13/afero/regexpfs.go | 224 - vendor/github.com/spf13/afero/symlink.go | 55 - vendor/github.com/spf13/afero/unionFile.go | 317 - vendor/github.com/spf13/afero/util.go | 330 - vendor/github.com/spf13/cast/.gitignore | 25 - vendor/github.com/spf13/cast/.travis.yml | 16 - vendor/github.com/spf13/cast/LICENSE | 21 - vendor/github.com/spf13/cast/Makefile | 40 - vendor/github.com/spf13/cast/README.md | 75 - vendor/github.com/spf13/cast/cast.go | 171 - vendor/github.com/spf13/cast/caste.go | 1249 --- vendor/github.com/spf13/cast/go.mod | 7 - vendor/github.com/spf13/cast/go.sum | 6 - vendor/github.com/spf13/cobra/.gitignore | 39 - vendor/github.com/spf13/cobra/.golangci.yml | 48 - vendor/github.com/spf13/cobra/.mailmap | 3 - vendor/github.com/spf13/cobra/.travis.yml | 28 - vendor/github.com/spf13/cobra/CHANGELOG.md | 51 - vendor/github.com/spf13/cobra/CONDUCT.md | 37 - vendor/github.com/spf13/cobra/CONTRIBUTING.md | 50 - vendor/github.com/spf13/cobra/LICENSE.txt | 174 - vendor/github.com/spf13/cobra/Makefile | 40 - vendor/github.com/spf13/cobra/README.md | 760 -- vendor/github.com/spf13/cobra/args.go | 109 - .../spf13/cobra/bash_completions.go | 681 -- .../spf13/cobra/bash_completions.md | 91 - vendor/github.com/spf13/cobra/cobra.go | 222 - vendor/github.com/spf13/cobra/command.go | 1666 ---- .../github.com/spf13/cobra/command_notwin.go | 5 - vendor/github.com/spf13/cobra/command_win.go | 26 - .../spf13/cobra/custom_completions.go | 557 -- .../spf13/cobra/fish_completions.go | 207 - .../spf13/cobra/fish_completions.md | 4 - vendor/github.com/spf13/cobra/go.mod | 12 - vendor/github.com/spf13/cobra/go.sum | 313 - .../spf13/cobra/powershell_completions.go | 285 - .../spf13/cobra/powershell_completions.md | 3 - .../spf13/cobra/projects_using_cobra.md | 38 - .../spf13/cobra/shell_completions.go | 84 - .../spf13/cobra/shell_completions.md | 483 - .../github.com/spf13/cobra/zsh_completions.go | 240 - .../github.com/spf13/cobra/zsh_completions.md | 48 - .../spf13/jwalterweatherman/.gitignore | 24 - .../spf13/jwalterweatherman/LICENSE | 21 - .../spf13/jwalterweatherman/README.md | 148 - .../jwalterweatherman/default_notepad.go | 111 - .../github.com/spf13/jwalterweatherman/go.mod | 7 - .../spf13/jwalterweatherman/log_counter.go | 46 - .../spf13/jwalterweatherman/notepad.go | 225 - vendor/github.com/spf13/pflag/.gitignore | 2 - vendor/github.com/spf13/pflag/.travis.yml | 22 - vendor/github.com/spf13/pflag/LICENSE | 28 - vendor/github.com/spf13/pflag/README.md | 296 - vendor/github.com/spf13/pflag/bool.go | 94 - vendor/github.com/spf13/pflag/bool_slice.go | 185 - vendor/github.com/spf13/pflag/bytes.go | 209 - vendor/github.com/spf13/pflag/count.go | 96 - vendor/github.com/spf13/pflag/duration.go | 86 - .../github.com/spf13/pflag/duration_slice.go | 166 - vendor/github.com/spf13/pflag/flag.go | 1239 --- vendor/github.com/spf13/pflag/float32.go | 88 - .../github.com/spf13/pflag/float32_slice.go | 174 - vendor/github.com/spf13/pflag/float64.go | 84 - .../github.com/spf13/pflag/float64_slice.go | 166 - vendor/github.com/spf13/pflag/go.mod | 3 - vendor/github.com/spf13/pflag/go.sum | 0 vendor/github.com/spf13/pflag/golangflag.go | 105 - vendor/github.com/spf13/pflag/int.go | 84 - vendor/github.com/spf13/pflag/int16.go | 88 - vendor/github.com/spf13/pflag/int32.go | 88 - vendor/github.com/spf13/pflag/int32_slice.go | 174 - vendor/github.com/spf13/pflag/int64.go | 84 - vendor/github.com/spf13/pflag/int64_slice.go | 166 - vendor/github.com/spf13/pflag/int8.go | 88 - vendor/github.com/spf13/pflag/int_slice.go | 158 - vendor/github.com/spf13/pflag/ip.go | 94 - vendor/github.com/spf13/pflag/ip_slice.go | 186 - vendor/github.com/spf13/pflag/ipmask.go | 122 - vendor/github.com/spf13/pflag/ipnet.go | 98 - vendor/github.com/spf13/pflag/string.go | 80 - vendor/github.com/spf13/pflag/string_array.go | 129 - vendor/github.com/spf13/pflag/string_slice.go | 163 - .../github.com/spf13/pflag/string_to_int.go | 149 - .../github.com/spf13/pflag/string_to_int64.go | 149 - .../spf13/pflag/string_to_string.go | 160 - vendor/github.com/spf13/pflag/uint.go | 88 - vendor/github.com/spf13/pflag/uint16.go | 88 - vendor/github.com/spf13/pflag/uint32.go | 88 - vendor/github.com/spf13/pflag/uint64.go | 88 - vendor/github.com/spf13/pflag/uint8.go | 88 - vendor/github.com/spf13/pflag/uint_slice.go | 168 - vendor/github.com/spf13/viper/.editorconfig | 15 - vendor/github.com/spf13/viper/.gitignore | 5 - vendor/github.com/spf13/viper/.golangci.yml | 27 - vendor/github.com/spf13/viper/LICENSE | 21 - vendor/github.com/spf13/viper/Makefile | 76 - vendor/github.com/spf13/viper/README.md | 806 -- vendor/github.com/spf13/viper/flags.go | 57 - vendor/github.com/spf13/viper/go.mod | 40 - vendor/github.com/spf13/viper/go.sum | 388 - vendor/github.com/spf13/viper/util.go | 230 - vendor/github.com/spf13/viper/viper.go | 2025 ----- vendor/github.com/subosito/gotenv/.env | 1 - .../github.com/subosito/gotenv/.env.invalid | 1 - vendor/github.com/subosito/gotenv/.gitignore | 3 - vendor/github.com/subosito/gotenv/.travis.yml | 10 - .../github.com/subosito/gotenv/CHANGELOG.md | 47 - vendor/github.com/subosito/gotenv/LICENSE | 21 - vendor/github.com/subosito/gotenv/README.md | 131 - .../github.com/subosito/gotenv/appveyor.yml | 9 - vendor/github.com/subosito/gotenv/gotenv.go | 265 - .../github.com/vultr/govultr/v2/.codecov.yml | 8 - vendor/github.com/vultr/govultr/v2/.gitignore | 22 - .../github.com/vultr/govultr/v2/.travis.yml | 16 - .../github.com/vultr/govultr/v2/CHANGELOG.md | 34 - .../vultr/govultr/v2/CONTRIBUTING.md | 64 - vendor/github.com/vultr/govultr/v2/LICENSE | 21 - vendor/github.com/vultr/govultr/v2/README.md | 137 - vendor/github.com/vultr/govultr/v2/account.go | 48 - .../vultr/govultr/v2/application.go | 57 - vendor/github.com/vultr/govultr/v2/backup.go | 80 - .../vultr/govultr/v2/bare_metal_server.go | 412 - .../vultr/govultr/v2/block_storage.go | 181 - .../vultr/govultr/v2/domain_records.go | 123 - vendor/github.com/vultr/govultr/v2/domains.go | 184 - .../vultr/govultr/v2/firewall_group.go | 131 - .../vultr/govultr/v2/firewall_rule.go | 126 - vendor/github.com/vultr/govultr/v2/go.mod | 8 - vendor/github.com/vultr/govultr/v2/go.sum | 14 - vendor/github.com/vultr/govultr/v2/govultr.go | 240 - .../github.com/vultr/govultr/v2/instance.go | 737 -- vendor/github.com/vultr/govultr/v2/ip.go | 24 - vendor/github.com/vultr/govultr/v2/iso.go | 155 - .../vultr/govultr/v2/listOptions.go | 7 - .../vultr/govultr/v2/load_balancer.go | 270 - vendor/github.com/vultr/govultr/v2/meta.go | 13 - vendor/github.com/vultr/govultr/v2/network.go | 130 - .../vultr/govultr/v2/object_storage.go | 197 - vendor/github.com/vultr/govultr/v2/os.go | 56 - vendor/github.com/vultr/govultr/v2/plans.go | 111 - vendor/github.com/vultr/govultr/v2/regions.go | 91 - .../vultr/govultr/v2/reserved_ip.go | 168 - .../github.com/vultr/govultr/v2/snapshot.go | 141 - vendor/github.com/vultr/govultr/v2/ssh_key.go | 129 - .../vultr/govultr/v2/startup_script.go | 131 - vendor/github.com/vultr/govultr/v2/user.go | 133 - vendor/golang.org/x/net/AUTHORS | 3 - vendor/golang.org/x/net/CONTRIBUTORS | 3 - vendor/golang.org/x/net/LICENSE | 27 - vendor/golang.org/x/net/PATENTS | 22 - vendor/golang.org/x/net/context/context.go | 56 - .../x/net/context/ctxhttp/ctxhttp.go | 71 - vendor/golang.org/x/net/context/go17.go | 72 - vendor/golang.org/x/net/context/go19.go | 20 - vendor/golang.org/x/net/context/pre_go17.go | 300 - vendor/golang.org/x/net/context/pre_go19.go | 109 - vendor/golang.org/x/oauth2/.travis.yml | 13 - vendor/golang.org/x/oauth2/AUTHORS | 3 - vendor/golang.org/x/oauth2/CONTRIBUTING.md | 26 - vendor/golang.org/x/oauth2/CONTRIBUTORS | 3 - vendor/golang.org/x/oauth2/LICENSE | 27 - vendor/golang.org/x/oauth2/README.md | 36 - vendor/golang.org/x/oauth2/go.mod | 9 - vendor/golang.org/x/oauth2/go.sum | 361 - .../x/oauth2/internal/client_appengine.go | 13 - vendor/golang.org/x/oauth2/internal/doc.go | 6 - vendor/golang.org/x/oauth2/internal/oauth2.go | 37 - vendor/golang.org/x/oauth2/internal/token.go | 294 - .../golang.org/x/oauth2/internal/transport.go | 33 - vendor/golang.org/x/oauth2/oauth2.go | 381 - vendor/golang.org/x/oauth2/token.go | 178 - vendor/golang.org/x/oauth2/transport.go | 89 - vendor/golang.org/x/sys/AUTHORS | 3 - vendor/golang.org/x/sys/CONTRIBUTORS | 3 - vendor/golang.org/x/sys/LICENSE | 27 - vendor/golang.org/x/sys/PATENTS | 22 - .../sys/internal/unsafeheader/unsafeheader.go | 30 - vendor/golang.org/x/sys/unix/.gitignore | 2 - vendor/golang.org/x/sys/unix/README.md | 184 - .../golang.org/x/sys/unix/affinity_linux.go | 86 - vendor/golang.org/x/sys/unix/aliases.go | 14 - vendor/golang.org/x/sys/unix/asm_aix_ppc64.s | 17 - vendor/golang.org/x/sys/unix/asm_darwin_386.s | 29 - .../golang.org/x/sys/unix/asm_darwin_amd64.s | 29 - vendor/golang.org/x/sys/unix/asm_darwin_arm.s | 30 - .../golang.org/x/sys/unix/asm_darwin_arm64.s | 30 - .../x/sys/unix/asm_dragonfly_amd64.s | 29 - .../golang.org/x/sys/unix/asm_freebsd_386.s | 29 - .../golang.org/x/sys/unix/asm_freebsd_amd64.s | 29 - .../golang.org/x/sys/unix/asm_freebsd_arm.s | 29 - .../golang.org/x/sys/unix/asm_freebsd_arm64.s | 29 - vendor/golang.org/x/sys/unix/asm_linux_386.s | 65 - .../golang.org/x/sys/unix/asm_linux_amd64.s | 57 - vendor/golang.org/x/sys/unix/asm_linux_arm.s | 56 - .../golang.org/x/sys/unix/asm_linux_arm64.s | 52 - .../golang.org/x/sys/unix/asm_linux_mips64x.s | 56 - .../golang.org/x/sys/unix/asm_linux_mipsx.s | 54 - .../golang.org/x/sys/unix/asm_linux_ppc64x.s | 44 - .../golang.org/x/sys/unix/asm_linux_riscv64.s | 47 - .../golang.org/x/sys/unix/asm_linux_s390x.s | 56 - vendor/golang.org/x/sys/unix/asm_netbsd_386.s | 29 - .../golang.org/x/sys/unix/asm_netbsd_amd64.s | 29 - vendor/golang.org/x/sys/unix/asm_netbsd_arm.s | 29 - .../golang.org/x/sys/unix/asm_netbsd_arm64.s | 29 - .../golang.org/x/sys/unix/asm_openbsd_386.s | 29 - .../golang.org/x/sys/unix/asm_openbsd_amd64.s | 29 - .../golang.org/x/sys/unix/asm_openbsd_arm.s | 29 - .../golang.org/x/sys/unix/asm_openbsd_arm64.s | 29 - .../x/sys/unix/asm_openbsd_mips64.s | 29 - .../golang.org/x/sys/unix/asm_solaris_amd64.s | 17 - .../golang.org/x/sys/unix/bluetooth_linux.go | 36 - vendor/golang.org/x/sys/unix/cap_freebsd.go | 195 - vendor/golang.org/x/sys/unix/constants.go | 13 - vendor/golang.org/x/sys/unix/dev_aix_ppc.go | 27 - vendor/golang.org/x/sys/unix/dev_aix_ppc64.go | 29 - vendor/golang.org/x/sys/unix/dev_darwin.go | 24 - vendor/golang.org/x/sys/unix/dev_dragonfly.go | 30 - vendor/golang.org/x/sys/unix/dev_freebsd.go | 30 - vendor/golang.org/x/sys/unix/dev_linux.go | 42 - vendor/golang.org/x/sys/unix/dev_netbsd.go | 29 - vendor/golang.org/x/sys/unix/dev_openbsd.go | 29 - vendor/golang.org/x/sys/unix/dirent.go | 102 - vendor/golang.org/x/sys/unix/endian_big.go | 9 - vendor/golang.org/x/sys/unix/endian_little.go | 9 - vendor/golang.org/x/sys/unix/env_unix.go | 31 - .../x/sys/unix/errors_freebsd_386.go | 233 - .../x/sys/unix/errors_freebsd_amd64.go | 233 - .../x/sys/unix/errors_freebsd_arm.go | 226 - .../x/sys/unix/errors_freebsd_arm64.go | 17 - vendor/golang.org/x/sys/unix/fcntl.go | 36 - vendor/golang.org/x/sys/unix/fcntl_darwin.go | 24 - .../x/sys/unix/fcntl_linux_32bit.go | 13 - vendor/golang.org/x/sys/unix/fdset.go | 29 - vendor/golang.org/x/sys/unix/gccgo.go | 60 - vendor/golang.org/x/sys/unix/gccgo_c.c | 45 - .../x/sys/unix/gccgo_linux_amd64.go | 20 - vendor/golang.org/x/sys/unix/ioctl.go | 74 - vendor/golang.org/x/sys/unix/mkall.sh | 243 - vendor/golang.org/x/sys/unix/mkerrors.sh | 726 -- vendor/golang.org/x/sys/unix/pagesize_unix.go | 15 - .../golang.org/x/sys/unix/pledge_openbsd.go | 163 - vendor/golang.org/x/sys/unix/ptrace_darwin.go | 11 - vendor/golang.org/x/sys/unix/ptrace_ios.go | 11 - vendor/golang.org/x/sys/unix/race.go | 30 - vendor/golang.org/x/sys/unix/race0.go | 25 - .../x/sys/unix/readdirent_getdents.go | 12 - .../x/sys/unix/readdirent_getdirentries.go | 19 - .../x/sys/unix/sockcmsg_dragonfly.go | 16 - .../golang.org/x/sys/unix/sockcmsg_linux.go | 36 - vendor/golang.org/x/sys/unix/sockcmsg_unix.go | 92 - .../x/sys/unix/sockcmsg_unix_other.go | 42 - vendor/golang.org/x/sys/unix/str.go | 26 - vendor/golang.org/x/sys/unix/syscall.go | 94 - vendor/golang.org/x/sys/unix/syscall_aix.go | 552 -- .../golang.org/x/sys/unix/syscall_aix_ppc.go | 54 - .../x/sys/unix/syscall_aix_ppc64.go | 85 - vendor/golang.org/x/sys/unix/syscall_bsd.go | 663 -- .../x/sys/unix/syscall_darwin.1_12.go | 31 - .../x/sys/unix/syscall_darwin.1_13.go | 107 - .../golang.org/x/sys/unix/syscall_darwin.go | 668 -- .../x/sys/unix/syscall_darwin_386.go | 50 - .../x/sys/unix/syscall_darwin_amd64.go | 50 - .../x/sys/unix/syscall_darwin_arm.go | 51 - .../x/sys/unix/syscall_darwin_arm64.go | 50 - .../x/sys/unix/syscall_darwin_libSystem.go | 33 - .../x/sys/unix/syscall_dragonfly.go | 541 -- .../x/sys/unix/syscall_dragonfly_amd64.go | 56 - .../golang.org/x/sys/unix/syscall_freebsd.go | 863 -- .../x/sys/unix/syscall_freebsd_386.go | 66 - .../x/sys/unix/syscall_freebsd_amd64.go | 66 - .../x/sys/unix/syscall_freebsd_arm.go | 62 - .../x/sys/unix/syscall_freebsd_arm64.go | 62 - .../golang.org/x/sys/unix/syscall_illumos.go | 77 - vendor/golang.org/x/sys/unix/syscall_linux.go | 2401 ----- .../x/sys/unix/syscall_linux_386.go | 387 - .../x/sys/unix/syscall_linux_amd64.go | 194 - .../x/sys/unix/syscall_linux_amd64_gc.go | 13 - .../x/sys/unix/syscall_linux_arm.go | 286 - .../x/sys/unix/syscall_linux_arm64.go | 245 - .../golang.org/x/sys/unix/syscall_linux_gc.go | 14 - .../x/sys/unix/syscall_linux_gc_386.go | 16 - .../x/sys/unix/syscall_linux_gc_arm.go | 13 - .../x/sys/unix/syscall_linux_gccgo_386.go | 30 - .../x/sys/unix/syscall_linux_gccgo_arm.go | 20 - .../x/sys/unix/syscall_linux_mips64x.go | 230 - .../x/sys/unix/syscall_linux_mipsx.go | 238 - .../x/sys/unix/syscall_linux_ppc64x.go | 156 - .../x/sys/unix/syscall_linux_riscv64.go | 230 - .../x/sys/unix/syscall_linux_s390x.go | 342 - .../x/sys/unix/syscall_linux_sparc64.go | 151 - .../golang.org/x/sys/unix/syscall_netbsd.go | 603 -- .../x/sys/unix/syscall_netbsd_386.go | 37 - .../x/sys/unix/syscall_netbsd_amd64.go | 37 - .../x/sys/unix/syscall_netbsd_arm.go | 37 - .../x/sys/unix/syscall_netbsd_arm64.go | 37 - .../golang.org/x/sys/unix/syscall_openbsd.go | 390 - .../x/sys/unix/syscall_openbsd_386.go | 41 - .../x/sys/unix/syscall_openbsd_amd64.go | 41 - .../x/sys/unix/syscall_openbsd_arm.go | 41 - .../x/sys/unix/syscall_openbsd_arm64.go | 41 - .../x/sys/unix/syscall_openbsd_mips64.go | 35 - .../golang.org/x/sys/unix/syscall_solaris.go | 740 -- .../x/sys/unix/syscall_solaris_amd64.go | 27 - vendor/golang.org/x/sys/unix/syscall_unix.go | 430 - .../golang.org/x/sys/unix/syscall_unix_gc.go | 15 - .../x/sys/unix/syscall_unix_gc_ppc64x.go | 24 - vendor/golang.org/x/sys/unix/timestruct.go | 76 - .../golang.org/x/sys/unix/unveil_openbsd.go | 42 - vendor/golang.org/x/sys/unix/xattr_bsd.go | 240 - .../golang.org/x/sys/unix/zerrors_aix_ppc.go | 1384 --- .../x/sys/unix/zerrors_aix_ppc64.go | 1385 --- .../x/sys/unix/zerrors_darwin_386.go | 1788 ---- .../x/sys/unix/zerrors_darwin_amd64.go | 1788 ---- .../x/sys/unix/zerrors_darwin_arm.go | 1788 ---- .../x/sys/unix/zerrors_darwin_arm64.go | 1788 ---- .../x/sys/unix/zerrors_dragonfly_amd64.go | 1737 ---- .../x/sys/unix/zerrors_freebsd_386.go | 1936 ---- .../x/sys/unix/zerrors_freebsd_amd64.go | 1935 ---- .../x/sys/unix/zerrors_freebsd_arm.go | 1825 ---- .../x/sys/unix/zerrors_freebsd_arm64.go | 1936 ---- vendor/golang.org/x/sys/unix/zerrors_linux.go | 2786 ------ .../x/sys/unix/zerrors_linux_386.go | 790 -- .../x/sys/unix/zerrors_linux_amd64.go | 790 -- .../x/sys/unix/zerrors_linux_arm.go | 796 -- .../x/sys/unix/zerrors_linux_arm64.go | 786 -- .../x/sys/unix/zerrors_linux_mips.go | 797 -- .../x/sys/unix/zerrors_linux_mips64.go | 797 -- .../x/sys/unix/zerrors_linux_mips64le.go | 797 -- .../x/sys/unix/zerrors_linux_mipsle.go | 797 -- .../x/sys/unix/zerrors_linux_ppc64.go | 853 -- .../x/sys/unix/zerrors_linux_ppc64le.go | 853 -- .../x/sys/unix/zerrors_linux_riscv64.go | 777 -- .../x/sys/unix/zerrors_linux_s390x.go | 850 -- .../x/sys/unix/zerrors_linux_sparc64.go | 847 -- .../x/sys/unix/zerrors_netbsd_386.go | 1779 ---- .../x/sys/unix/zerrors_netbsd_amd64.go | 1769 ---- .../x/sys/unix/zerrors_netbsd_arm.go | 1758 ---- .../x/sys/unix/zerrors_netbsd_arm64.go | 1769 ---- .../x/sys/unix/zerrors_openbsd_386.go | 1664 ---- .../x/sys/unix/zerrors_openbsd_amd64.go | 1774 ---- .../x/sys/unix/zerrors_openbsd_arm.go | 1666 ---- .../x/sys/unix/zerrors_openbsd_arm64.go | 1797 ---- .../x/sys/unix/zerrors_openbsd_mips64.go | 1862 ---- .../x/sys/unix/zerrors_solaris_amd64.go | 1553 ---- .../x/sys/unix/zptrace_armnn_linux.go | 41 - .../x/sys/unix/zptrace_linux_arm64.go | 17 - .../x/sys/unix/zptrace_mipsnn_linux.go | 50 - .../x/sys/unix/zptrace_mipsnnle_linux.go | 50 - .../x/sys/unix/zptrace_x86_linux.go | 80 - .../golang.org/x/sys/unix/zsyscall_aix_ppc.go | 1484 ---- .../x/sys/unix/zsyscall_aix_ppc64.go | 1442 --- .../x/sys/unix/zsyscall_aix_ppc64_gc.go | 1192 --- .../x/sys/unix/zsyscall_aix_ppc64_gccgo.go | 1070 --- .../x/sys/unix/zsyscall_darwin_386.1_13.go | 39 - .../x/sys/unix/zsyscall_darwin_386.1_13.s | 12 - .../x/sys/unix/zsyscall_darwin_386.go | 2430 ------ .../x/sys/unix/zsyscall_darwin_386.s | 290 - .../x/sys/unix/zsyscall_darwin_amd64.1_13.go | 39 - .../x/sys/unix/zsyscall_darwin_amd64.1_13.s | 12 - .../x/sys/unix/zsyscall_darwin_amd64.go | 2430 ------ .../x/sys/unix/zsyscall_darwin_amd64.s | 290 - .../x/sys/unix/zsyscall_darwin_arm.1_13.go | 39 - .../x/sys/unix/zsyscall_darwin_arm.1_13.s | 12 - .../x/sys/unix/zsyscall_darwin_arm.go | 2416 ----- .../x/sys/unix/zsyscall_darwin_arm.s | 288 - .../x/sys/unix/zsyscall_darwin_arm64.1_13.go | 39 - .../x/sys/unix/zsyscall_darwin_arm64.1_13.s | 12 - .../x/sys/unix/zsyscall_darwin_arm64.go | 2430 ------ .../x/sys/unix/zsyscall_darwin_arm64.s | 290 - .../x/sys/unix/zsyscall_dragonfly_amd64.go | 1676 ---- .../x/sys/unix/zsyscall_freebsd_386.go | 2015 ----- .../x/sys/unix/zsyscall_freebsd_amd64.go | 2015 ----- .../x/sys/unix/zsyscall_freebsd_arm.go | 2015 ----- .../x/sys/unix/zsyscall_freebsd_arm64.go | 2015 ----- .../x/sys/unix/zsyscall_illumos_amd64.go | 101 - .../golang.org/x/sys/unix/zsyscall_linux.go | 1933 ---- .../x/sys/unix/zsyscall_linux_386.go | 578 -- .../x/sys/unix/zsyscall_linux_amd64.go | 745 -- .../x/sys/unix/zsyscall_linux_arm.go | 715 -- .../x/sys/unix/zsyscall_linux_arm64.go | 602 -- .../x/sys/unix/zsyscall_linux_mips.go | 758 -- .../x/sys/unix/zsyscall_linux_mips64.go | 729 -- .../x/sys/unix/zsyscall_linux_mips64le.go | 729 -- .../x/sys/unix/zsyscall_linux_mipsle.go | 758 -- .../x/sys/unix/zsyscall_linux_ppc64.go | 807 -- .../x/sys/unix/zsyscall_linux_ppc64le.go | 807 -- .../x/sys/unix/zsyscall_linux_riscv64.go | 582 -- .../x/sys/unix/zsyscall_linux_s390x.go | 577 -- .../x/sys/unix/zsyscall_linux_sparc64.go | 740 -- .../x/sys/unix/zsyscall_netbsd_386.go | 1851 ---- .../x/sys/unix/zsyscall_netbsd_amd64.go | 1851 ---- .../x/sys/unix/zsyscall_netbsd_arm.go | 1851 ---- .../x/sys/unix/zsyscall_netbsd_arm64.go | 1851 ---- .../x/sys/unix/zsyscall_openbsd_386.go | 1692 ---- .../x/sys/unix/zsyscall_openbsd_amd64.go | 1692 ---- .../x/sys/unix/zsyscall_openbsd_arm.go | 1692 ---- .../x/sys/unix/zsyscall_openbsd_arm64.go | 1692 ---- .../x/sys/unix/zsyscall_openbsd_mips64.go | 1692 ---- .../x/sys/unix/zsyscall_solaris_amd64.go | 1967 ----- .../x/sys/unix/zsysctl_openbsd_386.go | 273 - .../x/sys/unix/zsysctl_openbsd_amd64.go | 271 - .../x/sys/unix/zsysctl_openbsd_arm.go | 273 - .../x/sys/unix/zsysctl_openbsd_arm64.go | 275 - .../x/sys/unix/zsysctl_openbsd_mips64.go | 279 - .../x/sys/unix/zsysnum_darwin_386.go | 437 - .../x/sys/unix/zsysnum_darwin_amd64.go | 439 - .../x/sys/unix/zsysnum_darwin_arm.go | 437 - .../x/sys/unix/zsysnum_darwin_arm64.go | 437 - .../x/sys/unix/zsysnum_dragonfly_amd64.go | 316 - .../x/sys/unix/zsysnum_freebsd_386.go | 396 - .../x/sys/unix/zsysnum_freebsd_amd64.go | 396 - .../x/sys/unix/zsysnum_freebsd_arm.go | 396 - .../x/sys/unix/zsysnum_freebsd_arm64.go | 396 - .../x/sys/unix/zsysnum_linux_386.go | 439 - .../x/sys/unix/zsysnum_linux_amd64.go | 361 - .../x/sys/unix/zsysnum_linux_arm.go | 403 - .../x/sys/unix/zsysnum_linux_arm64.go | 306 - .../x/sys/unix/zsysnum_linux_mips.go | 424 - .../x/sys/unix/zsysnum_linux_mips64.go | 354 - .../x/sys/unix/zsysnum_linux_mips64le.go | 354 - .../x/sys/unix/zsysnum_linux_mipsle.go | 424 - .../x/sys/unix/zsysnum_linux_ppc64.go | 403 - .../x/sys/unix/zsysnum_linux_ppc64le.go | 403 - .../x/sys/unix/zsysnum_linux_riscv64.go | 305 - .../x/sys/unix/zsysnum_linux_s390x.go | 368 - .../x/sys/unix/zsysnum_linux_sparc64.go | 382 - .../x/sys/unix/zsysnum_netbsd_386.go | 274 - .../x/sys/unix/zsysnum_netbsd_amd64.go | 274 - .../x/sys/unix/zsysnum_netbsd_arm.go | 274 - .../x/sys/unix/zsysnum_netbsd_arm64.go | 274 - .../x/sys/unix/zsysnum_openbsd_386.go | 218 - .../x/sys/unix/zsysnum_openbsd_amd64.go | 218 - .../x/sys/unix/zsysnum_openbsd_arm.go | 218 - .../x/sys/unix/zsysnum_openbsd_arm64.go | 217 - .../x/sys/unix/zsysnum_openbsd_mips64.go | 220 - .../golang.org/x/sys/unix/ztypes_aix_ppc.go | 353 - .../golang.org/x/sys/unix/ztypes_aix_ppc64.go | 357 - .../x/sys/unix/ztypes_darwin_386.go | 516 -- .../x/sys/unix/ztypes_darwin_amd64.go | 521 -- .../x/sys/unix/ztypes_darwin_arm.go | 516 -- .../x/sys/unix/ztypes_darwin_arm64.go | 521 -- .../x/sys/unix/ztypes_dragonfly_amd64.go | 470 - .../x/sys/unix/ztypes_freebsd_386.go | 710 -- .../x/sys/unix/ztypes_freebsd_amd64.go | 713 -- .../x/sys/unix/ztypes_freebsd_arm.go | 694 -- .../x/sys/unix/ztypes_freebsd_arm64.go | 691 -- vendor/golang.org/x/sys/unix/ztypes_linux.go | 3682 -------- .../golang.org/x/sys/unix/ztypes_linux_386.go | 619 -- .../x/sys/unix/ztypes_linux_amd64.go | 637 -- .../golang.org/x/sys/unix/ztypes_linux_arm.go | 614 -- .../x/sys/unix/ztypes_linux_arm64.go | 616 -- .../x/sys/unix/ztypes_linux_mips.go | 620 -- .../x/sys/unix/ztypes_linux_mips64.go | 619 -- .../x/sys/unix/ztypes_linux_mips64le.go | 619 -- .../x/sys/unix/ztypes_linux_mipsle.go | 620 -- .../x/sys/unix/ztypes_linux_ppc64.go | 626 -- .../x/sys/unix/ztypes_linux_ppc64le.go | 626 -- .../x/sys/unix/ztypes_linux_riscv64.go | 644 -- .../x/sys/unix/ztypes_linux_s390x.go | 640 -- .../x/sys/unix/ztypes_linux_sparc64.go | 621 -- .../x/sys/unix/ztypes_netbsd_386.go | 499 -- .../x/sys/unix/ztypes_netbsd_amd64.go | 507 -- .../x/sys/unix/ztypes_netbsd_arm.go | 504 -- .../x/sys/unix/ztypes_netbsd_arm64.go | 507 -- .../x/sys/unix/ztypes_openbsd_386.go | 572 -- .../x/sys/unix/ztypes_openbsd_amd64.go | 572 -- .../x/sys/unix/ztypes_openbsd_arm.go | 573 -- .../x/sys/unix/ztypes_openbsd_arm64.go | 566 -- .../x/sys/unix/ztypes_openbsd_mips64.go | 566 -- .../x/sys/unix/ztypes_solaris_amd64.go | 441 - vendor/golang.org/x/text/AUTHORS | 3 - vendor/golang.org/x/text/CONTRIBUTORS | 3 - vendor/golang.org/x/text/LICENSE | 27 - vendor/golang.org/x/text/PATENTS | 22 - .../golang.org/x/text/transform/transform.go | 709 -- .../x/text/unicode/norm/composition.go | 512 -- .../x/text/unicode/norm/forminfo.go | 278 - .../golang.org/x/text/unicode/norm/input.go | 109 - vendor/golang.org/x/text/unicode/norm/iter.go | 458 - .../x/text/unicode/norm/normalize.go | 609 -- .../x/text/unicode/norm/readwriter.go | 125 - .../x/text/unicode/norm/tables10.0.0.go | 7657 ---------------- .../x/text/unicode/norm/tables11.0.0.go | 7693 ---------------- .../x/text/unicode/norm/tables12.0.0.go | 7710 ---------------- .../x/text/unicode/norm/tables13.0.0.go | 7760 ----------------- .../x/text/unicode/norm/tables9.0.0.go | 7637 ---------------- .../x/text/unicode/norm/transform.go | 88 - vendor/golang.org/x/text/unicode/norm/trie.go | 54 - vendor/google.golang.org/appengine/LICENSE | 202 - .../appengine/internal/api.go | 678 -- .../appengine/internal/api_classic.go | 169 - .../appengine/internal/api_common.go | 123 - .../appengine/internal/app_id.go | 28 - .../appengine/internal/base/api_base.pb.go | 308 - .../appengine/internal/base/api_base.proto | 33 - .../internal/datastore/datastore_v3.pb.go | 4367 ---------- .../internal/datastore/datastore_v3.proto | 551 -- .../appengine/internal/identity.go | 55 - .../appengine/internal/identity_classic.go | 61 - .../appengine/internal/identity_flex.go | 11 - .../appengine/internal/identity_vm.go | 134 - .../appengine/internal/internal.go | 110 - .../appengine/internal/log/log_service.pb.go | 1313 --- .../appengine/internal/log/log_service.proto | 150 - .../appengine/internal/main.go | 16 - .../appengine/internal/main_common.go | 7 - .../appengine/internal/main_vm.go | 69 - .../appengine/internal/metadata.go | 60 - .../appengine/internal/net.go | 56 - .../appengine/internal/regen.sh | 40 - .../internal/remote_api/remote_api.pb.go | 361 - .../internal/remote_api/remote_api.proto | 44 - .../appengine/internal/transaction.go | 115 - .../internal/urlfetch/urlfetch_service.pb.go | 527 -- .../internal/urlfetch/urlfetch_service.proto | 64 - .../appengine/urlfetch/urlfetch.go | 210 - vendor/google.golang.org/protobuf/AUTHORS | 3 - .../google.golang.org/protobuf/CONTRIBUTORS | 3 - vendor/google.golang.org/protobuf/LICENSE | 27 - vendor/google.golang.org/protobuf/PATENTS | 22 - .../protobuf/encoding/prototext/decode.go | 791 -- .../protobuf/encoding/prototext/doc.go | 7 - .../protobuf/encoding/prototext/encode.go | 433 - .../protobuf/encoding/protowire/wire.go | 538 -- .../protobuf/internal/descfmt/stringer.go | 316 - .../protobuf/internal/descopts/options.go | 29 - .../protobuf/internal/detrand/rand.go | 61 - .../internal/encoding/defval/default.go | 213 - .../encoding/messageset/messageset.go | 258 - .../protobuf/internal/encoding/tag/tag.go | 207 - .../protobuf/internal/encoding/text/decode.go | 665 -- .../internal/encoding/text/decode_number.go | 190 - .../internal/encoding/text/decode_string.go | 161 - .../internal/encoding/text/decode_token.go | 373 - .../protobuf/internal/encoding/text/doc.go | 29 - .../protobuf/internal/encoding/text/encode.go | 267 - .../protobuf/internal/errors/errors.go | 89 - .../protobuf/internal/errors/is_go112.go | 39 - .../protobuf/internal/errors/is_go113.go | 12 - .../protobuf/internal/fieldsort/fieldsort.go | 40 - .../protobuf/internal/filedesc/build.go | 155 - .../protobuf/internal/filedesc/desc.go | 614 -- .../protobuf/internal/filedesc/desc_init.go | 471 - .../protobuf/internal/filedesc/desc_lazy.go | 704 -- .../protobuf/internal/filedesc/desc_list.go | 282 - .../internal/filedesc/desc_list_gen.go | 345 - .../protobuf/internal/filedesc/placeholder.go | 107 - .../protobuf/internal/filetype/build.go | 297 - .../protobuf/internal/flags/flags.go | 24 - .../internal/flags/proto_legacy_disable.go | 9 - .../internal/flags/proto_legacy_enable.go | 9 - .../protobuf/internal/genid/any_gen.go | 34 - .../protobuf/internal/genid/api_gen.go | 106 - .../protobuf/internal/genid/descriptor_gen.go | 829 -- .../protobuf/internal/genid/doc.go | 11 - .../protobuf/internal/genid/duration_gen.go | 34 - .../protobuf/internal/genid/empty_gen.go | 19 - .../protobuf/internal/genid/field_mask_gen.go | 31 - .../protobuf/internal/genid/goname.go | 25 - .../protobuf/internal/genid/map_entry.go | 16 - .../internal/genid/source_context_gen.go | 31 - .../protobuf/internal/genid/struct_gen.go | 116 - .../protobuf/internal/genid/timestamp_gen.go | 34 - .../protobuf/internal/genid/type_gen.go | 184 - .../protobuf/internal/genid/wrappers.go | 13 - .../protobuf/internal/genid/wrappers_gen.go | 175 - .../protobuf/internal/impl/api_export.go | 177 - .../protobuf/internal/impl/checkinit.go | 141 - .../protobuf/internal/impl/codec_extension.go | 223 - .../protobuf/internal/impl/codec_field.go | 828 -- .../protobuf/internal/impl/codec_gen.go | 5637 ------------ .../protobuf/internal/impl/codec_map.go | 389 - .../protobuf/internal/impl/codec_map_go111.go | 37 - .../protobuf/internal/impl/codec_map_go112.go | 11 - .../protobuf/internal/impl/codec_message.go | 159 - .../internal/impl/codec_messageset.go | 120 - .../protobuf/internal/impl/codec_reflect.go | 209 - .../protobuf/internal/impl/codec_tables.go | 557 -- .../protobuf/internal/impl/codec_unsafe.go | 17 - .../protobuf/internal/impl/convert.go | 467 - .../protobuf/internal/impl/convert_list.go | 141 - .../protobuf/internal/impl/convert_map.go | 121 - .../protobuf/internal/impl/decode.go | 274 - .../protobuf/internal/impl/encode.go | 199 - .../protobuf/internal/impl/enum.go | 21 - .../protobuf/internal/impl/extension.go | 156 - .../protobuf/internal/impl/legacy_enum.go | 219 - .../protobuf/internal/impl/legacy_export.go | 92 - .../internal/impl/legacy_extension.go | 175 - .../protobuf/internal/impl/legacy_file.go | 81 - .../protobuf/internal/impl/legacy_message.go | 502 -- .../protobuf/internal/impl/merge.go | 176 - .../protobuf/internal/impl/merge_gen.go | 209 - .../protobuf/internal/impl/message.go | 215 - .../protobuf/internal/impl/message_reflect.go | 364 - .../internal/impl/message_reflect_field.go | 466 - .../internal/impl/message_reflect_gen.go | 249 - .../protobuf/internal/impl/pointer_reflect.go | 177 - .../protobuf/internal/impl/pointer_unsafe.go | 173 - .../protobuf/internal/impl/validate.go | 576 -- .../protobuf/internal/impl/weak.go | 74 - .../protobuf/internal/mapsort/mapsort.go | 43 - .../protobuf/internal/pragma/pragma.go | 29 - .../protobuf/internal/set/ints.go | 58 - .../protobuf/internal/strs/strings.go | 196 - .../protobuf/internal/strs/strings_pure.go | 27 - .../protobuf/internal/strs/strings_unsafe.go | 94 - .../protobuf/internal/version/version.go | 79 - .../protobuf/proto/checkinit.go | 71 - .../protobuf/proto/decode.go | 274 - .../protobuf/proto/decode_gen.go | 603 -- .../google.golang.org/protobuf/proto/doc.go | 94 - .../protobuf/proto/encode.go | 346 - .../protobuf/proto/encode_gen.go | 97 - .../google.golang.org/protobuf/proto/equal.go | 154 - .../protobuf/proto/extension.go | 92 - .../google.golang.org/protobuf/proto/merge.go | 139 - .../protobuf/proto/messageset.go | 88 - .../google.golang.org/protobuf/proto/proto.go | 34 - .../protobuf/proto/proto_methods.go | 19 - .../protobuf/proto/proto_reflect.go | 19 - .../google.golang.org/protobuf/proto/reset.go | 43 - .../google.golang.org/protobuf/proto/size.go | 97 - .../protobuf/proto/size_gen.go | 55 - .../protobuf/proto/wrappers.go | 29 - .../protobuf/reflect/protoreflect/methods.go | 77 - .../protobuf/reflect/protoreflect/proto.go | 504 -- .../protobuf/reflect/protoreflect/source.go | 52 - .../protobuf/reflect/protoreflect/type.go | 631 -- .../protobuf/reflect/protoreflect/value.go | 285 - .../reflect/protoreflect/value_pure.go | 59 - .../reflect/protoreflect/value_union.go | 411 - .../reflect/protoreflect/value_unsafe.go | 98 - .../reflect/protoregistry/registry.go | 800 -- .../protobuf/runtime/protoiface/legacy.go | 15 - .../protobuf/runtime/protoiface/methods.go | 167 - .../protobuf/runtime/protoimpl/impl.go | 44 - .../protobuf/runtime/protoimpl/version.go | 56 - vendor/gopkg.in/ini.v1/.gitignore | 6 - vendor/gopkg.in/ini.v1/LICENSE | 191 - vendor/gopkg.in/ini.v1/Makefile | 15 - vendor/gopkg.in/ini.v1/README.md | 43 - vendor/gopkg.in/ini.v1/codecov.yml | 9 - vendor/gopkg.in/ini.v1/data_source.go | 76 - vendor/gopkg.in/ini.v1/deprecated.go | 25 - vendor/gopkg.in/ini.v1/error.go | 34 - vendor/gopkg.in/ini.v1/file.go | 517 -- vendor/gopkg.in/ini.v1/helper.go | 24 - vendor/gopkg.in/ini.v1/ini.go | 176 - vendor/gopkg.in/ini.v1/key.go | 829 -- vendor/gopkg.in/ini.v1/parser.go | 535 -- vendor/gopkg.in/ini.v1/section.go | 256 - vendor/gopkg.in/ini.v1/struct.go | 747 -- vendor/gopkg.in/yaml.v2/.travis.yml | 17 - vendor/gopkg.in/yaml.v2/LICENSE | 201 - vendor/gopkg.in/yaml.v2/LICENSE.libyaml | 31 - vendor/gopkg.in/yaml.v2/NOTICE | 13 - vendor/gopkg.in/yaml.v2/README.md | 133 - vendor/gopkg.in/yaml.v2/apic.go | 744 -- vendor/gopkg.in/yaml.v2/decode.go | 815 -- vendor/gopkg.in/yaml.v2/emitterc.go | 1685 ---- vendor/gopkg.in/yaml.v2/encode.go | 390 - vendor/gopkg.in/yaml.v2/go.mod | 5 - vendor/gopkg.in/yaml.v2/parserc.go | 1095 --- vendor/gopkg.in/yaml.v2/readerc.go | 412 - vendor/gopkg.in/yaml.v2/resolve.go | 258 - vendor/gopkg.in/yaml.v2/scannerc.go | 2711 ------ vendor/gopkg.in/yaml.v2/sorter.go | 113 - vendor/gopkg.in/yaml.v2/writerc.go | 26 - vendor/gopkg.in/yaml.v2/yaml.go | 478 - vendor/gopkg.in/yaml.v2/yamlh.go | 739 -- vendor/gopkg.in/yaml.v2/yamlprivateh.go | 173 - vendor/modules.txt | 112 - 980 files changed, 21143 insertions(+), 320436 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/coverage.yml create mode 100644 .github/workflows/notify-issue.yml delete mode 100644 .github/workflows/notify-release.yml create mode 100644 .github/workflows/releaser.yml create mode 100644 .golangci.yaml delete mode 100644 .travis.yml rename vendor/github.com/inconshreveable/mousetrap/LICENSE => NOTICE (85%) delete mode 100644 cmd/account/account_test.go delete mode 100644 cmd/applications/applications_test.go create mode 100644 cmd/applications/printer.go delete mode 100644 cmd/backups.go create mode 100644 cmd/backups/backups.go create mode 100644 cmd/backups/printer.go delete mode 100644 cmd/bareMetal.go delete mode 100644 cmd/bareMetalApp.go delete mode 100644 cmd/bareMetalOS.go delete mode 100644 cmd/bareMetalUserData.go create mode 100644 cmd/baremetal/baremetal.go create mode 100644 cmd/baremetal/printer.go create mode 100644 cmd/billing/billing.go create mode 100644 cmd/billing/printer.go delete mode 100644 cmd/blockStorage.go create mode 100644 cmd/blockstorage/blockstorage.go create mode 100644 cmd/blockstorage/printer.go create mode 100644 cmd/containerregistry/containerregistry.go create mode 100644 cmd/containerregistry/printer.go create mode 100644 cmd/database/database.go create mode 100644 cmd/database/printer.go delete mode 100644 cmd/dns.go create mode 100644 cmd/dns/dns.go create mode 100644 cmd/dns/printer.go delete mode 100644 cmd/dnsDomain.go delete mode 100644 cmd/dnsRecord.go delete mode 100644 cmd/firewall.go create mode 100644 cmd/firewall/firewall.go create mode 100644 cmd/firewall/printer.go delete mode 100644 cmd/firewallGroup.go delete mode 100644 cmd/firewallRule.go delete mode 100644 cmd/instance.go create mode 100644 cmd/instance/instance.go create mode 100644 cmd/instance/printer.go create mode 100644 cmd/ip/printer.go delete mode 100644 cmd/iso.go create mode 100644 cmd/iso/iso.go create mode 100644 cmd/iso/printer.go create mode 100644 cmd/kubernetes/kubernetes.go create mode 100644 cmd/kubernetes/printer.go delete mode 100644 cmd/loadBalancer.go create mode 100644 cmd/loadbalancer/loadbalancer.go create mode 100644 cmd/loadbalancer/printer.go create mode 100644 cmd/marketplace/marketplace.go create mode 100644 cmd/marketplace/printer.go delete mode 100644 cmd/network.go delete mode 100644 cmd/objectStorage.go create mode 100644 cmd/objectstorage/objectstorage.go create mode 100644 cmd/objectstorage/printer.go delete mode 100644 cmd/operatingSystems/operatingSystems.go delete mode 100644 cmd/operatingSystems/operatingSystems_test.go create mode 100644 cmd/operatingsystems/operatingsystems.go create mode 100644 cmd/operatingsystems/printer.go delete mode 100644 cmd/plans/plans_test.go create mode 100644 cmd/plans/printer.go delete mode 100644 cmd/printer/application.go delete mode 100644 cmd/printer/backup.go delete mode 100644 cmd/printer/bareMetal.go delete mode 100644 cmd/printer/blockStorage.go delete mode 100644 cmd/printer/dnsDomain.go delete mode 100644 cmd/printer/dnsRecord.go delete mode 100644 cmd/printer/firewallGroup.go delete mode 100644 cmd/printer/firewallRule.go create mode 100644 cmd/printer/info.go delete mode 100644 cmd/printer/instance.go delete mode 100644 cmd/printer/iso.go delete mode 100644 cmd/printer/loadBalancer.go delete mode 100644 cmd/printer/network.go delete mode 100644 cmd/printer/objectStorage.go delete mode 100644 cmd/printer/os.go delete mode 100644 cmd/printer/plans.go delete mode 100644 cmd/printer/regions.go delete mode 100644 cmd/printer/reservedIP.go delete mode 100644 cmd/printer/script.go delete mode 100644 cmd/printer/snapshot.go delete mode 100644 cmd/printer/user.go delete mode 100644 cmd/printer/userData.go delete mode 100644 cmd/printer/version.go create mode 100644 cmd/regions/printer.go delete mode 100644 cmd/regions/regions_test.go delete mode 100644 cmd/reservedIP.go create mode 100644 cmd/reservedip/printer.go create mode 100644 cmd/reservedip/reservedip.go delete mode 100644 cmd/script.go create mode 100644 cmd/script/printer.go create mode 100644 cmd/script/script.go delete mode 100644 cmd/snapshot.go create mode 100644 cmd/snapshot/printer.go create mode 100644 cmd/snapshot/snapshot.go delete mode 100644 cmd/sshkeys/sshKeys_test.go create mode 100644 cmd/userdata/printer.go create mode 100644 cmd/users/printer.go delete mode 100644 cmd/users/users_test.go create mode 100644 cmd/utils/errors.go create mode 100644 cmd/utils/utils.go create mode 100644 cmd/version/printer.go delete mode 100644 cmd/version/version_test.go create mode 100644 cmd/vpc/printer.go create mode 100644 cmd/vpc/vpc.go create mode 100644 cmd/vpc2/printer.go create mode 100644 cmd/vpc2/vpc2.go create mode 100755 scripts/entrypoint.sh delete mode 100644 vendor/github.com/fsnotify/fsnotify/.editorconfig delete mode 100644 vendor/github.com/fsnotify/fsnotify/.gitignore delete mode 100644 vendor/github.com/fsnotify/fsnotify/.travis.yml delete mode 100644 vendor/github.com/fsnotify/fsnotify/AUTHORS delete mode 100644 vendor/github.com/fsnotify/fsnotify/CHANGELOG.md delete mode 100644 vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md delete mode 100644 vendor/github.com/fsnotify/fsnotify/LICENSE delete mode 100644 vendor/github.com/fsnotify/fsnotify/README.md delete mode 100644 vendor/github.com/fsnotify/fsnotify/fen.go delete mode 100644 vendor/github.com/fsnotify/fsnotify/fsnotify.go delete mode 100644 vendor/github.com/fsnotify/fsnotify/inotify.go delete mode 100644 vendor/github.com/fsnotify/fsnotify/inotify_poller.go delete mode 100644 vendor/github.com/fsnotify/fsnotify/kqueue.go delete mode 100644 vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go delete mode 100644 vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go delete mode 100644 vendor/github.com/fsnotify/fsnotify/windows.go delete mode 100644 vendor/github.com/go-yaml/yaml/.travis.yml delete mode 100644 vendor/github.com/go-yaml/yaml/LICENSE delete mode 100644 vendor/github.com/go-yaml/yaml/LICENSE.libyaml delete mode 100644 vendor/github.com/go-yaml/yaml/NOTICE delete mode 100644 vendor/github.com/go-yaml/yaml/README.md delete mode 100644 vendor/github.com/go-yaml/yaml/apic.go delete mode 100644 vendor/github.com/go-yaml/yaml/decode.go delete mode 100644 vendor/github.com/go-yaml/yaml/emitterc.go delete mode 100644 vendor/github.com/go-yaml/yaml/encode.go delete mode 100644 vendor/github.com/go-yaml/yaml/parserc.go delete mode 100644 vendor/github.com/go-yaml/yaml/readerc.go delete mode 100644 vendor/github.com/go-yaml/yaml/resolve.go delete mode 100644 vendor/github.com/go-yaml/yaml/scannerc.go delete mode 100644 vendor/github.com/go-yaml/yaml/sorter.go delete mode 100644 vendor/github.com/go-yaml/yaml/writerc.go delete mode 100644 vendor/github.com/go-yaml/yaml/yaml.go delete mode 100644 vendor/github.com/go-yaml/yaml/yamlh.go delete mode 100644 vendor/github.com/go-yaml/yaml/yamlprivateh.go delete mode 100644 vendor/github.com/golang/protobuf/AUTHORS delete mode 100644 vendor/github.com/golang/protobuf/CONTRIBUTORS delete mode 100644 vendor/github.com/golang/protobuf/LICENSE delete mode 100644 vendor/github.com/golang/protobuf/proto/buffer.go delete mode 100644 vendor/github.com/golang/protobuf/proto/defaults.go delete mode 100644 vendor/github.com/golang/protobuf/proto/deprecated.go delete mode 100644 vendor/github.com/golang/protobuf/proto/discard.go delete mode 100644 vendor/github.com/golang/protobuf/proto/extensions.go delete mode 100644 vendor/github.com/golang/protobuf/proto/properties.go delete mode 100644 vendor/github.com/golang/protobuf/proto/proto.go delete mode 100644 vendor/github.com/golang/protobuf/proto/registry.go delete mode 100644 vendor/github.com/golang/protobuf/proto/text_decode.go delete mode 100644 vendor/github.com/golang/protobuf/proto/text_encode.go delete mode 100644 vendor/github.com/golang/protobuf/proto/wire.go delete mode 100644 vendor/github.com/golang/protobuf/proto/wrappers.go delete mode 100644 vendor/github.com/google/go-querystring/LICENSE delete mode 100644 vendor/github.com/google/go-querystring/query/encode.go delete mode 100644 vendor/github.com/hashicorp/go-cleanhttp/LICENSE delete mode 100644 vendor/github.com/hashicorp/go-cleanhttp/README.md delete mode 100644 vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go delete mode 100644 vendor/github.com/hashicorp/go-cleanhttp/doc.go delete mode 100644 vendor/github.com/hashicorp/go-cleanhttp/go.mod delete mode 100644 vendor/github.com/hashicorp/go-cleanhttp/handlers.go delete mode 100644 vendor/github.com/hashicorp/go-retryablehttp/.gitignore delete mode 100644 vendor/github.com/hashicorp/go-retryablehttp/LICENSE delete mode 100644 vendor/github.com/hashicorp/go-retryablehttp/Makefile delete mode 100644 vendor/github.com/hashicorp/go-retryablehttp/README.md delete mode 100644 vendor/github.com/hashicorp/go-retryablehttp/client.go delete mode 100644 vendor/github.com/hashicorp/go-retryablehttp/go.mod delete mode 100644 vendor/github.com/hashicorp/go-retryablehttp/go.sum delete mode 100644 vendor/github.com/hashicorp/go-retryablehttp/roundtripper.go delete mode 100644 vendor/github.com/hashicorp/hcl/.gitignore delete mode 100644 vendor/github.com/hashicorp/hcl/.travis.yml delete mode 100644 vendor/github.com/hashicorp/hcl/LICENSE delete mode 100644 vendor/github.com/hashicorp/hcl/Makefile delete mode 100644 vendor/github.com/hashicorp/hcl/README.md delete mode 100644 vendor/github.com/hashicorp/hcl/appveyor.yml delete mode 100644 vendor/github.com/hashicorp/hcl/decoder.go delete mode 100644 vendor/github.com/hashicorp/hcl/go.mod delete mode 100644 vendor/github.com/hashicorp/hcl/go.sum delete mode 100644 vendor/github.com/hashicorp/hcl/hcl.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/ast/ast.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/ast/walk.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/parser/error.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/parser/parser.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/printer/printer.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/token/position.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/token/token.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/parser/flatten.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/parser/parser.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/scanner/scanner.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/token/position.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/token/token.go delete mode 100644 vendor/github.com/hashicorp/hcl/lex.go delete mode 100644 vendor/github.com/hashicorp/hcl/parse.go delete mode 100644 vendor/github.com/inconshreveable/mousetrap/README.md delete mode 100644 vendor/github.com/inconshreveable/mousetrap/trap_others.go delete mode 100644 vendor/github.com/inconshreveable/mousetrap/trap_windows.go delete mode 100644 vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go delete mode 100644 vendor/github.com/magiconair/properties/.gitignore delete mode 100644 vendor/github.com/magiconair/properties/.travis.yml delete mode 100644 vendor/github.com/magiconair/properties/CHANGELOG.md delete mode 100644 vendor/github.com/magiconair/properties/LICENSE delete mode 100644 vendor/github.com/magiconair/properties/README.md delete mode 100644 vendor/github.com/magiconair/properties/decode.go delete mode 100644 vendor/github.com/magiconair/properties/doc.go delete mode 100644 vendor/github.com/magiconair/properties/go.mod delete mode 100644 vendor/github.com/magiconair/properties/integrate.go delete mode 100644 vendor/github.com/magiconair/properties/lex.go delete mode 100644 vendor/github.com/magiconair/properties/load.go delete mode 100644 vendor/github.com/magiconair/properties/parser.go delete mode 100644 vendor/github.com/magiconair/properties/properties.go delete mode 100644 vendor/github.com/magiconair/properties/rangecheck.go delete mode 100644 vendor/github.com/mitchellh/mapstructure/.travis.yml delete mode 100644 vendor/github.com/mitchellh/mapstructure/CHANGELOG.md delete mode 100644 vendor/github.com/mitchellh/mapstructure/LICENSE delete mode 100644 vendor/github.com/mitchellh/mapstructure/README.md delete mode 100644 vendor/github.com/mitchellh/mapstructure/decode_hooks.go delete mode 100644 vendor/github.com/mitchellh/mapstructure/error.go delete mode 100644 vendor/github.com/mitchellh/mapstructure/go.mod delete mode 100644 vendor/github.com/mitchellh/mapstructure/mapstructure.go delete mode 100644 vendor/github.com/pelletier/go-toml/.dockerignore delete mode 100644 vendor/github.com/pelletier/go-toml/.gitignore delete mode 100644 vendor/github.com/pelletier/go-toml/CONTRIBUTING.md delete mode 100644 vendor/github.com/pelletier/go-toml/Dockerfile delete mode 100644 vendor/github.com/pelletier/go-toml/LICENSE delete mode 100644 vendor/github.com/pelletier/go-toml/Makefile delete mode 100644 vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md delete mode 100644 vendor/github.com/pelletier/go-toml/README.md delete mode 100644 vendor/github.com/pelletier/go-toml/azure-pipelines.yml delete mode 100644 vendor/github.com/pelletier/go-toml/benchmark.sh delete mode 100644 vendor/github.com/pelletier/go-toml/doc.go delete mode 100644 vendor/github.com/pelletier/go-toml/example-crlf.toml delete mode 100644 vendor/github.com/pelletier/go-toml/example.toml delete mode 100644 vendor/github.com/pelletier/go-toml/fuzz.go delete mode 100644 vendor/github.com/pelletier/go-toml/fuzz.sh delete mode 100644 vendor/github.com/pelletier/go-toml/fuzzit.sh delete mode 100644 vendor/github.com/pelletier/go-toml/go.mod delete mode 100644 vendor/github.com/pelletier/go-toml/go.sum delete mode 100644 vendor/github.com/pelletier/go-toml/keysparsing.go delete mode 100644 vendor/github.com/pelletier/go-toml/lexer.go delete mode 100644 vendor/github.com/pelletier/go-toml/localtime.go delete mode 100644 vendor/github.com/pelletier/go-toml/marshal.go delete mode 100644 vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml delete mode 100644 vendor/github.com/pelletier/go-toml/marshal_test.toml delete mode 100644 vendor/github.com/pelletier/go-toml/parser.go delete mode 100644 vendor/github.com/pelletier/go-toml/position.go delete mode 100644 vendor/github.com/pelletier/go-toml/token.go delete mode 100644 vendor/github.com/pelletier/go-toml/toml.go delete mode 100644 vendor/github.com/pelletier/go-toml/tomltree_create.go delete mode 100644 vendor/github.com/pelletier/go-toml/tomltree_write.go delete mode 100644 vendor/github.com/spf13/afero/.gitignore delete mode 100644 vendor/github.com/spf13/afero/.travis.yml delete mode 100644 vendor/github.com/spf13/afero/LICENSE.txt delete mode 100644 vendor/github.com/spf13/afero/README.md delete mode 100644 vendor/github.com/spf13/afero/afero.go delete mode 100644 vendor/github.com/spf13/afero/appveyor.yml delete mode 100644 vendor/github.com/spf13/afero/basepath.go delete mode 100644 vendor/github.com/spf13/afero/cacheOnReadFs.go delete mode 100644 vendor/github.com/spf13/afero/const_bsds.go delete mode 100644 vendor/github.com/spf13/afero/const_win_unix.go delete mode 100644 vendor/github.com/spf13/afero/copyOnWriteFs.go delete mode 100644 vendor/github.com/spf13/afero/go.mod delete mode 100644 vendor/github.com/spf13/afero/go.sum delete mode 100644 vendor/github.com/spf13/afero/httpFs.go delete mode 100644 vendor/github.com/spf13/afero/ioutil.go delete mode 100644 vendor/github.com/spf13/afero/lstater.go delete mode 100644 vendor/github.com/spf13/afero/match.go delete mode 100644 vendor/github.com/spf13/afero/mem/dir.go delete mode 100644 vendor/github.com/spf13/afero/mem/dirmap.go delete mode 100644 vendor/github.com/spf13/afero/mem/file.go delete mode 100644 vendor/github.com/spf13/afero/memmap.go delete mode 100644 vendor/github.com/spf13/afero/os.go delete mode 100644 vendor/github.com/spf13/afero/path.go delete mode 100644 vendor/github.com/spf13/afero/readonlyfs.go delete mode 100644 vendor/github.com/spf13/afero/regexpfs.go delete mode 100644 vendor/github.com/spf13/afero/symlink.go delete mode 100644 vendor/github.com/spf13/afero/unionFile.go delete mode 100644 vendor/github.com/spf13/afero/util.go delete mode 100644 vendor/github.com/spf13/cast/.gitignore delete mode 100644 vendor/github.com/spf13/cast/.travis.yml delete mode 100644 vendor/github.com/spf13/cast/LICENSE delete mode 100644 vendor/github.com/spf13/cast/Makefile delete mode 100644 vendor/github.com/spf13/cast/README.md delete mode 100644 vendor/github.com/spf13/cast/cast.go delete mode 100644 vendor/github.com/spf13/cast/caste.go delete mode 100644 vendor/github.com/spf13/cast/go.mod delete mode 100644 vendor/github.com/spf13/cast/go.sum delete mode 100644 vendor/github.com/spf13/cobra/.gitignore delete mode 100644 vendor/github.com/spf13/cobra/.golangci.yml delete mode 100644 vendor/github.com/spf13/cobra/.mailmap delete mode 100644 vendor/github.com/spf13/cobra/.travis.yml delete mode 100644 vendor/github.com/spf13/cobra/CHANGELOG.md delete mode 100644 vendor/github.com/spf13/cobra/CONDUCT.md delete mode 100644 vendor/github.com/spf13/cobra/CONTRIBUTING.md delete mode 100644 vendor/github.com/spf13/cobra/LICENSE.txt delete mode 100644 vendor/github.com/spf13/cobra/Makefile delete mode 100644 vendor/github.com/spf13/cobra/README.md delete mode 100644 vendor/github.com/spf13/cobra/args.go delete mode 100644 vendor/github.com/spf13/cobra/bash_completions.go delete mode 100644 vendor/github.com/spf13/cobra/bash_completions.md delete mode 100644 vendor/github.com/spf13/cobra/cobra.go delete mode 100644 vendor/github.com/spf13/cobra/command.go delete mode 100644 vendor/github.com/spf13/cobra/command_notwin.go delete mode 100644 vendor/github.com/spf13/cobra/command_win.go delete mode 100644 vendor/github.com/spf13/cobra/custom_completions.go delete mode 100644 vendor/github.com/spf13/cobra/fish_completions.go delete mode 100644 vendor/github.com/spf13/cobra/fish_completions.md delete mode 100644 vendor/github.com/spf13/cobra/go.mod delete mode 100644 vendor/github.com/spf13/cobra/go.sum delete mode 100644 vendor/github.com/spf13/cobra/powershell_completions.go delete mode 100644 vendor/github.com/spf13/cobra/powershell_completions.md delete mode 100644 vendor/github.com/spf13/cobra/projects_using_cobra.md delete mode 100644 vendor/github.com/spf13/cobra/shell_completions.go delete mode 100644 vendor/github.com/spf13/cobra/shell_completions.md delete mode 100644 vendor/github.com/spf13/cobra/zsh_completions.go delete mode 100644 vendor/github.com/spf13/cobra/zsh_completions.md delete mode 100644 vendor/github.com/spf13/jwalterweatherman/.gitignore delete mode 100644 vendor/github.com/spf13/jwalterweatherman/LICENSE delete mode 100644 vendor/github.com/spf13/jwalterweatherman/README.md delete mode 100644 vendor/github.com/spf13/jwalterweatherman/default_notepad.go delete mode 100644 vendor/github.com/spf13/jwalterweatherman/go.mod delete mode 100644 vendor/github.com/spf13/jwalterweatherman/log_counter.go delete mode 100644 vendor/github.com/spf13/jwalterweatherman/notepad.go delete mode 100644 vendor/github.com/spf13/pflag/.gitignore delete mode 100644 vendor/github.com/spf13/pflag/.travis.yml delete mode 100644 vendor/github.com/spf13/pflag/LICENSE delete mode 100644 vendor/github.com/spf13/pflag/README.md delete mode 100644 vendor/github.com/spf13/pflag/bool.go delete mode 100644 vendor/github.com/spf13/pflag/bool_slice.go delete mode 100644 vendor/github.com/spf13/pflag/bytes.go delete mode 100644 vendor/github.com/spf13/pflag/count.go delete mode 100644 vendor/github.com/spf13/pflag/duration.go delete mode 100644 vendor/github.com/spf13/pflag/duration_slice.go delete mode 100644 vendor/github.com/spf13/pflag/flag.go delete mode 100644 vendor/github.com/spf13/pflag/float32.go delete mode 100644 vendor/github.com/spf13/pflag/float32_slice.go delete mode 100644 vendor/github.com/spf13/pflag/float64.go delete mode 100644 vendor/github.com/spf13/pflag/float64_slice.go delete mode 100644 vendor/github.com/spf13/pflag/go.mod delete mode 100644 vendor/github.com/spf13/pflag/go.sum delete mode 100644 vendor/github.com/spf13/pflag/golangflag.go delete mode 100644 vendor/github.com/spf13/pflag/int.go delete mode 100644 vendor/github.com/spf13/pflag/int16.go delete mode 100644 vendor/github.com/spf13/pflag/int32.go delete mode 100644 vendor/github.com/spf13/pflag/int32_slice.go delete mode 100644 vendor/github.com/spf13/pflag/int64.go delete mode 100644 vendor/github.com/spf13/pflag/int64_slice.go delete mode 100644 vendor/github.com/spf13/pflag/int8.go delete mode 100644 vendor/github.com/spf13/pflag/int_slice.go delete mode 100644 vendor/github.com/spf13/pflag/ip.go delete mode 100644 vendor/github.com/spf13/pflag/ip_slice.go delete mode 100644 vendor/github.com/spf13/pflag/ipmask.go delete mode 100644 vendor/github.com/spf13/pflag/ipnet.go delete mode 100644 vendor/github.com/spf13/pflag/string.go delete mode 100644 vendor/github.com/spf13/pflag/string_array.go delete mode 100644 vendor/github.com/spf13/pflag/string_slice.go delete mode 100644 vendor/github.com/spf13/pflag/string_to_int.go delete mode 100644 vendor/github.com/spf13/pflag/string_to_int64.go delete mode 100644 vendor/github.com/spf13/pflag/string_to_string.go delete mode 100644 vendor/github.com/spf13/pflag/uint.go delete mode 100644 vendor/github.com/spf13/pflag/uint16.go delete mode 100644 vendor/github.com/spf13/pflag/uint32.go delete mode 100644 vendor/github.com/spf13/pflag/uint64.go delete mode 100644 vendor/github.com/spf13/pflag/uint8.go delete mode 100644 vendor/github.com/spf13/pflag/uint_slice.go delete mode 100644 vendor/github.com/spf13/viper/.editorconfig delete mode 100644 vendor/github.com/spf13/viper/.gitignore delete mode 100644 vendor/github.com/spf13/viper/.golangci.yml delete mode 100644 vendor/github.com/spf13/viper/LICENSE delete mode 100644 vendor/github.com/spf13/viper/Makefile delete mode 100644 vendor/github.com/spf13/viper/README.md delete mode 100644 vendor/github.com/spf13/viper/flags.go delete mode 100644 vendor/github.com/spf13/viper/go.mod delete mode 100644 vendor/github.com/spf13/viper/go.sum delete mode 100644 vendor/github.com/spf13/viper/util.go delete mode 100644 vendor/github.com/spf13/viper/viper.go delete mode 100644 vendor/github.com/subosito/gotenv/.env delete mode 100644 vendor/github.com/subosito/gotenv/.env.invalid delete mode 100644 vendor/github.com/subosito/gotenv/.gitignore delete mode 100644 vendor/github.com/subosito/gotenv/.travis.yml delete mode 100644 vendor/github.com/subosito/gotenv/CHANGELOG.md delete mode 100644 vendor/github.com/subosito/gotenv/LICENSE delete mode 100644 vendor/github.com/subosito/gotenv/README.md delete mode 100644 vendor/github.com/subosito/gotenv/appveyor.yml delete mode 100644 vendor/github.com/subosito/gotenv/gotenv.go delete mode 100644 vendor/github.com/vultr/govultr/v2/.codecov.yml delete mode 100644 vendor/github.com/vultr/govultr/v2/.gitignore delete mode 100644 vendor/github.com/vultr/govultr/v2/.travis.yml delete mode 100644 vendor/github.com/vultr/govultr/v2/CHANGELOG.md delete mode 100644 vendor/github.com/vultr/govultr/v2/CONTRIBUTING.md delete mode 100644 vendor/github.com/vultr/govultr/v2/LICENSE delete mode 100644 vendor/github.com/vultr/govultr/v2/README.md delete mode 100644 vendor/github.com/vultr/govultr/v2/account.go delete mode 100644 vendor/github.com/vultr/govultr/v2/application.go delete mode 100644 vendor/github.com/vultr/govultr/v2/backup.go delete mode 100644 vendor/github.com/vultr/govultr/v2/bare_metal_server.go delete mode 100644 vendor/github.com/vultr/govultr/v2/block_storage.go delete mode 100644 vendor/github.com/vultr/govultr/v2/domain_records.go delete mode 100644 vendor/github.com/vultr/govultr/v2/domains.go delete mode 100644 vendor/github.com/vultr/govultr/v2/firewall_group.go delete mode 100644 vendor/github.com/vultr/govultr/v2/firewall_rule.go delete mode 100644 vendor/github.com/vultr/govultr/v2/go.mod delete mode 100644 vendor/github.com/vultr/govultr/v2/go.sum delete mode 100644 vendor/github.com/vultr/govultr/v2/govultr.go delete mode 100644 vendor/github.com/vultr/govultr/v2/instance.go delete mode 100644 vendor/github.com/vultr/govultr/v2/ip.go delete mode 100644 vendor/github.com/vultr/govultr/v2/iso.go delete mode 100644 vendor/github.com/vultr/govultr/v2/listOptions.go delete mode 100644 vendor/github.com/vultr/govultr/v2/load_balancer.go delete mode 100644 vendor/github.com/vultr/govultr/v2/meta.go delete mode 100644 vendor/github.com/vultr/govultr/v2/network.go delete mode 100644 vendor/github.com/vultr/govultr/v2/object_storage.go delete mode 100644 vendor/github.com/vultr/govultr/v2/os.go delete mode 100644 vendor/github.com/vultr/govultr/v2/plans.go delete mode 100644 vendor/github.com/vultr/govultr/v2/regions.go delete mode 100644 vendor/github.com/vultr/govultr/v2/reserved_ip.go delete mode 100644 vendor/github.com/vultr/govultr/v2/snapshot.go delete mode 100644 vendor/github.com/vultr/govultr/v2/ssh_key.go delete mode 100644 vendor/github.com/vultr/govultr/v2/startup_script.go delete mode 100644 vendor/github.com/vultr/govultr/v2/user.go delete mode 100644 vendor/golang.org/x/net/AUTHORS delete mode 100644 vendor/golang.org/x/net/CONTRIBUTORS delete mode 100644 vendor/golang.org/x/net/LICENSE delete mode 100644 vendor/golang.org/x/net/PATENTS delete mode 100644 vendor/golang.org/x/net/context/context.go delete mode 100644 vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go delete mode 100644 vendor/golang.org/x/net/context/go17.go delete mode 100644 vendor/golang.org/x/net/context/go19.go delete mode 100644 vendor/golang.org/x/net/context/pre_go17.go delete mode 100644 vendor/golang.org/x/net/context/pre_go19.go delete mode 100644 vendor/golang.org/x/oauth2/.travis.yml delete mode 100644 vendor/golang.org/x/oauth2/AUTHORS delete mode 100644 vendor/golang.org/x/oauth2/CONTRIBUTING.md delete mode 100644 vendor/golang.org/x/oauth2/CONTRIBUTORS delete mode 100644 vendor/golang.org/x/oauth2/LICENSE delete mode 100644 vendor/golang.org/x/oauth2/README.md delete mode 100644 vendor/golang.org/x/oauth2/go.mod delete mode 100644 vendor/golang.org/x/oauth2/go.sum delete mode 100644 vendor/golang.org/x/oauth2/internal/client_appengine.go delete mode 100644 vendor/golang.org/x/oauth2/internal/doc.go delete mode 100644 vendor/golang.org/x/oauth2/internal/oauth2.go delete mode 100644 vendor/golang.org/x/oauth2/internal/token.go delete mode 100644 vendor/golang.org/x/oauth2/internal/transport.go delete mode 100644 vendor/golang.org/x/oauth2/oauth2.go delete mode 100644 vendor/golang.org/x/oauth2/token.go delete mode 100644 vendor/golang.org/x/oauth2/transport.go delete mode 100644 vendor/golang.org/x/sys/AUTHORS delete mode 100644 vendor/golang.org/x/sys/CONTRIBUTORS delete mode 100644 vendor/golang.org/x/sys/LICENSE delete mode 100644 vendor/golang.org/x/sys/PATENTS delete mode 100644 vendor/golang.org/x/sys/internal/unsafeheader/unsafeheader.go delete mode 100644 vendor/golang.org/x/sys/unix/.gitignore delete mode 100644 vendor/golang.org/x/sys/unix/README.md delete mode 100644 vendor/golang.org/x/sys/unix/affinity_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/aliases.go delete mode 100644 vendor/golang.org/x/sys/unix/asm_aix_ppc64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_darwin_386.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_darwin_amd64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_darwin_arm.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_darwin_arm64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_freebsd_386.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_freebsd_amd64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_freebsd_arm.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_freebsd_arm64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_386.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_amd64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_arm.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_arm64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_mips64x.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_mipsx.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_riscv64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_linux_s390x.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_netbsd_386.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_netbsd_amd64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_netbsd_arm.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_netbsd_arm64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_openbsd_386.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_openbsd_amd64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_openbsd_arm.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_openbsd_arm64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s delete mode 100644 vendor/golang.org/x/sys/unix/asm_solaris_amd64.s delete mode 100644 vendor/golang.org/x/sys/unix/bluetooth_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/cap_freebsd.go delete mode 100644 vendor/golang.org/x/sys/unix/constants.go delete mode 100644 vendor/golang.org/x/sys/unix/dev_aix_ppc.go delete mode 100644 vendor/golang.org/x/sys/unix/dev_aix_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/dev_darwin.go delete mode 100644 vendor/golang.org/x/sys/unix/dev_dragonfly.go delete mode 100644 vendor/golang.org/x/sys/unix/dev_freebsd.go delete mode 100644 vendor/golang.org/x/sys/unix/dev_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/dev_netbsd.go delete mode 100644 vendor/golang.org/x/sys/unix/dev_openbsd.go delete mode 100644 vendor/golang.org/x/sys/unix/dirent.go delete mode 100644 vendor/golang.org/x/sys/unix/endian_big.go delete mode 100644 vendor/golang.org/x/sys/unix/endian_little.go delete mode 100644 vendor/golang.org/x/sys/unix/env_unix.go delete mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/fcntl.go delete mode 100644 vendor/golang.org/x/sys/unix/fcntl_darwin.go delete mode 100644 vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go delete mode 100644 vendor/golang.org/x/sys/unix/fdset.go delete mode 100644 vendor/golang.org/x/sys/unix/gccgo.go delete mode 100644 vendor/golang.org/x/sys/unix/gccgo_c.c delete mode 100644 vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/ioctl.go delete mode 100644 vendor/golang.org/x/sys/unix/mkall.sh delete mode 100644 vendor/golang.org/x/sys/unix/mkerrors.sh delete mode 100644 vendor/golang.org/x/sys/unix/pagesize_unix.go delete mode 100644 vendor/golang.org/x/sys/unix/pledge_openbsd.go delete mode 100644 vendor/golang.org/x/sys/unix/ptrace_darwin.go delete mode 100644 vendor/golang.org/x/sys/unix/ptrace_ios.go delete mode 100644 vendor/golang.org/x/sys/unix/race.go delete mode 100644 vendor/golang.org/x/sys/unix/race0.go delete mode 100644 vendor/golang.org/x/sys/unix/readdirent_getdents.go delete mode 100644 vendor/golang.org/x/sys/unix/readdirent_getdirentries.go delete mode 100644 vendor/golang.org/x/sys/unix/sockcmsg_dragonfly.go delete mode 100644 vendor/golang.org/x/sys/unix/sockcmsg_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/sockcmsg_unix.go delete mode 100644 vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go delete mode 100644 vendor/golang.org/x/sys/unix/str.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_aix.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_aix_ppc.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_bsd.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin.1_12.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin.1_13.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_386.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_dragonfly.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_illumos.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_386.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gc.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_s390x.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_openbsd_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_solaris.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_unix.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_unix_gc.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go delete mode 100644 vendor/golang.org/x/sys/unix/timestruct.go delete mode 100644 vendor/golang.org/x/sys/unix/unveil_openbsd.go delete mode 100644 vendor/golang.org/x/sys/unix/xattr_bsd.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_darwin_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_darwin_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_mips.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/zptrace_linux_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/zptrace_x86_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_386.1_13.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_386.1_13.s delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_386.s delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.s delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.1_13.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.1_13.s delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.s delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.s delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_darwin_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_darwin_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_darwin_386.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_386.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_mips.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go delete mode 100644 vendor/golang.org/x/text/AUTHORS delete mode 100644 vendor/golang.org/x/text/CONTRIBUTORS delete mode 100644 vendor/golang.org/x/text/LICENSE delete mode 100644 vendor/golang.org/x/text/PATENTS delete mode 100644 vendor/golang.org/x/text/transform/transform.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/composition.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/forminfo.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/input.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/iter.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/normalize.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/readwriter.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/tables10.0.0.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/tables11.0.0.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/tables12.0.0.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/tables13.0.0.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/tables9.0.0.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/transform.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/trie.go delete mode 100644 vendor/google.golang.org/appengine/LICENSE delete mode 100644 vendor/google.golang.org/appengine/internal/api.go delete mode 100644 vendor/google.golang.org/appengine/internal/api_classic.go delete mode 100644 vendor/google.golang.org/appengine/internal/api_common.go delete mode 100644 vendor/google.golang.org/appengine/internal/app_id.go delete mode 100644 vendor/google.golang.org/appengine/internal/base/api_base.pb.go delete mode 100644 vendor/google.golang.org/appengine/internal/base/api_base.proto delete mode 100644 vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go delete mode 100644 vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto delete mode 100644 vendor/google.golang.org/appengine/internal/identity.go delete mode 100644 vendor/google.golang.org/appengine/internal/identity_classic.go delete mode 100644 vendor/google.golang.org/appengine/internal/identity_flex.go delete mode 100644 vendor/google.golang.org/appengine/internal/identity_vm.go delete mode 100644 vendor/google.golang.org/appengine/internal/internal.go delete mode 100644 vendor/google.golang.org/appengine/internal/log/log_service.pb.go delete mode 100644 vendor/google.golang.org/appengine/internal/log/log_service.proto delete mode 100644 vendor/google.golang.org/appengine/internal/main.go delete mode 100644 vendor/google.golang.org/appengine/internal/main_common.go delete mode 100644 vendor/google.golang.org/appengine/internal/main_vm.go delete mode 100644 vendor/google.golang.org/appengine/internal/metadata.go delete mode 100644 vendor/google.golang.org/appengine/internal/net.go delete mode 100644 vendor/google.golang.org/appengine/internal/regen.sh delete mode 100644 vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go delete mode 100644 vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto delete mode 100644 vendor/google.golang.org/appengine/internal/transaction.go delete mode 100644 vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go delete mode 100644 vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto delete mode 100644 vendor/google.golang.org/appengine/urlfetch/urlfetch.go delete mode 100644 vendor/google.golang.org/protobuf/AUTHORS delete mode 100644 vendor/google.golang.org/protobuf/CONTRIBUTORS delete mode 100644 vendor/google.golang.org/protobuf/LICENSE delete mode 100644 vendor/google.golang.org/protobuf/PATENTS delete mode 100644 vendor/google.golang.org/protobuf/encoding/prototext/decode.go delete mode 100644 vendor/google.golang.org/protobuf/encoding/prototext/doc.go delete mode 100644 vendor/google.golang.org/protobuf/encoding/prototext/encode.go delete mode 100644 vendor/google.golang.org/protobuf/encoding/protowire/wire.go delete mode 100644 vendor/google.golang.org/protobuf/internal/descfmt/stringer.go delete mode 100644 vendor/google.golang.org/protobuf/internal/descopts/options.go delete mode 100644 vendor/google.golang.org/protobuf/internal/detrand/rand.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/defval/default.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/messageset/messageset.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/text/decode.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/text/decode_number.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/text/decode_string.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/text/decode_token.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/text/doc.go delete mode 100644 vendor/google.golang.org/protobuf/internal/encoding/text/encode.go delete mode 100644 vendor/google.golang.org/protobuf/internal/errors/errors.go delete mode 100644 vendor/google.golang.org/protobuf/internal/errors/is_go112.go delete mode 100644 vendor/google.golang.org/protobuf/internal/errors/is_go113.go delete mode 100644 vendor/google.golang.org/protobuf/internal/fieldsort/fieldsort.go delete mode 100644 vendor/google.golang.org/protobuf/internal/filedesc/build.go delete mode 100644 vendor/google.golang.org/protobuf/internal/filedesc/desc.go delete mode 100644 vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go delete mode 100644 vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go delete mode 100644 vendor/google.golang.org/protobuf/internal/filedesc/desc_list.go delete mode 100644 vendor/google.golang.org/protobuf/internal/filedesc/desc_list_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/filedesc/placeholder.go delete mode 100644 vendor/google.golang.org/protobuf/internal/filetype/build.go delete mode 100644 vendor/google.golang.org/protobuf/internal/flags/flags.go delete mode 100644 vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go delete mode 100644 vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/any_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/api_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/doc.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/duration_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/empty_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/field_mask_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/goname.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/map_entry.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/source_context_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/struct_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/timestamp_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/type_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/wrappers.go delete mode 100644 vendor/google.golang.org/protobuf/internal/genid/wrappers_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/api_export.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/checkinit.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_extension.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_field.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_map.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_message.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_messageset.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_tables.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/convert.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/convert_list.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/convert_map.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/decode.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/encode.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/enum.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/extension.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/legacy_enum.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/legacy_export.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/legacy_extension.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/legacy_file.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/legacy_message.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/merge.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/merge_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/message.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/message_reflect.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/message_reflect_field.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/message_reflect_gen.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/validate.go delete mode 100644 vendor/google.golang.org/protobuf/internal/impl/weak.go delete mode 100644 vendor/google.golang.org/protobuf/internal/mapsort/mapsort.go delete mode 100644 vendor/google.golang.org/protobuf/internal/pragma/pragma.go delete mode 100644 vendor/google.golang.org/protobuf/internal/set/ints.go delete mode 100644 vendor/google.golang.org/protobuf/internal/strs/strings.go delete mode 100644 vendor/google.golang.org/protobuf/internal/strs/strings_pure.go delete mode 100644 vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go delete mode 100644 vendor/google.golang.org/protobuf/internal/version/version.go delete mode 100644 vendor/google.golang.org/protobuf/proto/checkinit.go delete mode 100644 vendor/google.golang.org/protobuf/proto/decode.go delete mode 100644 vendor/google.golang.org/protobuf/proto/decode_gen.go delete mode 100644 vendor/google.golang.org/protobuf/proto/doc.go delete mode 100644 vendor/google.golang.org/protobuf/proto/encode.go delete mode 100644 vendor/google.golang.org/protobuf/proto/encode_gen.go delete mode 100644 vendor/google.golang.org/protobuf/proto/equal.go delete mode 100644 vendor/google.golang.org/protobuf/proto/extension.go delete mode 100644 vendor/google.golang.org/protobuf/proto/merge.go delete mode 100644 vendor/google.golang.org/protobuf/proto/messageset.go delete mode 100644 vendor/google.golang.org/protobuf/proto/proto.go delete mode 100644 vendor/google.golang.org/protobuf/proto/proto_methods.go delete mode 100644 vendor/google.golang.org/protobuf/proto/proto_reflect.go delete mode 100644 vendor/google.golang.org/protobuf/proto/reset.go delete mode 100644 vendor/google.golang.org/protobuf/proto/size.go delete mode 100644 vendor/google.golang.org/protobuf/proto/size_gen.go delete mode 100644 vendor/google.golang.org/protobuf/proto/wrappers.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/source.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/type.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/value.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go delete mode 100644 vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go delete mode 100644 vendor/google.golang.org/protobuf/runtime/protoiface/legacy.go delete mode 100644 vendor/google.golang.org/protobuf/runtime/protoiface/methods.go delete mode 100644 vendor/google.golang.org/protobuf/runtime/protoimpl/impl.go delete mode 100644 vendor/google.golang.org/protobuf/runtime/protoimpl/version.go delete mode 100644 vendor/gopkg.in/ini.v1/.gitignore delete mode 100644 vendor/gopkg.in/ini.v1/LICENSE delete mode 100644 vendor/gopkg.in/ini.v1/Makefile delete mode 100644 vendor/gopkg.in/ini.v1/README.md delete mode 100644 vendor/gopkg.in/ini.v1/codecov.yml delete mode 100644 vendor/gopkg.in/ini.v1/data_source.go delete mode 100644 vendor/gopkg.in/ini.v1/deprecated.go delete mode 100644 vendor/gopkg.in/ini.v1/error.go delete mode 100644 vendor/gopkg.in/ini.v1/file.go delete mode 100644 vendor/gopkg.in/ini.v1/helper.go delete mode 100644 vendor/gopkg.in/ini.v1/ini.go delete mode 100644 vendor/gopkg.in/ini.v1/key.go delete mode 100644 vendor/gopkg.in/ini.v1/parser.go delete mode 100644 vendor/gopkg.in/ini.v1/section.go delete mode 100644 vendor/gopkg.in/ini.v1/struct.go delete mode 100644 vendor/gopkg.in/yaml.v2/.travis.yml delete mode 100644 vendor/gopkg.in/yaml.v2/LICENSE delete mode 100644 vendor/gopkg.in/yaml.v2/LICENSE.libyaml delete mode 100644 vendor/gopkg.in/yaml.v2/NOTICE delete mode 100644 vendor/gopkg.in/yaml.v2/README.md delete mode 100644 vendor/gopkg.in/yaml.v2/apic.go delete mode 100644 vendor/gopkg.in/yaml.v2/decode.go delete mode 100644 vendor/gopkg.in/yaml.v2/emitterc.go delete mode 100644 vendor/gopkg.in/yaml.v2/encode.go delete mode 100644 vendor/gopkg.in/yaml.v2/go.mod delete mode 100644 vendor/gopkg.in/yaml.v2/parserc.go delete mode 100644 vendor/gopkg.in/yaml.v2/readerc.go delete mode 100644 vendor/gopkg.in/yaml.v2/resolve.go delete mode 100644 vendor/gopkg.in/yaml.v2/scannerc.go delete mode 100644 vendor/gopkg.in/yaml.v2/sorter.go delete mode 100644 vendor/gopkg.in/yaml.v2/writerc.go delete mode 100644 vendor/gopkg.in/yaml.v2/yaml.go delete mode 100644 vendor/gopkg.in/yaml.v2/yamlh.go delete mode 100644 vendor/gopkg.in/yaml.v2/yamlprivateh.go delete mode 100644 vendor/modules.txt diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..d921d0ff --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 00000000..53af0d95 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,52 @@ +name: "Unit/Coverage Tests" + +on: pull_request_target + +jobs: + coverage: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: "1.21" + + - 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" + + - name: Add Comment + uses: actions/github-script@v5 + if: always() + with: + script: | + const output = `### Unit Tests and Coverage +
Show Output + + \`\`\` + ${{ steps.results.outputs.content }} + \`\`\` +
+ + *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 + }) diff --git a/.github/workflows/gochecks.yml b/.github/workflows/gochecks.yml index 6058c4a8..a5334755 100644 --- a/.github/workflows/gochecks.yml +++ b/.github/workflows/gochecks.yml @@ -6,7 +6,7 @@ on: - master jobs: - Go-Lint: + Golangci-Lint: runs-on: ubuntu-latest steps: @@ -14,22 +14,17 @@ jobs: - name: Setup Go uses: actions/setup-go@v2 with: - go-version: '1.14.0' + go-version: "1.21" - name: Install dependencies run: | go version - go get -u golang.org/x/lint/golint + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2 - - name: Run Lint + - name: Run golangci-lint run: | - golint_files=$(golint cmd) - if [[ -n ${golint_files} ]]; then - echo 'fix the following linting errors:' - echo "${golint_files}" - exit 1 - fi - exit 0 + golangci-lint run cmd/... + Go-Fmt: runs-on: ubuntu-latest @@ -39,7 +34,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v2 with: - go-version: '1.14.0' + go-version: "1.21" - name: Run fmt run: | @@ -49,4 +44,4 @@ jobs: echo "${gofmt_files}" exit 1 fi - exit 0 \ No newline at end of file + exit 0 diff --git a/.github/workflows/notify-issue.yml b/.github/workflows/notify-issue.yml new file mode 100644 index 00000000..8b58c9c4 --- /dev/null +++ b/.github/workflows/notify-issue.yml @@ -0,0 +1,18 @@ +name: notify-issue + +on: + issues: + types: [opened] + +jobs: + issue: + runs-on: ubuntu-latest + name: New Issue Notification + steps: + - run: | + echo "{\"text\":\"Vultr-CLI : New Issue https://github.com/vultr/vultr-cli/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 }} diff --git a/.github/workflows/notify-release.yml b/.github/workflows/notify-release.yml deleted file mode 100644 index 25acd165..00000000 --- a/.github/workflows/notify-release.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: notify-release - -on: - push: - tags: v* - -jobs: - release: - runs-on: ubuntu-latest - name: Release Notification - steps: - - name: Get the version - id: get_version - run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - - run: | - echo "{\"text\":\"Vultr-CLI : Release https://github.com/vultr/vultr-cli/releases/tag/${{ steps.get_version.outputs.VERSION }} \"}" > 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 }} \ No newline at end of file diff --git a/.github/workflows/releaser.yml b/.github/workflows/releaser.yml new file mode 100644 index 00000000..012d4309 --- /dev/null +++ b/.github/workflows/releaser.yml @@ -0,0 +1,95 @@ +name: "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.21" + - + name: Docker Login + env: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + run: | + echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + distribution: goreleaser + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.VULTRBOT_TOKEN }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + - + name: Clear + if: always() + run: | + rm -f ${HOME}/.docker/config.json + + release: + runs-on: ubuntu-latest + needs: ["goreleaser", "create-tag"] + name: Release Notification + steps: + - name: Get the version + id: get_version + run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} + - run: | + echo "{\"text\":\"Vultr-CLI : 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 }} diff --git a/.gitignore b/.gitignore index b1fa8748..de9566ff 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ .vscode/ .idea builds/ -dist/ \ No newline at end of file +dist/ +vendor/ diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 00000000..49485b19 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,125 @@ +linters-settings: + dupl: + threshold: 300 + funlen: + lines: -1 # the number of lines (code + empty lines) is not a right metric and leads to code without empty line or one-liner. + statements: 50 + goconst: + min-len: 2 + min-occurrences: 3 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - ifElseChain + - octalLiteral + - whyNoLint + gocyclo: + min-complexity: 15 + goimports: + local-prefixes: github.com/golangci/golangci-lint + gomnd: + # don't include the "operation" and "assign" + checks: + - argument + - case + - condition + - return + ignored-numbers: + - '0' + - '1' + - '2' + - '3' + ignored-functions: + - strings.SplitN + + govet: + check-shadowing: true + settings: + printf: + funcs: + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + lll: + line-length: 140 + misspell: + locale: US + nolintlint: + allow-unused: false # report any unused nolint directives + require-explanation: false # don't require an explanation for nolint directives + require-specific: false # don't require nolint directives to be specific about which linter is being skipped + revive: + rules: + - name: unexported-return + disabled: true + +linters: + disable-all: true + enable: + - dogsled + - dupl + - errcheck + - exportloopref + - funlen + - gocyclo + - gofmt + - goimports + - gomnd + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - lll + - misspell + - nakedret + - noctx + - nolintlint + - revive + - staticcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - whitespace + + # don't enable: + # - bodyclose + # - depguard + # - asciicheck + # - scopelint + # - gochecknoglobals + # - gocognit + # - gocritic + # - godot + # - godox + # - goerr113 + # - interfacer + # - maligned + # - nestif + # - prealloc + # - testpackage + # - wsl + +issues: + # List of regexps of issue texts to exclude. + # + # But independently of this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. + # To list all excluded by default patterns execute `golangci-lint run --help` + # + # Default: https://golangci-lint.run/usage/false-positives/#default-exclusions + exclude: + - abcdef + - ST1023 + exclude-rules: +run: + timeout: 5m diff --git a/.goreleaser.yml b/.goreleaser.yml index bbaba413..c3502484 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,7 +1,3 @@ -before: - hooks: - - go mod download - - go generate ./... builds: - env: @@ -9,13 +5,8 @@ builds: binary: vultr-cli - asmflags: - - -D mysymbol - - all=-trimpath={{.Env.GOPATH}} - - gcflags: - - all=-trimpath={{.Env.GOPATH}} - - ./dontoptimizeme=-N + flags: + - -trimpath #removes all file system paths from the compiled executable goos: - linux @@ -25,15 +16,21 @@ builds: goarch: - amd64 - arm64 + - arm + + goarm: + - 6 + - 7 archives: - - replacements: - darwin: macOs - linux: linux - windows: windows - amd64: 64-bit - arm64: arm64-bit + name_template: >- + {{- .ProjectName }}_v + {{- .Version }}_ + {{- if eq .Os "darwin" }}macOs + {{- else }}{{ .Os }}{{ end }}_ + {{- if eq .Arch "arm" }}arm32-v{{ .Arm }} + {{- else }}{{ .Arch }}{{ end }} format: tar.gz @@ -44,7 +41,6 @@ archives: - goos: windows format: zip - checksum: name_template: "{{ .ProjectName }}_v{{ .Version }}_checksums.txt" algorithm: sha256 @@ -63,11 +59,10 @@ brews: - name: vultr-cli - tap: + repository: owner: vultr name: homebrew-vultr-cli - url_template: "https://github.com/vultr/vultr-cli/releases/download/{{ .Tag }}/{{ .ArtifactName }}" commit_author: @@ -78,8 +73,6 @@ brews: description: "Official command-line tool for Vultr services" - dependencies: - - go test: | output = shell_output("#{bin}/vultr-cli version 2>&1", 1) assert_match "Please export your VULTR API key as an environment variable or add `api-key` to your config file, eg:\nexport VULTR_API_KEY=''\n", output @@ -92,9 +85,12 @@ dockers: - dockerfile: Dockerfile.goreleaser image_templates: - "vultr/vultr-cli:release" + - "vultr/vultr-cli:latest" - "vultr/vultr-cli:{{ .Tag }}" + extra_files: + - scripts/entrypoint.sh release: github: owner: vultr - name: vultr-cli \ No newline at end of file + name: vultr-cli diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7011790d..00000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -# .travis.yml -language: go - -go: - - 1.16.x - -env: - - GO111MODULE=on - -services: - - docker - -after_success: - - docker login -u="$DOCKER_USER" -p="$DOCKER_PASSWORD" - -deploy: - - provider: script - cleanup: false - script: curl -sL https://git.io/goreleaser | bash - on: - tags: true - condition: $TRAVIS_OS_NAME = linux \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 079c61d4..f4f240b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,378 @@ # Change Log +## [v2.21.0](https://github.com/vultr/vultr-cli/compare/v2.20.0...v2.21.0) (2023-11-29) +### Enhancements +* Database: Add usage commands [PR 378](https://github.com/vultr/vultr-cli/pull/378) +* Container Registry: Implemented [PR 380](https://github.com/vultr/vultr-cli/pull/380) +* Bare Metal: Update tags display to use delimiters [PR 372](https://github.com/vultr/vultr-cli/pull/372) +* Instance: Update tags display to use delimiters [PR 372](https://github.com/vultr/vultr-cli/pull/372) +* Database: Add read replica promotion [PR 375](https://github.com/vultr/vultr-cli/pull/375) +* Kubernetes: Add kubeconfig filepath export [PR 361](https://github.com/vultr/vultr-cli/pull/361) + +### Dependencies +* Update govultr to v3.4.1 [PR 376](https://github.com/vultr/vultr-cli/pull/376) +* Bump golang.org/x/oauth2 from 0.14.0 to 0.15.0 [PR 379](https://github.com/vultr/vultr-cli/pull/379) +* Bump github.com/spf13/cobra from 1.7.0 to 1.8.0 [PR 371](https://github.com/vultr/vultr-cli/pull/371) +* Bump govultr to v3.4.0 [PR 374](https://github.com/vultr/vultr-cli/pull/374) +* Bump golang.org/x/oauth2 from 0.13.0 to 0.14.0 [PR 373](https://github.com/vultr/vultr-cli/pull/373) + +## [v2.20.0](https://github.com/vultr/vultr-cli/compare/v2.19.0...v2.20.0) 2023-11-01 +### Enhancements +* Managed Database public/private hostnames, cleanup summarize view [PR 363](https://github.com/vultr/vultr-cli/pull/363) +* Allow some commands to be run without authenticating against the API [PR 364](https://github.com/vultr/vultr-cli/pull/364) +* Add support for the VKE HA control plane option [PR 368](https://github.com/vultr/vultr-cli/pull/368) +* Add Support for DBaaS FerretDB Subscriptions [PR 369](https://github.com/vultr/vultr-cli/pull/369) + +### Bug Fixes +* Adjust DBaaS VPC pointer to detect changes [PR 366](https://github.com/vultr/vultr-cli/pull/366) + +### Dependencies +* Bump golang.org/x/net from 0.15.0 to 0.17.0 [PR 358](https://github.com/vultr/vultr-cli/pull/358) +* Update govultr to v3.3.2 [PR 362](https://github.com/vultr/vultr-cli/pull/362) +* Update govultr to v3.3.3 [PR 365](https://github.com/vultr/vultr-cli/pull/365) +* Update govultr to v3.3.4 [PR 367](https://github.com/vultr/vultr-cli/pull/367) +* Bump golang.org/x/oauth2 from 0.12.0 to 0.13.0 [PR 356](https://github.com/vultr/vultr-cli/pull/356) +* Bump github.com/spf13/viper from 1.16.0 to 1.17.0 [PR 357](https://github.com/vultr/vultr-cli/pull/357) + +## [v2.19.0](https://github.com/vultr/vultr-cli/compare/v2.18.2...v2.19.0) (2023-10-18) +### Enhancements +* Kubernetes: Add summarize list options [PR 348](https://github.com/vultr/vultr-cli/pull/348) +* Database: Add summarize list options [PR 348](https://github.com/vultr/vultr-cli/pull/348) +* Load Balancer: Add summarize list options [PR 348](https://github.com/vultr/vultr-cli/pull/348) +* Rework the printer output code [PR 355](https://github.com/vultr/vultr-cli/pull/355) + +### Documentation +* VPC2: Correct create command example [PR 350](https://github.com/vultr/vultr-cli/pull/350) + +### Bug Fixes +* Remove the useless cobra init help toggle flag [PR 349](https://github.com/vultr/vultr-cli/pull/349) + +### Dependencies +* Bump golang.org/x/oauth2 from 0.11.0 to 0.12.0 [PR 351](https://github.com/vultr/vultr-cli/pull/351) +* Update to go v1.21 [PR 347](https://github.com/vultr/vultr-cli/pull/347) + +### Automation +* Add project name back to the archive file names in goreleaser [PR 346](https://github.com/vultr/vultr-cli/pull/346) +* Add golangci-lint and fix linter errors [PR 353](https://github.com/vultr/vultr-cli/pull/353) +* Remove unnecessary Go dependency from .goreleaser [PR 97](https://github.com/vultr/vultr-cli/pull/97) + +### New Contributors +* @0az made their first contribution in [PR 97](https://github.com/vultr/vultr-cli/pull/97) +* @resmo made their first contribution in [PR 350](https://github.com/vultr/vultr-cli/pull/350) + +## [v2.18.2](https://github.com/vultr/vultr-cli/compare/v2.18.0...v2.18.2) (2023-08-24) +### Automation +* Update how archive names are generated by goreleaser [PR 342](https://github.com/vultr/vultr-cli/pull/342) +* Remove deprecated brews tap command in goreleaser [PR_344](https://github.com/vultr/vultr-cli/pull/344) + +## [v2.18.0](https://github.com/vultr/vultr-cli/compare/v2.17.0...v2.18.0) (2023-08-23) +### Enhancements +* Database: Add VPC support for DBaaS instances [PR 331](https://github.com/vultr/vultr-cli/pull/331) +* Bare Metal: Add support for VPC 2.0 [PR 335](https://github.com/vultr/vultr-cli/pull/335) +* Instance: Add support for VPC 2.0 [PR 335](https://github.com/vultr/vultr-cli/pull/335) +* Application: Add more aliases for the apps command [PR 336](https://github.com/vultr/vultr-cli/pull/336) +* VPC2: Add Nodes Endpoints [PR 339](https://github.com/vultr/vultr-cli/pull/339) +* Database: Managed Database Nesting Refactor [PR 340](https://github.com/vultr/vultr-cli/pull/340) + +### Bug Fixes +* Instance: Fix reserved IPv4 flag docs [PR 337](https://github.com/vultr/vultr-cli/pull/337) + +### Dependencies +* Update govultr to v3.3.0 [PR 334](https://github.com/vultr/vultr-cli/pull/334) +* Update govultr to v3.3.1 [PR 338](https://github.com/vultr/vultr-cli/pull/338) +* Update govultr to v3.1.0 [PR 329](https://github.com/vultr/vultr-cli/pull/329) +* Bump github.com/vultr/govultr/v3 from 3.1.0 to 3.2.0 [PR 330](https://github.com/vultr/vultr-cli/pull/330) +* Bump golang.org/x/oauth2 from 0.9.0 to 0.10.0 [PR 328](https://github.com/vultr/vultr-cli/pull/328) +* Bump golang.org/x/oauth2 from 0.10.0 to 0.11.0 [PR 333](https://github.com/vultr/vultr-cli/pull/333) + +### New Contributors +* @nhooyr made their first contribution in [PR 337](https://github.com/vultr/vultr-cli/pull/337) + +## [v2.17.0](https://github.com/vultr/vultr-cli/compare/v2.16.2...v2.17.0) (2023-06-14) +### Enhancements +* Instances: Add support for attaching and detaching VPC networks [PR 318](https://github.com/vultr/vultr-cli/pull/318) + +### Bug Fixes +* Database: Fix database update errors and remove db engine/version [PR 314](https://github.com/vultr/vultr-cli/pull/314) + +### Documentation +* README: Use a more succinct Homebrew command to tap-and-install [PR 315](https://github.com/vultr/vultr-cli/pull/315) +* README: Fix spelling [PR 324](https://github.com/vultr/vultr-cli/pull/324) +* README: Add docker install/usage instructions [PR 322](https://github.com/vultr/vultr-cli/pull/322) +* README: Mention default config yaml location [PR 325](https://github.com/vultr/vultr-cli/pull/325) + +### Dependencies +* Bump github.com/vultr/govultr/v3 from 3.0.2 to 3.0.3 [PR 320](https://github.com/vultr/vultr-cli/pull/320) +* Bump github.com/spf13/cobra from 1.6.1 to 1.7.0 [PR 310](https://github.com/vultr/vultr-cli/pull/310) +* Bump github.com/spf13/viper from 1.15.0 to 1.16.0 [PR 319](https://github.com/vultr/vultr-cli/pull/319) +* Bump golang.org/x/oauth2 from 0.6.0 to 0.7.0 [PR 312](https://github.com/vultr/vultr-cli/pull/312) +* Bump golang.org/x/oauth2 from 0.7.0 to 0.8.0 [PR 316](https://github.com/vultr/vultr-cli/pull/316) +* Bump golang.org/x/oauth2 from 0.8.0 to 0.9.0 [PR 323](https://github.com/vultr/vultr-cli/pull/323) +* Update Github workflows to go v1.20 [PR 311](https://github.com/vultr/vultr-cli/pull/311) + +### New Contributors +* @ELLIOTTCABLE made their first contribution in [PR 315](https://github.com/vultr/vultr-cli/pull/315) + +## [v2.16.2](https://github.com/vultr/vultr-cli/compare/v2.15.1...v2.16.2) (2023-03-31) +### Enhancements +* Database: Add DBaaS Support [PR 302](https://github.com/vultr/vultr-cli/pull/302) + +### Dependencies +* Update go to 1.20 [PR 303](https://github.com/vultr/vultr-cli/pull/303) +* Update govultr to v3.0.1 [PR 301](https://github.com/vultr/vultr-cli/pull/301) +* Update govultr to v3.0.2 [PR 304](https://github.com/vultr/vultr-cli/pull/304) +* Fix goreleaser configurations [PR 306](https://github.com/vultr/vultr-cli/pull/306) +* Fix github automatic release configurations [PR 308](https://github.com/vultr/vultr-cli/pull/308) + +### New Contributors +* @christhemorse made their first contribution in [PR 302](https://github.com/vultr/vultr-cli/pull/302) + +## [v2.15.1](https://github.com/vultr/vultr-cli/compare/v2.15.0...v2.15.1) (2023-03-09) +### Enhancements +* Update goreleaser to add latest docker image tag [PR 287](https://github.com/vultr/vultr-cli/pull/287) + +### Documentation +* Block Storage: Make cli param examples consistently use = [PR 291](https://github.com/vultr/vultr-cli/pull/291) +* Instances: Make cli param examples consistently use = [PR 291](https://github.com/vultr/vultr-cli/pull/291) +* Regions: Add vcg plan options to docstrings [PR 299](https://github.com/vultr/vultr-cli/pull/299) +* Plans: Add vcg plan options to docstrings [PR 299](https://github.com/vultr/vultr-cli/pull/299) + +### Dependencies +* Bump github.com/spf13/cobra from 1.5.0 to 1.6.0 by [PR 288](https://github.com/vultr/vultr-cli/pull/288) +* Bump github.com/spf13/cobra from 1.6.0 to 1.6.1 by [PR 289](https://github.com/vultr/vultr-cli/pull/289) +* Bump github.com/spf13/viper from 1.13.0 to 1.14.0 [PR 290](https://github.com/vultr/vultr-cli/pull/290) +* Bump golang.org/x/oauth2 from 0.0.0-20221014153046-6fdb5e3db783 to 0.5.0 [PR 296](https://github.com/vultr/vultr-cli/pull/296) +* Bump golang.org/x/oauth2 from 0.5.0 to 0.6.0 [PR 298](https://github.com/vultr/vultr-cli/pull/298) +* Bump github.com/spf13/viper from 1.14.0 to 1.15.0 [PR 293](https://github.com/vultr/vultr-cli/pull/293) +* Bump golang.org/x/net from 0.6.0 to 0.7.0 [PR 297](https://github.com/vultr/vultr-cli/pull/297) + +### New Contributors +* @happytreees made their first contribution in [PR 287](https://github.com/vultr/vultr-cli/pull/287) + +## [v2.15.0](https://github.com/vultr/vultr-cli/compare/v2.14.2...v2.15.0) (2022-10-04) +### Enhancements +* Add arm builds [PR 283](https://github.com/vultr/vultr-cli/pull/283) + +### Dependencies +* Bump github.com/spf13/cobra from 1.4.0 to 1.5.0 [PR 274](https://github.com/vultr/vultr-cli/pull/274) +* Bump go from 1.17 to 1.19 [PR 284]( https://github.com/vultr/vultr-cli/pull/284) + +### Documentation +* Remove extraneous dash from example command-line [PR 279](https://github.com/vultr/vultr-cli/pull/279) + +### New Contributors +* @uplime made their first contribution in [PR 279](https://github.com/vultr/vultr-cli/pull/279) +* @mondragonfx made their first contribution in [PR 284](https://github.com/vultr/vultr-cli/pull/284) + +## [v2.14.2](https://github.com/vultr/vultr-cli/compare/v2.14.1...v2.14.2) (2022-06-14) +### Enhancements +* Reserved IP: Add support for reserved IP label updates [PR 272](https://github.com/vultr/vultr-cli/pull/272) + +### Dependencies +* Bump govultr version from 2.17.1 to 2.17.2 [PR 272](https://github.com/vultr/vultr-cli/pull/272) + +## [v2.14.1](https://github.com/vultr/vultr-cli/compare/v2.14.0...v2.14.1) (2022-06-03) +### Enhancements +* Plans: Add GPU fields [PR 269](https://github.com/vultr/vultr-cli/pull/269) +* Instances: Update `tag` to string pointer [PR 268](https://github.com/vultr/vultr-cli/pull/268) +* Kuberneted: Update `tag` to string pointer [PR 268](https://github.com/vultr/vultr-cli/pull/268) + +### Dependencies +* Bump github.com/spf13/viper from 1.11.0 to 1.12.0 [PR 266](https://github.com/vultr/vultr-cli/pull/266) +* Bump govultr version from 2.16.0 to 2.17.1 [PR 267](https://github.com/vultr/vultr-cli/pull/267) + +## [v2.14.0](https://github.com/vultr/vultr-cli/compare/v2.13.0..v2.14.0) (2022-05-09) +### Enhancements +* Kubernetes : Add support for kubernetes version upgrades on individual clusters [PR 263](https://github.com/vultr/vultr-cli/pull/263) +* Kubernetes : Add support for node pool auto scaler options [PR 261](https://github.com/vultr/vultr-cli/pull/261) +* Firewall Rule : Update IP type option to match API verbiage for firewall rules [PR 262](https://github.com/vultr/vultr-cli/pull/262) +* Baremetal : Add support for multiple tags via the `tags` field [PR 259](https://github.com/vultr/vultr-cli/pull/259) +* Instances : Add support for multiple tags via the `tags` field [PR 259](https://github.com/vultr/vultr-cli/pull/259) + +### Deprecations +* Firewall Rule : The `type` option on firewall rules has been replaced by `ip-type` [PR 262](https://github.com/vultr/vultr-cli/pull/262) +* Baremetal : the `tag` field has been replaced by `tags` which supports multiple tags [PR 259](https://github.com/vultr/vultr-cli/pull/259) +* Instances : the `tag` field has been replaced by `tags` which supports multiple tags [PR 259](https://github.com/vultr/vultr-cli/pull/259) + +### Dependencies +* Bump github.com/vultr/govultr/v2 from 2.15.1 to 2.16.0 [PR 260](https://github.com/vultr/vultr-cli/pull/260) + +### Documentation +* Update BSD install instructions [PR 258](https://github.com/vultr/vultr-cli/pull/258) +* Update README and improve verbiage for snapshots [PR 257](https://github.com/vultr/vultr-cli/pull/257) + +## [v2.13.0](https://github.com/vultr/vultr-cli/compare/v2.12.2..v2.13.0) (2022-04-15) +### Enhancements +* VPC : new commands which will be replacing `network` (private networks) [PR 251](https://github.com/vultr/vultr-cli/pull/251) +* BlockStorage : adding support for new `block_type` field [PR 249](https://github.com/vultr/vultr-cli/pull/249/) +* LoadBalancer : Updating `vpc` functionality added [PR 251](https://github.com/vultr/vultr-cli/pull/251) + +### Deprecations +* Network : These commands have been replaced by `vpc` [PR 251](https://github.com/vultr/vultr-cli/pull/251) +* Instance : The following fields have been deprecated on the `create` command `private-network` and `network`. Please use `vpc-enable` or `vpc-ids` [PR 251](https://github.com/vultr/vultr-cli/pull/251) +* LoadBalancer : The following fields have been deprecated on the `create` command `private-network`. Please use `vpc` instead [PR 251](https://github.com/vultr/vultr-cli/pull/251) + +### Dependencies +* Bump github.com/vultr/govultr/v2 from 2.14.1 to 2.15.1 [PR 249](https://github.com/vultr/vultr-cli/pull/249) +* Bump github.com/spf13/viper from 1.10.1 to 1.11.0 [PR 252](https://github.com/vultr/vultr-cli/pull/252) + +### Documentation +* Add Fedora installation instructions [PR 246](https://github.com/vultr/vultr-cli/pull/246) + +## [v2.12.2](https://github.com/vultr/vultr-cli/compare/v2.12.1..v2.12.2) (2022-04-01) +### Enhancements +* Instances : fix csv flags `ssh-keys` and `network` [PR 244](https://github.com/vultr/vultr-cli/pull/244) @optik-aper +* Plans + Regions: Add new plan types in examples [PR 241](https://github.com/vultr/vultr-cli/pull/241/) @AFatalErrror +* Plans Metal : new command to retrieve just bare metal plans [PR 240](https://github.com/vultr/vultr-cli/pull/240) @optik-aper +* Readme : fix command example [PR 239](https://github.com/vultr/vultr-cli/pull/239) @travispaul + +### Dependencies +* Bump github.com/vultr/govultr/v2 from 2.14.1 to 2.14.2 [PR 238](https://github.com/vultr/vultr-cli/pull/238) +* Bump github.com/spf13/cobra from 1.3.0 to 1.4.0 [PR 236](https://github.com/vultr/vultr-cli/pull/236) +* Bump builds from go 1.16 -> 1.17 [PR 243](https://github.com/vultr/vultr-cli/pull/243) + +## [v2.12.1](https://github.com/vultr/vultr-cli/compare/v2.12.0..v2.12.1) (2022-02-07) +### Dependencies +* Bump github.com/vultr/govultr/v2 from 2.14.0 to 2.14.1 [PR 232](https://github.com/vultr/vultr-cli/pull/232) + +### Enhancements +* Firewall Rule : Add ip type, source and subnet size to firewall rule printer [PR 234](https://github.com/vultr/vultr-cli/pull/234) + +## [v2.12.0](https://github.com/vultr/vultr-cli/compare/v2.11.3..v2.12.0) (2022-01-21) +### Dependencies +* Bump github.com/vultr/govultr/v2 from 2.12.0 to 2.14.0 [PR 230](https://github.com/vultr/vultr-cli/pull/230) +* Bump github.com/spf13/viper from 1.10.0 to 1.10.1 [PR 224](https://github.com/vultr/vultr-cli/pull/224) +* Bump github.com/spf13/cobra from 1.2.1 to 1.3.0 [PR 223](https://github.com/vultr/vultr-cli/pull/223) + +### Enhancements +* Script : Return b64 script when getting script by id [PR 229](https://github.com/vultr/vultr-cli/pull/229) + +### Breaking Changes +* Script : get command will display data vertically now instead of horizontal [PR 229](https://github.com/vultr/vultr-cli/pull/229) + +### Bug Fixes +* Firewalls : change source from int to string [PR 228](https://github.com/vultr/vultr-cli/pull/228) + +## [v2.11.3](https://github.com/vultr/vultr-cli/compare/v2.11.2..v2.11.3) (2021-12-13) +### Dependencies +* Bump github.com/spf13/viper from 1.9.0 to 1.10.0 [PR 219](https://github.com/vultr/vultr-cli/pull/219) + +### Enhancements +* Add OpenBSD install instructions [PR 218](https://github.com/vultr/vultr-cli/pull/218) + +## [v2.11.2](https://github.com/vultr/vultr-cli/compare/v2.11.1..v2.11.2) (2021-12-01) +### Dependencies +* Update GoVultr from 2.11.1 to 2.12.0 [PR 215](https://github.com/vultr/vultr-cli/pull/215) + +## [v2.11.1](https://github.com/vultr/vultr-cli/compare/v2.11.0..v2.11.1) (2021-11-29) +### Dependencies +* Bump github.com/vultr/govultr/v2 from 2.11.0 to 2.11.1 [PR 213](https://github.com/vultr/vultr-cli/pull/213) + +### Bug Fixes +* Load Balancers : Allow SSL certificates to be passed in on Create and Update [PR 213](https://github.com/vultr/vultr-cli/pull/213) + +## [v2.11.0](https://github.com/vultr/vultr-cli/compare/v2.10.0..v2.11.0) (2021-11-23) +### Enhancements +* DNS: Add support for getting a domains dns sec status [PR 211](https://github.com/vultr/vultr-cli/pull/211) +* Instance : Support changing hostname on reinstall [PR 209](https://github.com/vultr/vultr-cli/pull/209) [PR 210](https://github.com/vultr/vultr-cli/pull/210) + +### Dependencies +* Update GoVultr from 2.10.0 to 2.11.0 [PR 209](https://github.com/vultr/vultr-cli/pull/209) + +## [v2.10.0](https://github.com/vultr/vultr-cli/compare/v2.9.0..v2.10.0) (2021-11-04) +### Enhancements +* Billing: Add support for retrieving billing information [PR 203](https://github.com/vultr/vultr-cli/pull/203) + +### Dependencies +* Update GoVultr from 2.9.2 to 2.10.0 [PR 203](https://github.com/vultr/vultr-cli/pull/203) + +## [v2.9.0](https://github.com/vultr/vultr-cli/compare/v2.8.5..v2.9.0) (2021-10-27) +### Bug Fixes +* Allow `go get` and `go install` to work with `github.com/vultr/vultr-cli/v2` [PR 199](https://github.com/vultr/vultr-cli/pull/199) + +## [v2.8.5](https://github.com/vultr/vultr-cli/compare/v2.8.4..v2.8.5) (2021-10-20) +### Dependencies +* Update GoVultr from 2.9.0 to 2.9.1 and update necessary fields [PR 196](https://github.com/vultr/vultr-cli/pull/196) +* Update GoVultr from 2.9.1 to 2.9.2 [PR 197](https://github.com/vultr/vultr-cli/pull/197) + +### Enhancements +* Kubernetes: Add support for adding/modifying tags on Node Pools [PR 196](https://github.com/vultr/vultr-cli/pull/196) + +## [v2.8.4](https://github.com/vultr/vultr-cli/compare/v2.8.3..v2.8.4) (2021-09-28) +### Dependencies +* Update GoVultr from 2.8.1 to 2.9.0 and update necessary fields [PR 192](https://github.com/vultr/vultr-cli/pull/192) + +### Enhancements +* Snapshots: `COMPRESSED SIZE` has been added to printer output [PR 192](https://github.com/vultr/vultr-cli/pull/192) +* Kubernetes: `COUNT` has changed to `NODE QUANTITY` and `PLAN ID` has changed to `PLAN` for kubernetes printer output [PR 192](https://github.com/vultr/vultr-cli/pull/192) + +## [v2.8.3](https://github.com/vultr/vultr-cli/compare/v2.8.2..v2.8.3) (2021-09-20) +### Dependencies +* Bump github.com/spf13/viper from 1.8.1 to 1.9.0 [PR 189](https://github.com/vultr/vultr-cli/pull/189) -## [v2.4.1](https://github.com/vultr/vultr-cli/compare/v2.4.0..v2.4.1) (2021-04-12) ### Bug Fixes -* LoadBalancers: fix update issues [PR 145](https://github.com/vultr/vultr-cli/pull/145) +* Backups: Fix typo in backups alias [PR 188](https://github.com/vultr/vultr-cli/pull/188). Thanks @rmorey for your contribution + +## [v2.8.2](https://github.com/vultr/vultr-cli/compare/v2.8.1..v2.8.2) (2021-09-07) +### Enhancements +* Instances: change default value for notify flag [PR 185](https://github.com/vultr/vultr-cli/pull/185) +* README: add example using boolean flag [PR 186](https://github.com/vultr/vultr-cli/pull/186) + +## [v2.8.1](https://github.com/vultr/vultr-cli/compare/v2.8.0..v2.8.1) (2021-09-01) +### Dependencies +* GoVultr 2.8.0 -> 2.8.1 (added more kubernetes support)[PR 181](https://github.com/vultr/vultr-cli/pull/181) + +### Enhancements +* Kubernetes: Add support for new Kubernetes calls [PR 181](https://github.com/vultr/vultr-cli/pull/181) +* Add User-Agent: [PR 182](https://github.com/vultr/vultr-cli/pull/182) + +## [v2.8.0](https://github.com/vultr/vultr-cli/compare/v2.7.0..v2.8.0) (2021-08-23) +### Dependencies +* GoVultr 2.7.1 -> 2.8.0 (added kubernetes support)[PR 177](https://github.com/vultr/vultr-cli/pull/177) + +### Enhancements +* Kubernetes: Add support for Kubernetes (VKE) [PR 178](https://github.com/vultr/vultr-cli/pull/178) +* README: update commands needed for building from source [PR 173](https://github.com/vultr/vultr-cli/pull/173) +* README: update examples [PR 174](https://github.com/vultr/vultr-cli/pull/174) + +## [v2.7.0](https://github.com/vultr/vultr-cli/compare/v2.6.0..v2.7.0) (2021-07-16) +### Dependencies +* GoVultr 2.6.0 -> 2.7.1 (added image_id support for instance and bare metal updates) [PR 169](https://github.com/vultr/vultr-cli/pull/169) + +### Enhancements +* Instances: Add image_id support [PR 169](https://github.com/vultr/vultr-cli/pull/169) +* Bare-metal: Add image_id support [PR 169](https://github.com/vultr/vultr-cli/pull/169) +* Add documentation for autocompletions in README + +## [v2.6.0](https://github.com/vultr/vultr-cli/compare/v2.5.3..v2.6.0) (2021-07-07) +### Dependencies +* Bump github.com/spf13/viper from 1.7.1 to 1.8.1 [PR 163](https://github.com/vultr/vultr-cli/pull/163) +* GoVultr v2.5.1 -> 2.6.0 (added support for persistent_pxe) [PR 164](https://github.com/vultr/vultr-cli/pull/164) + +### Enhancements +* Bare-metal : Support `persistent_pxe` on create [PR 164](https://github.com/vultr/vultr-cli/pull/164) + +## [v2.5.3](https://github.com/vultr/vultr-cli/compare/v2.5.2..v2.5.3) (2021-06-28) +### Dependencies +* Bump github.com/spf13/viper from 1.7.1 to 1.8.1 [PR 160](https://github.com/vultr/vultr-cli/pull/160) + +## [v2.5.2](https://github.com/vultr/vultr-cli/compare/v2.5.1..v2.5.2) (2021-05-17) +### Enhancement +* Support config files in $XDG_CONFIG_HOME [PR 153](https://github.com/vultr/vultr-cli/pull/153) + +### Documentation +* Add Arch Linux install instructions [PR 154](https://github.com/vultr/vultr-cli/pull/154) + +## [v2.5.1](https://github.com/vultr/vultr-cli/compare/v2.5.0..v2.5.1) (2021-05-12) +### Dependencies +* GoVultr v2.5.0 -> 2.5.1 (fixes issue with backup schedules) [PR 151](https://github.com/vultr/vultr-cli/pull/151) + +## [v2.5.0](https://github.com/vultr/vultr-cli/compare/v2.4.1..v2.5.0) (2021-05-06) +### Enhancement +* LoadBalancers : New Features [149](https://github.com/vultr/vultr-cli/pull/149) + * Ability to attach private networks + * Ability to set firewalls + * Get Firewall Rules + * List Firewall Rules ## [v2.4.0](https://github.com/vultr/vultr-cli/compare/v2.3.0..v2.4.0) (2021-04-01) ### Enhancement @@ -45,7 +415,7 @@ ## [v2.0.0](https://github.com/vultr/vultr-cli/compare/v1.0.0..v2.0.0) (2020-11-24) ### Enhancement -* Vultr-CLI v2.0.0 release +* Vultr-CLI v2.0.0 release ### Changes * Vultr-CLI v2.0.0 is running on API v2 @@ -53,11 +423,11 @@ ## [v1.0.0](https://github.com/vultr/vultr-cli/compare/v0.4.0..v1.0.0) (2020-11-19) ### Enhancement -* Vultr-CLI v1.0.0 release [PR 114](https://github.com/vultr/vultr-cli/pull/114) +* Vultr-CLI v1.0.0 release [PR 114](https://github.com/vultr/vultr-cli/pull/114) ## [v0.4.0](https://github.com/vultr/vultr-cli/compare/v0.3.2..v0.4.0) (2020-09-03) ### Enhancement -* Improve error responses by adding a newline [PR 109](https://github.com/vultr/vultr-cli/pull/109) +* Improve error responses by adding a newline [PR 109](https://github.com/vultr/vultr-cli/pull/109) * Add Server User Data subcommands Get and Set [PR 105](https://github.com/vultr/vultr-cli/pull/105) ### Dependencies @@ -119,7 +489,7 @@ ### Enhancements * Bump GoVultr to v0.1.5 [PR 55](https://github.com/vultr/vultr-cli/pull/55) - + ## [v0.1.6](https://github.com/vultr/vultr-cli/compare/v0.1.5..v0.1.6) (2019-09-04) ### Enhancements * Print the original API error messages in [PR 50](https://github.com/vultr/vultr-cli/pull/50) && [PR 52](https://github.com/vultr/vultr-cli/pull/52) @@ -140,7 +510,7 @@ ## [v0.1.3](https://github.com/vultr/vultr-cli/compare/v0.1.2..v0.1.3) (2019-08-21) ### Bug Fixes * Quote handling on DNS Record Data [PR #41](https://github.com/vultr/vultr-cli/pull/41) - + ## [v0.1.2](https://github.com/vultr/vultr-cli/compare/v0.1.1..v0.1.2) (2019-07-15) ### Dependencies * Updated dependencies [PR #35](https://github.com/vultr/vultr-cli/pull/35) diff --git a/Dockerfile b/Dockerfile index 846bb0c6..3c6ece36 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.14-alpine as builder +FROM golang:1.21-alpine as builder RUN apk add --update \ make @@ -17,4 +17,4 @@ FROM alpine:latest RUN apk add --no-cache ca-certificates COPY --from=builder /go/src/github.com/vultr/vultr-cli/builds/* / -ENTRYPOINT ["/vultr-cli_linux_amd64"] \ No newline at end of file +ENTRYPOINT ["/vultr-cli_linux_amd64"] diff --git a/Makefile b/Makefile index 890911c9..ebe4e025 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: remove format export CGO=0 -export GOFLAGS=-mod=vendor -trimpath +export GOFLAGS=-trimpath DIR?=builds @@ -29,6 +29,9 @@ $(DIR)/vultr-cli_windows_386.exe: $(DIR) $(DIR)/vultr-cli_windows_amd64.exe: $(DIR) env GOOS=windows GOARCH=amd64 go build -o $@ +$(DIR)/vultr-cli_linux_arm: $(DIR) + env GOOS=linux GOARCH=arm go build -o $@ + remove: @rm -rf builds @@ -37,4 +40,4 @@ format: docker: docker build . -t vultr/vultr-cli - docker push vultr/vultr-cli \ No newline at end of file + docker push vultr/vultr-cli diff --git a/vendor/github.com/inconshreveable/mousetrap/LICENSE b/NOTICE similarity index 85% rename from vendor/github.com/inconshreveable/mousetrap/LICENSE rename to NOTICE index 5f0d1fb6..7a4ee3d6 100644 --- a/vendor/github.com/inconshreveable/mousetrap/LICENSE +++ b/NOTICE @@ -1,4 +1,7 @@ -Copyright 2014 Alan Shreve +Vultr-CLI +Copyright © 2024 Vultr + +This product includes software developed at Vultr Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index d3404b4e..756bd5e1 100644 --- a/README.md +++ b/README.md @@ -9,32 +9,37 @@ Usage: vultr-cli [command] Available Commands: - account Retrieve information about your account - api-key retrieve information about the current API key - apps Display all available applications - backups display all available backups - bare-metal bare-metal is used to access bare metal server commands - block-storage block storage commands - dns dns is used to access dns commands - firewall firewall is used to access firewall commands - help Help about any command - iso iso is used to access iso commands - load-balancer load balancer commands - network network interacts with network actions - object-storage object storage commands - operatingSystems grab all available operating systems - plans get information about Vultr plans - regions get regions - reserved-ip reserved-ip lets you interact with reserved-ip - script startup script commands - instance commands to interact with instances on vultr - snapshot snapshot commands - ssh-key ssh-key commands - user user commands - version Display current version of Vultr-cli + account Retrieve information about your account + apps Display all available applications + backups Display backups + bare-metal bare-metal is used to access bare metal server commands + billing Display billing information + block-storage block storage commands + completion Generate the autocompletion script for the specified shell + container-registry commands to interact with container registries + database Commands to interact with managed databases on vultr + dns dns is used to access dns commands + firewall firewall is used to access firewall commands + help Help about any command + instance commands to interact with instances on vultr + iso iso is used to access iso commands + kubernetes kubernetes is used to access kubernetes commands + load-balancer load balancer commands + object-storage object storage commands + os os is used to access os commands + plans get information about Vultr plans + regions get regions + reserved-ip reserved-ip lets you interact with reserved-ip + script startup script commands + snapshot snapshot commands + ssh-key ssh-key commands + user user commands + version Display current version of Vultr-cli + vpc Interact with VPCs + vpc2 Interact with VPC 2.0 networks Flags: - --config string config file (default is $HOME/.vultr-cli.yaml) + --config string config file (default is $HOME/.vultr-cli.yaml) (default "#HOME/.vultr-cli.yaml") -h, --help help for vultr-cli -t, --toggle Help message for toggle @@ -43,85 +48,169 @@ Use "vultr-cli [command] --help" for more information about a command. ## Installation -There are three ways to install `vultr-cli`: +These are the options available to install `vultr-cli`: 1. Download a release from GitHub 2. From source 3. Package Manager + - Arch Linux - Brew + - OpenBSD (-current) - Snap (Coming soon) - Chocolatey (Coming soon) -4. [Docker Hub](https://hub.docker.com/repository/docker/vultr/vultr-cli) - +4. Docker + ### GitHub Release If you are to visit the `vultr-cli` [releases](https://github.com/vultr/vultr-cli/releases) page. You can download a compiled version of `vultr-cli` for you Linux/MacOS/Windows in 64bit. -### Building from source +### Building from source You will need Go installed on your machine in order to work with the source (and make if you decide to pull the repo down). -`go get -u github.com/vultr/vultr-cli` +`go get -u github.com/vultr/vultr-cli/v2` -Another way to build from source is to +Another way to build from source is to -``` +```sh git clone git@github.com:vultr/vultr-cli.git or git clone https://github.com/vultr/vultr-cli.git cd vultr-cli -make build_(pass name of os + arch) +make builds/vultr-cli_(pass name of os + arch, as shown below) ``` The available make build options are - make builds/vultr-cli_darwin_amd64 -- make builds/vultr-cli_darwin_arm64 +- make builds/vultr-cli_darwin_arm64 - make builds/vultr-cli_linux_386 -- make builds/builds/vultr-cli_linux_amd64 -- make builds/builds/vultr-cli_linux_arm64 +- make builds/vultr-cli_linux_amd64 +- make builds/vultr-cli_linux_arm64 - make builds/vultr-cli_windows_386.exe - make builds/vultr-cli_windows_amd64.exe +- make builds/vultr-cli_linux_arm Note that the latter method will install the `vultr-cli` executable in `builds/vultr-cli_(name of os + arch)`. +### Installing on Arch Linux + +```sh +pacman -S vultr-cli +``` + ### Installing via Brew -You will need to tap for formula -``` sh -brew tap vultr/vultr-cli +```sh +brew install vultr/vultr-cli/vultr-cli +``` + +### Installing on Fedora + +```sh +dnf install vultr-cli +``` + +### Installing on OpenBSD + +```sh +pkg_add vultr-cli +``` + +### Docker +You can find the image on [Docker Hub](https://hub.docker.com/repository/docker/vultr/vultr-cli). To install the latest version via `docker`: + +```sh +docker pull vultr/vultr-cli:latest +``` + +To pull an older image, you can pass the version string in the tag. For example: +```sh +docker pull vultr/vultr-cli:v2.15.1 ``` -Then install the formula +The available versions are listed [here](https://github.com/vultr/vultr-cli/releases). + +As described in the next section, you must authenticate in order to use the CLI. To pass the environment variable into docker, you can do so via: -```sh -brew install vultr-cli +```sh +docker run -e VULTR_API_KEY vultr/vultr-cli:latest instance list ``` +This assumes you've already set the environment variable in your shell environment, otherwise, you can pass it in via `-e VULTR_API_KEY=` + ## Using Vultr-cli ### Authentication -In order to use `vultr-cli` you will need to export your [Vultr API KEY](https://my.vultr.com/settings/#settingsapi) +In order to use `vultr-cli` you will need to export your [Vultr API KEY](https://my.vultr.com/settings/#settingsapi) -`export VULTR_API_KEY=your_api_key` +`export VULTR_API_KEY=` ### Examples `vultr-cli` can interact with all of your Vultr resources. Here are some basic examples to get you started: -##### List all available servers -`vultr-cli server list` +##### List all available instances +`vultr-cli instance list` -##### Create a server -`vultr-cli server create --region --plan --os --hostname ` +##### Create an instance +`vultr-cli instance create --region --plan --os --host ` ##### Create a DNS Domain `vultr-cli dns domain create --domain --ip ` +##### Utilizing a boolean flag +You should use = when using a boolean flag. + +`vultr-cli instance create --region --plan --os --host --notify=true` ##### Utilizing the config flag -The config flag can be used to specify the vultr-cli.yaml file path when it's outside the default location. If the file has the `api-key` defined, the CLI will use the vultr-cli.yaml config, otherwise it will default to reading the environment variable for the api key. +The config flag can be used to specify the vultr-cli.yaml file path when it's outside the default location (default is $HOME/.vultr-cli.yaml). If the file has the `api-key` defined, the CLI will use the vultr-cli.yaml config, otherwise it will default to reading the environment variable for the api key. `vultr-cli instance list --config /Users/myuser/vultr-cli.yaml` ### Example vultr-cli.yaml config file + +Currently the only available field that you can use with a config file is `api-key`. Your yaml file will have a single entry which would be: + `api-key: MYKEY` +### CLI Autocompletion +`vultr-cli completion` will return autocompletions, but this feature requires setup. + +Some guides: + +
+

Bash:

+ $ source <(yourprogram completion bash) + + To load completions for each session, execute once: + Linux: + $ yourprogram completion bash > /etc/bash_completion.d/yourprogram + + macOS: + $ yourprogram completion bash > /usr/local/etc/bash_completion.d/yourprogram + +

Zsh:

+ If shell completion is not already enabled in your environment, + you will need to enable it. You can execute the following once: + + $ echo "autoload -U compinit; compinit" >> ~/.zshrc + + To load completions for each session, execute once: + $ yourprogram completion zsh > "${fpath[1]}/_yourprogram" + + You will need to start a new shell for this setup to take effect. + +

fish:

+ $ yourprogram completion fish | source + + To load completions for each session, execute once: + $ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish + +

PowerShell:

+ PS> yourprogram completion powershell | Out-String | Invoke-Expression + + To load completions for every new session, run: + PS> yourprogram completion powershell > yourprogram.ps1 + and source this file from your PowerShell profile. +
+ ## Contributing Feel free to send pull requests our way! Please see the [contributing guidelines](CONTRIBUTING.md). diff --git a/cmd/account/account.go b/cmd/account/account.go index 1564c6ee..f14322f4 100644 --- a/cmd/account/account.go +++ b/cmd/account/account.go @@ -1,28 +1,14 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Package account provides the account functionality for the CLI package account import ( "context" - "fmt" - "os" + "errors" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/pkg/cli" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" ) var ( @@ -33,34 +19,23 @@ var ( ` ) -// Interface for account -type AccountInterface interface { - Get() (*govultr.Account, error) - validate(cmd *cobra.Command, args []string) -} - -// Options for account -type Options struct { - Base *cli.Base -} - -// NewAccountOptions returns Options struct -func NewAccountOptions(base *cli.Base) *Options { - return &Options{Base: base} -} - // NewCmdAccount creates a cobra command for Account func NewCmdAccount(base *cli.Base) *cobra.Command { - o := NewAccountOptions(base) + o := &options{Base: base} cmd := &cobra.Command{ Use: "account", Short: "get account information", Long: accountLong, Example: accountExample, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, Run: func(cmd *cobra.Command, args []string) { - o.validate(cmd, args) - account, err := o.Get() + account, err := o.get() o.Base.Printer.Display(&AccountPrinter{Account: account}, err) }, @@ -69,17 +44,11 @@ func NewCmdAccount(base *cli.Base) *cobra.Command { return cmd } -func (o *Options) validate(cmd *cobra.Command, args []string) { - o.Base.Printer.Output = viper.GetString("output") +type options struct { + Base *cli.Base } -// Get account information -func (o *Options) Get() (*govultr.Account, error) { - account, err := o.Base.Client.Account.Get(context.Background()) - if err != nil { - fmt.Printf("Error getting account information : %v\n", err) - os.Exit(1) - } - - return account, nil +func (o *options) get() (*govultr.Account, error) { + account, _, err := o.Base.Client.Account.Get(context.Background()) + return account, err } diff --git a/cmd/account/account_test.go b/cmd/account/account_test.go deleted file mode 100644 index 80118524..00000000 --- a/cmd/account/account_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package account - -import ( - "context" - "reflect" - "testing" - - "github.com/vultr/vultr-cli/pkg/cli" - - "github.com/vultr/govultr/v2" -) - -type mockVultrAccount struct { - client *govultr.Client -} - -func (m mockVultrAccount) Get(ctx context.Context) (*govultr.Account, error) { - return &govultr.Account{ - Balance: 10, - PendingCharges: 100, - Name: "John Smith", - Email: "john@example.com", - ACL: []string{"manage_users", "subscriptions", "billing"}, - }, nil -} - -func TestNewAccountOptions(t *testing.T) { - accountOption := NewAccountOptions(&cli.Base{Client: &govultr.Client{Account: mockVultrAccount{nil}}}) - - ref := reflect.TypeOf(accountOption) - if _, ok := ref.MethodByName("Get"); !ok { - t.Errorf("Missing get function") - } - - if _, ok := ref.MethodByName("validate"); ok { - t.Errorf("validate isn't exported shouldn't be accessible") - } - - aInterface := reflect.TypeOf(new(AccountInterface)).Elem() - if !ref.Implements(aInterface) { - t.Errorf("Options does not implement AccountInterface") - } -} - -func TestNewCmdAccount(t *testing.T) { - cmd := NewCmdAccount(&cli.Base{Client: &govultr.Client{Account: mockVultrAccount{nil}}}) - - if cmd.Short != "get account information" { - t.Errorf("invalid short") - } - - if cmd.Use != "account" { - t.Errorf("invalid account") - } - -} - -func TestOptions_Get(t *testing.T) { - a := NewAccountOptions(&cli.Base{Client: &govultr.Client{Account: mockVultrAccount{nil}}}) - - expectedAccount := &govultr.Account{ - Balance: 10, - PendingCharges: 100, - Name: "John Smith", - Email: "john@example.com", - ACL: []string{"manage_users", "subscriptions", "billing"}, - } - - account, _ := a.Get() - - if !reflect.DeepEqual(account, expectedAccount) { - t.Errorf("OSOptions.list returned %v expected %v", account, expectedAccount) - } - -} diff --git a/cmd/account/printer.go b/cmd/account/printer.go index adb0f0b9..3bc2774a 100644 --- a/cmd/account/printer.go +++ b/cmd/account/printer.go @@ -1,10 +1,11 @@ package account import ( - "encoding/json" + "strconv" - "github.com/go-yaml/yaml" - "github.com/vultr/govultr/v2" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" ) // AccountPrinter ... @@ -13,35 +14,42 @@ type AccountPrinter struct { } // JSON ... -func (s *AccountPrinter) JSON() []byte { - prettyJSON, err := json.MarshalIndent(s, "", " ") - if err != nil { - panic("move this into byte") - } - - return prettyJSON +func (a *AccountPrinter) JSON() []byte { + return printer.MarshalObject(a, "json") } -// Yaml ... -func (s *AccountPrinter) Yaml() []byte { - yam, err := yaml.Marshal(s) - if err != nil { - panic("move this into byte") - } - return yam +// YAML ... +func (a *AccountPrinter) YAML() []byte { + return printer.MarshalObject(a, "yaml") } // Columns ... -func (a *AccountPrinter) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"BALANCE", "PENDING CHARGES", "LAST PAYMENT DATE", "LAST PAYMENT AMOUNT", "NAME", "EMAIL", "ACLS"}} +func (a *AccountPrinter) Columns() [][]string { + return [][]string{0: { + "BALANCE", + "PENDING CHARGES", + "LAST PAYMENT DATE", + "LAST PAYMENT AMOUNT", + "NAME", + "EMAIL", + "ACLS", + }} } // Data ... -func (a *AccountPrinter) Data() map[int][]interface{} { - return map[int][]interface{}{0: {a.Account.Balance, a.Account.PendingCharges, a.Account.LastPaymentDate, a.Account.LastPaymentAmount, a.Account.Name, a.Account.Email, a.Account.ACL}} +func (a *AccountPrinter) Data() [][]string { + return [][]string{0: { + strconv.FormatFloat(float64(a.Account.Balance), 'f', utils.DecimalPrecision, 32), + strconv.FormatFloat(float64(a.Account.PendingCharges), 'f', utils.DecimalPrecision, 32), + a.Account.LastPaymentDate, + strconv.FormatFloat(float64(a.Account.LastPaymentAmount), 'f', utils.DecimalPrecision, 32), + a.Account.Name, + a.Account.Email, + printer.ArrayOfStringsToString(a.Account.ACL), + }} } // Paging ... -func (a *AccountPrinter) Paging() map[int][]interface{} { +func (a *AccountPrinter) Paging() [][]string { return nil } diff --git a/cmd/applications/applications.go b/cmd/applications/applications.go index 2529930f..38288e60 100644 --- a/cmd/applications/applications.go +++ b/cmd/applications/applications.go @@ -1,28 +1,15 @@ +// Package applications provides the application functionality for the CLI package applications -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - import ( "context" + "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" - "github.com/vultr/vultr-cli/cmd/utils" - "github.com/vultr/vultr-cli/pkg/cli" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" ) var ( @@ -45,61 +32,60 @@ var ( ` ) -// Interface for regions -type Interface interface { - List() ([]govultr.Application, *govultr.Meta, error) - validate(cmd *cobra.Command, args []string) -} - -// Options for regions -type Options struct { - Base *cli.Base - Printer *printer.Output -} - -func NewApplicationOptions(base *cli.Base) *Options { - return &Options{Base: base} -} - // NewCmdApplications creates cobra command for applications func NewCmdApplications(base *cli.Base) *cobra.Command { - o := NewApplicationOptions(base) + o := &options{Base: base} + cmd := &cobra.Command{ Use: "apps", - Aliases: []string{"a", "application", "applications", "app"}, Short: "display applications", + Aliases: []string{"a", "application", "applications", "app"}, Long: appLong, Example: appExample, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + return nil + }, } + // List list := &cobra.Command{ Use: "list", - Aliases: []string{"l"}, Short: "list applications", + Aliases: []string{"l"}, Long: listLong, Example: listExample, - Run: func(cmd *cobra.Command, args []string) { - o.validate(cmd, args) - apps, meta, err := o.List() - data := &printer.Applications{Applications: apps, Meta: meta} - o.Printer.Display(data, err) + RunE: func(cmd *cobra.Command, args []string) error { + apps, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving application list : %v", err) + } + + data := &ApplicationsPrinter{Applications: apps, Meta: meta} + o.Base.Printer.Display(data, err) + + return nil }, } list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - list.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) cmd.AddCommand(list) return cmd } -func (o *Options) validate(cmd *cobra.Command, args []string) { - o.Base.Printer.Output = viper.GetString("output") - o.Base.Options = utils.GetPaging(cmd) - o.Base.Args = args +type options struct { + Base *cli.Base + Printer *printer.Output } -// List all applications -func (o *Options) List() ([]govultr.Application, *govultr.Meta, error) { - return o.Base.Client.Application.List(context.Background(), o.Base.Options) +func (o *options) list() ([]govultr.Application, *govultr.Meta, error) { + list, meta, _, err := o.Base.Client.Application.List(context.Background(), o.Base.Options) + return list, meta, err } diff --git a/cmd/applications/applications_test.go b/cmd/applications/applications_test.go deleted file mode 100644 index 3470c373..00000000 --- a/cmd/applications/applications_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package applications - -import ( - "context" - "reflect" - "testing" - - "github.com/vultr/vultr-cli/pkg/cli" - - "github.com/vultr/govultr/v2" -) - -type mockVultrApplications struct { - client *govultr.Client -} - -func (m mockVultrApplications) List(ctx context.Context, options *govultr.ListOptions) ([]govultr.Application, *govultr.Meta, error) { - return []govultr.Application{ - { - ID: 1, - Name: "LEMP", - ShortName: "LEMP", - DeployName: "Lemp on CentOS 6", - }, - }, &govultr.Meta{ - Total: 1, - Links: nil, - }, nil -} - -func TestOptions_List(t *testing.T) { - appOption := NewApplicationOptions(&cli.Base{Client: &govultr.Client{Application: mockVultrApplications{nil}}}) - - expected := []govultr.Application{ - { - ID: 1, - Name: "LEMP", - ShortName: "LEMP", - DeployName: "Lemp on CentOS 6", - }, - } - expectedMeta := &govultr.Meta{ - Total: 1, - Links: nil, - } - - app, meta, _ := appOption.List() - - if !reflect.DeepEqual(expected, app) { - t.Errorf("AppOptions.list returned %v expected %v", app, expected) - } - - if !reflect.DeepEqual(expectedMeta, meta) { - t.Errorf("AppOptions.list returned %v expected %v", meta, expectedMeta) - } -} - -func TestNewApplicationOptions(t *testing.T) { - appOption := NewApplicationOptions(&cli.Base{Client: &govultr.Client{Application: mockVultrApplications{nil}}}) - - ref := reflect.TypeOf(appOption) - if _, ok := ref.MethodByName("List"); !ok { - t.Errorf("Missing list function") - } - - if _, ok := ref.MethodByName("validate"); ok { - t.Errorf("validate isn't exported shouldn't be accessible") - } - - pInterface := reflect.TypeOf(new(Interface)).Elem() - if !ref.Implements(pInterface) { - t.Errorf("Options does not implement Interface") - } -} - -func TestNewCmdApplications(t *testing.T) { - cmd := NewCmdApplications(&cli.Base{Client: &govultr.Client{Application: mockVultrApplications{nil}}}) - - if cmd.Short != "display applications" { - t.Errorf("invalid short") - } - - if cmd.Use != "apps" { - t.Errorf("invalid apps") - } - - alias := []string{"a", "application", "applications", "app"} - if !reflect.DeepEqual(cmd.Aliases, alias) { - t.Errorf("expected alias %v got %v", alias, cmd.Aliases) - } -} diff --git a/cmd/applications/printer.go b/cmd/applications/printer.go new file mode 100644 index 00000000..b24d8613 --- /dev/null +++ b/cmd/applications/printer.go @@ -0,0 +1,66 @@ +package applications + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// ApplicationsPrinter represents the plans data from the API +type ApplicationsPrinter struct { + Applications []govultr.Application `json:"applications"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON provides the JSON formatted byte data +func (a *ApplicationsPrinter) JSON() []byte { + return printer.MarshalObject(a, "json") +} + +// YAML provides the YAML formatted byte data +func (a *ApplicationsPrinter) YAML() []byte { + return printer.MarshalObject(a, "yaml") +} + +// Columns provides the plan columns for the printer +func (a *ApplicationsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "NAME", + "SHORT NAME", + "DEPLOY NAME", + "TYPE", + "VENDOR", + "IMAGE ID", + }} +} + +// Data provides the plan data for the printer +func (a *ApplicationsPrinter) Data() [][]string { + var data [][]string + + if len(a.Applications) == 0 { + data = append(data, []string{"---", "---", "---", "---", "---", "---", "---"}) + return data + } + + for i := range a.Applications { + data = append(data, []string{ + strconv.Itoa(a.Applications[i].ID), + a.Applications[i].Name, + a.Applications[i].ShortName, + a.Applications[i].DeployName, + a.Applications[i].Type, + a.Applications[i].Vendor, + a.Applications[i].ImageID, + }) + } + + return data +} + +// Paging validates and forms the paging data for output +func (a *ApplicationsPrinter) Paging() [][]string { + return printer.NewPaging(a.Meta.Total, &a.Meta.Links.Next, &a.Meta.Links.Prev).Compose() +} diff --git a/cmd/backups.go b/cmd/backups.go deleted file mode 100644 index c99c6576..00000000 --- a/cmd/backups.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// Backups represents the application command -func Backups() *cobra.Command { - backupsCmd := &cobra.Command{ - Use: "backups", - Aliases: []string{"a"}, - Short: "Display backups", - } - - backupsCmd.AddCommand(backupsList, backupsGet) - - backupsList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - backupsList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return backupsCmd -} - -var backupsList = &cobra.Command{ - Use: "list", - Short: "list backups", - Aliases: []string{"l"}, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - backups, meta, err := client.Backup.List(context.TODO(), options) - if err != nil { - fmt.Printf("error getting backups : %v\n", err) - os.Exit(1) - } - - printer.Backups(backups, meta) - }, -} - -var backupsGet = &cobra.Command{ - Use: "get", - Short: "get backup", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a backupID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - backup, err := client.Backup.Get(context.TODO(), args[0]) - if err != nil { - fmt.Printf("error getting backup : %v\n", err) - os.Exit(1) - } - - printer.Backup(backup) - }, -} diff --git a/cmd/backups/backups.go b/cmd/backups/backups.go new file mode 100644 index 00000000..8a476f5b --- /dev/null +++ b/cmd/backups/backups.go @@ -0,0 +1,110 @@ +// Package backups provides access to the backups for the CLI +package backups + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + backupsLong = `` + backupsExample = `` + listLong = `` + listExample = `` + getLong = `` + getExample = `` +) + +// NewCmdBackups provides the backup command for the CLI +func NewCmdBackups(base *cli.Base) *cobra.Command { + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "backups", + Aliases: []string{"backup", "b"}, + Short: "user commands", + Long: backupsLong, + Example: backupsExample, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "list all backups", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + backups, meta, err := o.list() + if err != nil { + printer.Error(fmt.Errorf("error retrieving backups list : %v", err)) + os.Exit(1) + } + data := &BackupsPrinter{Backups: backups, Meta: meta} + o.Base.Printer.Display(data, err) + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get + get := &cobra.Command{ + Use: "get", + Short: "get a backup", + Long: getLong, + Example: getExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a backup ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + backup, err := o.get() + if err != nil { + panic(fmt.Errorf("error retrieving backup : %v", err)) + } + + data := &BackupPrinter{Backup: backup} + o.Base.Printer.Display(data, err) + }, + } + + cmd.AddCommand(get, list) + return cmd +} + +type options struct { + Base *cli.Base +} + +func (b *options) list() ([]govultr.Backup, *govultr.Meta, error) { + backups, meta, _, err := b.Base.Client.Backup.List(b.Base.Context, b.Base.Options) + return backups, meta, err +} + +func (b *options) get() (*govultr.Backup, error) { + backup, _, err := b.Base.Client.Backup.Get(b.Base.Context, b.Base.Args[0]) + return backup, err +} diff --git a/cmd/backups/printer.go b/cmd/backups/printer.go new file mode 100644 index 00000000..3d123252 --- /dev/null +++ b/cmd/backups/printer.go @@ -0,0 +1,99 @@ +package backups + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// BackupsPrinter ... +type BackupsPrinter struct { + Backups []govultr.Backup `json:"backups"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (b *BackupsPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BackupsPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BackupsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "DATE CREATED", + "DESCRIPTION", + "SIZE", + "STATUS", + }} +} + +// Data ... +func (b *BackupsPrinter) Data() [][]string { + data := [][]string{} + for i := range b.Backups { + data = append(data, []string{ + b.Backups[i].ID, + b.Backups[i].DateCreated, + b.Backups[i].Description, + strconv.Itoa(b.Backups[i].Size), + b.Backups[i].Status, + }) + } + return data +} + +// Paging ... +func (b *BackupsPrinter) Paging() [][]string { + return printer.NewPaging(b.Meta.Total, &b.Meta.Links.Next, &b.Meta.Links.Prev).Compose() +} + +// ====================================== + +// BackupPrinter ... +type BackupPrinter struct { + Backup *govultr.Backup `json:"backup"` +} + +// JSON ... +func (b *BackupPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BackupPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BackupPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "DATE CREATED", + "DESCRIPTION", + "SIZE", + "STATUS", + }} +} + +// Data ... +func (b *BackupPrinter) Data() [][]string { + return [][]string{0: { + b.Backup.ID, + b.Backup.DateCreated, + b.Backup.Description, + strconv.Itoa(b.Backup.Size), + b.Backup.Status, + }} +} + +// Paging ... +func (b *BackupPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/bareMetal.go b/cmd/bareMetal.go deleted file mode 100644 index 1f5ddd27..00000000 --- a/cmd/bareMetal.go +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/base64" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// BareMetal represents the baremetal commands -func BareMetal() *cobra.Command { - bareMetalCmd := &cobra.Command{ - Use: "bare-metal", - Short: "bare-metal is used to access bare metal server commands", - Aliases: []string{"bm"}, - } - - bareMetalCmd.AddCommand( - BareMetalApp(), - bareMetalBandwidth, - bareMetalCreate, - bareMetalDelete, - bareMetalHalt, - bareMetalStart, - bareMetalGet, - bareMetalGetVNCUrl, - bareMetalListIPV4, - bareMetalListIPV6, - bareMetalList, - BareMetalOS(), - bareMetalReboot, - bareMetalReinstall, - BareMetalUserData(), - ) - - // create server - bareMetalCreate.Flags().StringP("region", "r", "", "ID of the region where the server will be created.") - bareMetalCreate.MarkFlagRequired("region") - bareMetalCreate.Flags().StringP("plan", "p", "", "ID of the plan that the server will subscribe to.") - bareMetalCreate.MarkFlagRequired("plan") - bareMetalCreate.Flags().IntP("operatingSystems", "o", 0, "ID of the operating system that will be installed on the server.") - bareMetalCreate.Flags().StringP("script", "s", "", "(optional) ID of the startup script that will run after the server is created.") - bareMetalCreate.Flags().StringP("snapshot", "", "", "(optional) ID of the snapshot that the server will be restored from.") - bareMetalCreate.Flags().StringP("ipv6", "i", "", "(optional) Whether IPv6 is enabled on the server. Possible values: 'yes', 'no'. Defaults to 'no'.") - bareMetalCreate.Flags().StringP("label", "l", "", "(optional) The label to assign to the server.") - bareMetalCreate.Flags().StringSliceP("ssh", "k", []string{}, "(optional) Comma separated list of SSH key IDs that will be added to the server.") - bareMetalCreate.Flags().IntP("app", "a", 0, "(optional) ID of the application that will be installed on the server.") - bareMetalCreate.Flags().StringP("userdata", "u", "", "(optional) A generic data store, which some provisioning tools and cloud operating systems use as a configuration file.") - bareMetalCreate.Flags().StringP("notify", "n", "", "(optional) Whether an activation email will be sent when the server is ready. Possible values: 'yes', 'no'. Defaults to 'yes'.") - bareMetalCreate.Flags().StringP("hostname", "m", "", "(optional) The hostname to assign to the server.") - bareMetalCreate.Flags().StringP("tag", "t", "", "(optional) The tag to assign to the server.") - bareMetalCreate.Flags().StringP("ripv4", "v", "", "(optional) IP address of the floating IP to use as the main IP of this server.") - - bareMetalList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - bareMetalList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - bareMetalListIPV4.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - bareMetalListIPV4.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - bareMetalListIPV6.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - bareMetalListIPV6.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return bareMetalCmd -} - -var bareMetalCreate = &cobra.Command{ - Use: "create", - Short: "create a bare metal server", - Aliases: []string{"c"}, - Run: func(cmd *cobra.Command, args []string) { - region, _ := cmd.Flags().GetString("region") - plan, _ := cmd.Flags().GetString("plan") - osID, _ := cmd.Flags().GetInt("operatingSystems") - script, _ := cmd.Flags().GetString("script") - snapshot, _ := cmd.Flags().GetString("snapshot") - ipv6, _ := cmd.Flags().GetString("ipv6") - label, _ := cmd.Flags().GetString("label") - sshKeys, _ := cmd.Flags().GetStringSlice("ssh") - app, _ := cmd.Flags().GetInt("app") - userdata, _ := cmd.Flags().GetString("userdata") - notify, _ := cmd.Flags().GetString("notify") - hostname, _ := cmd.Flags().GetString("hostname") - tag, _ := cmd.Flags().GetString("tag") - ripv4, _ := cmd.Flags().GetString("ripv4") - - options := &govultr.BareMetalCreate{ - StartupScriptID: script, - Plan: plan, - SnapshotID: snapshot, - Label: label, - SSHKeyIDs: sshKeys, - Hostname: hostname, - Tag: tag, - ReservedIPv4: ripv4, - OsID: osID, - Region: region, - AppID: app, - } - - if userdata != "" { - options.UserData = base64.StdEncoding.EncodeToString([]byte(userdata)) - } - - if notify == "yes" { - options.ActivationEmail = govultr.BoolToBoolPtr(true) - } - - if ipv6 == "yes" { - options.EnableIPv6 = govultr.BoolToBoolPtr(true) - } - - osOptions := map[string]interface{}{"app_id": app, "snapshot_id": snapshot, "os_id": osID} - osOption, err := optionCheckBM(osOptions) - - if err != nil { - fmt.Printf("error creating bare metal server : %v\n", err) - os.Exit(1) - } - - // If no osOptions were selected and osID has a real value then set the osOptions to os_id - if osOption == "" && osID == 0 { - fmt.Printf("error creating bare metal server : an app, snapshot, or operatingSystems ID must be provided\n") - os.Exit(1) - } - - bm, err := client.BareMetalServer.Create(context.TODO(), options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.BareMetal(bm) - }, -} - -var bareMetalDelete = &cobra.Command{ - Use: "delete ", - Short: "Delete a bare metal server", - Aliases: []string{"destroy"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - if err := client.BareMetalServer.Delete(context.TODO(), args[0]); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("deleted bare metal server") - }, -} - -var bareMetalList = &cobra.Command{ - Use: "list", - Short: "List all bare metal servers.", - Aliases: []string{"l"}, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - list, meta, err := client.BareMetalServer.List(context.TODO(), options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.BareMetalList(list, meta) - }, -} - -var bareMetalGet = &cobra.Command{ - Use: "get ", - Short: "Get a bare metal server by ", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - srv, err := client.BareMetalServer.Get(context.TODO(), args[0]) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.BareMetal(srv) - }, -} - -var bareMetalGetVNCUrl = &cobra.Command{ - Use: "vnc ", - Short: "Get a bare metal server's VNC url by ", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - vnc, err := client.BareMetalServer.GetVNCUrl(context.TODO(), args[0]) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println(vnc.URL) - }, -} - -var bareMetalBandwidth = &cobra.Command{ - Use: "bandwidth ", - Short: "Get a bare metal server's bandwidth usage", - Aliases: []string{"b"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - bw, err := client.BareMetalServer.GetBandwidth(context.TODO(), args[0]) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.BareMetalBandwidth(bw) - }, -} - -var bareMetalHalt = &cobra.Command{ - Use: "halt ", - Short: "Halt a bare metal server.", - Long: `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.`, - Aliases: []string{"h"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - if err := client.BareMetalServer.Halt(context.TODO(), args[0]); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("bare metal server halted.") - }, -} - -var bareMetalStart = &cobra.Command{ - Use: "start ", - Short: "Start a bare metal server.", - Long: `Start a bare metal server.`, - Aliases: []string{"h"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - if err := client.BareMetalServer.Start(context.TODO(), args[0]); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("bare metal server started.") - }, -} - -var bareMetalListIPV4 = &cobra.Command{ - Use: "ipv4 ", - Short: "List the IPv4 information of a bare metal server.", - Long: `List the IPv4 information of a bare metal server. IP information is only available for bare metal servers in the "active" state.`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - info, meta, err := client.BareMetalServer.ListIPv4s(context.TODO(), args[0], options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.BareMetalIPV4Info(info, meta) - }, -} - -var bareMetalListIPV6 = &cobra.Command{ - Use: "ipv6 ", - Short: "List the IPv6 information of a bare metal server.", - Long: `List the IPv6 information of a bare metal server. IP information is only available for bare metal servers in the "active" state.`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - info, meta, err := client.BareMetalServer.ListIPv6s(context.TODO(), args[0], options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.BareMetalIPV6Info(info, meta) - }, -} - -var bareMetalReboot = &cobra.Command{ - Use: "reboot ", - Short: "Reboot a bare metal server. This is a hard reboot, which means that the server is powered off, then back on.", - Aliases: []string{"r"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - if err := client.BareMetalServer.Reboot(context.TODO(), args[0]); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("bare metal server rebooted.") - }, -} - -var bareMetalReinstall = &cobra.Command{ - Use: "reinstall ", - Short: "Reinstall the operating system on a bare metal server.", - Long: `Reinstall the operating system on a bare metal server. - All data will be permanently lost, but the IP address will remain the same. There is no going back from this call.`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - if _, err := client.BareMetalServer.Reinstall(context.TODO(), args[0]); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("bare metal server reinstalled.") - }, -} - -func optionCheckBM(options map[string]interface{}) (string, error) { - result := []string{} - - for k, v := range options { - switch v.(type) { - case int: - if v != 0 { - result = append(result, k) - } - case string: - if v != "" { - result = append(result, k) - } - } - } - - if len(result) > 1 { - return "", fmt.Errorf("Too many options have been selected : %v : please select one", result) - } - - // Return back an empty slice so we can possibly add in osID - if len(result) == 0 { - return "", nil - } - - return result[0], nil -} diff --git a/cmd/bareMetalApp.go b/cmd/bareMetalApp.go deleted file mode 100644 index 0d23fcf0..00000000 --- a/cmd/bareMetalApp.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - "strconv" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// BareMetalApp represents the baremetal app commands -func BareMetalApp() *cobra.Command { - bareMetalAppCmd := &cobra.Command{ - Use: "app", - Short: "app is used to access bare metal server application commands", - Aliases: []string{"a"}, - } - - bareMetalAppCmd.AddCommand(bareMetalAppChange, bareMetalAppChangeList) - - return bareMetalAppCmd -} - -var bareMetalAppChange = &cobra.Command{ - Use: "change ", - Short: "Change a bare metal server's application", - Aliases: []string{"c"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a bareMetalID and appID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - appID, _ := strconv.Atoi(args[1]) - options := &govultr.BareMetalUpdate{ - AppID: appID, - } - - _, err := client.BareMetalServer.Update(context.TODO(), args[0], options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("bare metal server's application changed") - }, -} - -var bareMetalAppChangeList = &cobra.Command{ - Use: "list ", - Short: "available apps a bare metal server can change to.", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - list, err := client.BareMetalServer.GetUpgrades(context.TODO(), id) - - if err != nil { - fmt.Printf("error listing available applications : %v\n", err) - os.Exit(1) - } - - printer.AppList(list.Applications) - }, -} diff --git a/cmd/bareMetalOS.go b/cmd/bareMetalOS.go deleted file mode 100644 index e945ff48..00000000 --- a/cmd/bareMetalOS.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - "strconv" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// BareMetalOS represents the baremetal operating system commands -func BareMetalOS() *cobra.Command { - bareMetalOSCmd := &cobra.Command{ - Use: "operatingSystems", - Short: "operatingSystems is used to access bare metal server operating system commands", - Aliases: []string{"o"}, - } - - bareMetalOSCmd.AddCommand(bareMetalOSChange, bareMetalOSChangeList) - - return bareMetalOSCmd -} - -var bareMetalOSChange = &cobra.Command{ - Use: "change ", - Short: "Change a bare metal server's operating system", - Aliases: []string{"c"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a bareMetalID and osID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - osid, _ := strconv.Atoi(args[1]) - options := &govultr.BareMetalUpdate{ - OsID: osid, - } - - if _, err := client.BareMetalServer.Update(context.TODO(), args[0], options); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("bare metal server's operating system changed") - }, -} - -var bareMetalOSChangeList = &cobra.Command{ - Use: "list ", - Short: "available operating systems a bare metal server can change to.", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - list, err := client.BareMetalServer.GetUpgrades(context.TODO(), id) - - if err != nil { - fmt.Printf("error listing available operatingSystems : %v\n", err) - os.Exit(1) - } - - printer.OsList(list.OS) - }, -} diff --git a/cmd/bareMetalUserData.go b/cmd/bareMetalUserData.go deleted file mode 100644 index 9b38cada..00000000 --- a/cmd/bareMetalUserData.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// BareMetalUserData represents the baremetal userdata commands -func BareMetalUserData() *cobra.Command { - bareMetalUserDataCmd := &cobra.Command{ - Use: "user-data", - Short: "user-data is used to access bare metal server user-data commands", - Aliases: []string{"u"}, - } - - bareMetalSetUserData.Flags().StringP("userdata", "d", "/dev/stdin", "file to read userdata from") - bareMetalUserDataCmd.AddCommand(bareMetalGetUserData, bareMetalSetUserData) - - return bareMetalUserDataCmd -} - -var bareMetalGetUserData = &cobra.Command{ - Use: "get ", - Short: "Get the user-data of a bare metal server.", - Aliases: []string{"g"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - u, err := client.BareMetalServer.GetUserData(context.Background(), args[0]) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.UserData(u) - }, -} - -var bareMetalSetUserData = &cobra.Command{ - Use: "set ", - Short: "Set the plain text user-data of a bare metal server.", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a bareMetalID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - userData, _ := cmd.Flags().GetString("userdata") - - rawData, err := ioutil.ReadFile(userData) - if err != nil { - fmt.Printf("error reading user-data : %v\n", err) - os.Exit(1) - } - - options := &govultr.BareMetalUpdate{ - UserData: base64.StdEncoding.EncodeToString(rawData), - } - - _, err = client.BareMetalServer.Update(context.TODO(), args[0], options) - if err != nil { - fmt.Printf("error setting user-data : %v\n", err) - os.Exit(1) - } - - fmt.Println("Set user-data for bare metal") - }, -} diff --git a/cmd/baremetal/baremetal.go b/cmd/baremetal/baremetal.go new file mode 100644 index 00000000..13f01d22 --- /dev/null +++ b/cmd/baremetal/baremetal.go @@ -0,0 +1,1232 @@ +// Package baremetal provides functionality to perform operations on +// bare metal servers through the CLI +package baremetal + +import ( + "encoding/base64" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/applications" + "github.com/vultr/vultr-cli/v3/cmd/ip" + "github.com/vultr/vultr-cli/v3/cmd/operatingsystems" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/userdata" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Show all commands available to bare-metal` + example = ` + # Full example + vultr-cli bare-metal + ` + + listLong = `` + listExample = `` + getLong = `` + getExample = `` + createLong = `` + createExample = `` + deleteLong = `` + deleteExample = `` + + haltLong = ` + 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. + ` + + haltExample = `` + + startLong = `` + startExample = `` + rebootLong = `This is a hard reboot, which means that the server is powered off, then back on.` + rebootExample = `` + + reinstallLong = `Reinstall the operating system on a bare metal server. +All data will be permanently lost, but the IP address will remain the same. +There is no going back from this call.` + reinstallExample = `` + + tagsLong = `Update the tags on a bare metal server` + tagsExample = ` + # Full example + vultr-cli bare-metal tags tags="tag-1,tag-2" + + # Shortened example with aliases + vultr-cli bm tags -t="tag-1,tag-2" + ` + + vpc2AttachLong = `Attaches an existing VPC 2.0 network to the specified bare metal server` + vpc2AttachExample = ` + # Full example + vultr-cli bare-metal vpc2 attach --vpc-id="2126b7d9-5e2a-491e-8840-838aa6b5f294" + ` + vpc2DetachLong = `Detaches an existing VPC 2.0 network from the specified bare metal server` + vpc2DetachExample = ` + # Full example + vultr-cli bare-metal vpc2 detach --vpc-id="2126b7d9-5e2a-491e-8840-838aa6b5f294" + ` + + applicationLong = `` + applicationExample = `` + + applicationChangeLong = `` + applicationChangeExample = `` + + applicationListLong = `` + applicationListExample = `` + + imageLong = `` + imageExample = `` + imageChangeLong = `` + imageChangeExample = `` + + operatingSystemLong = `` + operatingSystemExample = `` + operatingSystemChangeLong = `` + operatingSystemChangeExample = `` + operatingSystemListLong = `` + operatingSystemListExample = `` + + userDataLong = `` + userDataExample = `` + userDataGetLong = `` + userDataGetExample = `` + userDataSetLong = `` + userDataSetExample = `` + + vncLong = `` + vncExample = `` + + bandwidthLong = `` + bandwidthExample = `` + + ipv4Long = `IP information is only available for bare metal servers in the "active" state.` + ipv4Example = `` + + ipv6Long = ` +List the IPv6 information of a bare metal server. IP information is only available for bare metal servers in the "active" state. +` + ipv6Example = `` + + vpc2Long = `` + vpc2ListLong = `` + vpc2ListExample = `` +) + +// NewCmdBareMetal ... +func NewCmdBareMetal(base *cli.Base) *cobra.Command { //nolint:funlen,gocyclo + o := options{Base: base} + + cmd := &cobra.Command{ + Use: "bare-metal", + Short: "Bare metal server commands", + Aliases: []string{"bm"}, + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "List all bare metal servers.", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + list, meta, err := o.list() + if err != nil { + printer.Error(fmt.Errorf("error retrieving bare metal list : %v", err)) + os.Exit(1) + } + data := &BareMetalsPrinter{BareMetals: list, Meta: meta} + o.Base.Printer.Display(data, err) + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "Get a bare metal server by ID", + Aliases: []string{"l"}, + Long: getLong, + Example: getExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + bm, err := o.get() + if err != nil { + printer.Error(fmt.Errorf("error retrieving bare metal : %v", err)) + os.Exit(1) + } + data := &BareMetalPrinter{BareMetal: *bm} + o.Base.Printer.Display(data, err) + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "Create a bare metal server", + Aliases: []string{"c"}, + Long: createLong, + Example: createExample, + Run: func(cmd *cobra.Command, args []string) { + o.CreateReq = parseCreateFlags(cmd) + bm, err := o.create() + if err != nil { + printer.Error(fmt.Errorf("error with bare metal create : %v", err)) + os.Exit(1) + } + + data := &BareMetalPrinter{BareMetal: *bm} + o.Base.Printer.Display(data, err) + }, + } + + create.Flags().StringP("region", "r", "", "ID of the region where the server will be created.") + create.Flags().StringP("plan", "p", "", "ID of the plan that the server will subscribe to.") + create.Flags().Int("os", 0, "ID of the operating system that will be installed on the server.") + create.Flags().StringP( + "script", + "s", + "", + "(optional) ID of the startup script that will run after the server is created.", + ) + create.Flags().StringP( + "snapshot", + "", + "", + "(optional) ID of the snapshot that the server will be restored from.", + ) + create.Flags().StringP( + "ipv6", + "i", + "", + "(optional) Whether IPv6 is enabled on the server. Possible values: 'yes', 'no'. Defaults to 'no'.", + ) + create.Flags().StringP("label", "l", "", "(optional) The label to assign to the server.") + create.Flags().StringSliceP( + "ssh", + "k", + []string{}, + "(optional) Comma separated list of SSH key IDs that will be added to the server.", + ) + create.Flags().IntP( + "app", + "a", + 0, + "(optional) ID of the application that will be installed on the server.", + ) + create.Flags().StringP("image", "", "", "(optional) Image ID of the application that will be installed on the server.") + create.Flags().StringP( + "userdata", + "u", + "", + "(optional) A generic data store, which some provisioning tools and cloud operating systems use as a configuration file.", + ) + create.Flags().StringP( + "notify", + "n", + "", + "(optional) Whether an activation email will be sent when the server is ready. Possible values: 'yes', 'no'. Defaults to 'yes'.", + ) + create.Flags().StringP("hostname", "m", "", "(optional) The hostname to assign to the server.") + create.Flags().StringP("tag", "t", "", "Deprecated: use `tags` instead. (optional) The tag to assign to the server.") + create.Flags().StringSliceP("tags", "", []string{}, "(optional) A comma separated list of tags to assign to the server.") + create.Flags().StringP("ripv4", "v", "", "(optional) IP address of the floating IP to use as the main IP of this server.") + create.Flags().BoolP("persistent_pxe", "x", false, "enable persistent_pxe | true or false") + + if err := create.MarkFlagRequired("region"); err != nil { + printer.Error(fmt.Errorf("error marking bare metal create 'region' flag required: %v", err)) + os.Exit(1) + } + + if err := create.MarkFlagRequired("plan"); err != nil { + printer.Error(fmt.Errorf("error marking bare metal create 'plan' flag required: %v", err)) + os.Exit(1) + } + + installFlags := []string{"app", "snapshot", "os", "image"} + create.MarkFlagsMutuallyExclusive(installFlags...) + create.MarkFlagsOneRequired(installFlags...) + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "Delete a bare metal server", + Aliases: []string{"destroy"}, + Long: deleteLong, + Example: deleteExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.del(); err != nil { + printer.Error(fmt.Errorf("error deleting bare metal : %v", err)) + os.Exit(1) + } + o.Base.Printer.Display(printer.Info("bare metal server has been deleted"), nil) + }, + } + + // Halt + halt := &cobra.Command{ + Use: "halt ", + Short: "Halt a bare metal server.", + Aliases: []string{"h"}, + Long: haltLong, + Example: haltExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.halt(); err != nil { + printer.Error(fmt.Errorf("error halting bare metal : %v", err)) + os.Exit(1) + } + o.Base.Printer.Display(printer.Info("bare metal server has been halted"), nil) + }, + } + + // Start + start := &cobra.Command{ + Use: "start ", + Short: "Start a bare metal server.", + Long: startLong, + Example: startExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.start(); err != nil { + printer.Error(fmt.Errorf("error starting bare metal : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("bare metal server has been started"), nil) + }, + } + + // Reboot + reboot := &cobra.Command{ + Use: "reboot ", + Short: "Reboot a bare metal server.", + Aliases: []string{"r"}, + Long: rebootLong, + Example: rebootExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.reboot(); err != nil { + printer.Error(fmt.Errorf("error rebooting bare metal : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("bare metal server has been rebooted"), nil) + }, + } + + // Reinstall + reinstall := &cobra.Command{ + Use: "reinstall ", + Short: "Reinstall the operating system on a bare metal server.", + Long: reinstallLong, + Example: reinstallExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.reinstall(); err != nil { + printer.Error(fmt.Errorf("error reinstalling bare metal : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("bare metal server has initiated reinstallation"), nil) + }, + } + + // Application + application := &cobra.Command{ + Use: "app", + Short: "app is used to access bare metal server application commands", + Aliases: []string{"a", "application"}, + Long: applicationLong, + Example: applicationExample, + } + + // Application Change + applicationChange := &cobra.Command{ + Use: "change ", + Short: "Change a bare metal server application", + Aliases: []string{"c"}, + Long: applicationChangeLong, + Example: applicationChangeExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + appID, err := cmd.Flags().GetInt("app") + if err != nil { + printer.Error(fmt.Errorf("error parsing app flag for bare metal app ID change : %v", err)) + os.Exit(1) + } + + o.UpdateReq = &govultr.BareMetalUpdate{ + AppID: appID, + } + + bm, err := o.update() + if err != nil { + printer.Error(fmt.Errorf("error with bare metal update : %v", err)) + os.Exit(1) + } + + data := &BareMetalPrinter{BareMetal: *bm} + o.Base.Printer.Display(data, err) + }, + } + + applicationChange.Flags().IntP( + "app", + "a", + 0, + "ID of the application that will be installed on the server", + ) + + if err := applicationChange.MarkFlagRequired("app"); err != nil { + printer.Error(fmt.Errorf("error marking bare metal 'app' flag required : %v", err)) + os.Exit(1) + } + + // Application List + applicationList := &cobra.Command{ + Use: "list ", + Short: "Available apps for a bare metal server", + Long: applicationListLong, + Example: applicationListExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + upgrades, err := o.getUpgrades() + if err != nil { + printer.Error(fmt.Errorf("error with bare metal get upgrades : %v", err)) + os.Exit(1) + } + data := &applications.ApplicationsPrinter{Applications: upgrades.Applications} + o.Base.Printer.Display(data, err) + }, + } + + application.AddCommand(applicationChange, applicationList) + + // Image + image := &cobra.Command{ + Use: "image", + Short: "image is used to access bare metal server image commands", + Aliases: []string{"i"}, + Long: imageLong, + Example: imageExample, + } + + // Image Change + imageChange := &cobra.Command{ + Use: "change ", + Short: "Change a bare metal server's image", + Aliases: []string{"c"}, + Long: imageChangeLong, + Example: imageChangeExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + imageID, err := cmd.Flags().GetString("image") + if err != nil { + printer.Error(fmt.Errorf("error parsing image flag for bare metal image change : %v", err)) + os.Exit(1) + } + + o.UpdateReq = &govultr.BareMetalUpdate{ + ImageID: imageID, + } + + bm, err := o.update() + if err != nil { + printer.Error(fmt.Errorf("error with bare metal image update : %v", err)) + os.Exit(1) + } + + data := &BareMetalPrinter{BareMetal: *bm} + o.Base.Printer.Display(data, err) + }, + } + + imageChange.Flags().StringP( + "image", + "i", + "", + "ID of the image that will be installed on the server", + ) + + if err := imageChange.MarkFlagRequired("image"); err != nil { + printer.Error(fmt.Errorf("error marking bare metal 'image' flag required : %v", err)) + os.Exit(1) + } + + image.AddCommand(imageChange) + + // OS + operatingSystem := &cobra.Command{ + Use: "os", + Short: "Server operating system commands", + Aliases: []string{"o"}, + Long: operatingSystemLong, + Example: operatingSystemExample, + } + + // OS Change + operatingSystemChange := &cobra.Command{ + Use: "change ", + Short: "Change a bare metal server's image", + Aliases: []string{"c"}, + Long: operatingSystemChangeLong, + Example: operatingSystemChangeExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + operatingSystemID, err := cmd.Flags().GetInt("os") + if err != nil { + printer.Error(fmt.Errorf("error parsing os flag for bare metal os change : %v", err)) + os.Exit(1) + } + + o.UpdateReq = &govultr.BareMetalUpdate{ + OsID: operatingSystemID, + } + + bm, err := o.update() + if err != nil { + printer.Error(fmt.Errorf("error with bare metal os update : %v", err)) + os.Exit(1) + } + + data := &BareMetalPrinter{BareMetal: *bm} + o.Base.Printer.Display(data, err) + }, + } + + operatingSystemChange.Flags().IntP( + "os", + "o", + 0, + "ID of the operating system that will be installed on the server", + ) + if err := operatingSystemChange.MarkFlagRequired("os"); err != nil { + printer.Error(fmt.Errorf("error marking bare metal 'os' flag required : %v", err)) + os.Exit(1) + } + + // Operating System List + operatingSystemList := &cobra.Command{ + Use: "list ", + Short: "Available operating systems for a bare metal server", + Long: operatingSystemListLong, + Example: operatingSystemListExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + upgrades, err := o.getUpgrades() + if err != nil { + printer.Error(fmt.Errorf("error with bare metal get upgrades : %v", err)) + os.Exit(1) + } + + data := &operatingsystems.OSPrinter{OperatingSystems: upgrades.OS} + o.Base.Printer.Display(data, nil) + }, + } + + operatingSystem.AddCommand(operatingSystemChange, operatingSystemList) + + // User Data + userData := &cobra.Command{ + Use: "user-data", + Short: "Commands for bare metal server user data", + Aliases: []string{"u"}, + Long: userDataLong, + Example: userDataExample, + } + + // User Data Get + userDataGet := &cobra.Command{ + Use: "get ", + Short: "Get the user data of a bare metal server", + Aliases: []string{"g"}, + Long: userDataGetLong, + Example: userDataGetExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + ud, err := o.getUserData() + if err != nil { + printer.Error(fmt.Errorf("error with bare metal get user data : %v", err)) + os.Exit(1) + } + + data := &userdata.UserDataPrinter{UserData: *ud} + o.Base.Printer.Display(data, nil) + }, + } + + // User Data Set + userDataSet := &cobra.Command{ + Use: "set ", + Short: "Set the plain text user-data of a bare metal server", + Long: userDataSetLong, + Example: userDataSetExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + path, err := cmd.Flags().GetString("user-data") + if err != nil { + printer.Error(fmt.Errorf("error parsing user-data flag for bare metal user data set : %v", err)) + os.Exit(1) + } + + rawData, err := os.ReadFile(filepath.Clean(path)) + if err != nil { + printer.Error(fmt.Errorf("error reading user-data : %v", err)) + os.Exit(1) + } + + o.UpdateReq = &govultr.BareMetalUpdate{ + UserData: base64.StdEncoding.EncodeToString(rawData), + } + + _, errUpdate := o.update() + if err != nil { + printer.Error(fmt.Errorf("error updating bare metal user-data : %v", errUpdate)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("bare metal server user data has been set"), nil) + }, + } + + userDataSet.Flags().StringP("user-data", "d", "/dev/stdin", "file to read userdata from") + if err := userDataSet.MarkFlagRequired("user-data"); err != nil { + printer.Error(fmt.Errorf("error marking bare metal 'user-data' flag required: %v", err)) + os.Exit(1) + } + + userData.AddCommand(userDataGet, userDataSet) + + // VNC URL + vnc := &cobra.Command{ + Use: "vnc ", + Short: "get a bare metal server's VNC url", + Long: vncLong, + Example: vncExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + vnc, err := o.getVNCURL() + if err != nil { + printer.Error(fmt.Errorf("error retrieving bare metal VNC URL : %v", err)) + os.Exit(1) + } + + data := &BareMetalVNCPrinter{VNC: *vnc} + o.Base.Printer.Display(data, nil) + }, + } + + // Bandwidth + bandwidth := &cobra.Command{ + Use: "bandwidth ", + Short: "Get a bare metal server's bandwidth usage", + Aliases: []string{"b"}, + Long: bandwidthLong, + Example: bandwidthExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + bw, err := o.getBandwidth() + if err != nil { + printer.Error(fmt.Errorf("error retrieving bare metal bandwidth usage : %v", err)) + os.Exit(1) + } + + data := &BareMetalBandwidthPrinter{Bandwidth: *bw} + o.Base.Printer.Display(data, nil) + }, + } + + // Tags + tags := &cobra.Command{ + Use: "tags ", + Short: "add or modify tags on the bare metal server.", + Long: tagsLong, + Example: tagsExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + tags, _ := cmd.Flags().GetStringSlice("tags") + o.UpdateReq = &govultr.BareMetalUpdate{ + Tags: tags, + } + + _, err := o.update() + if err != nil { + printer.Error(fmt.Errorf("error updating bare metal tags : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("bare metal server tags have been updated"), nil) + }, + } + + tags.Flags().StringSliceP("tags", "t", []string{}, "A comma separated list of tags to apply to the server") + if err := tags.MarkFlagRequired("tags"); err != nil { + printer.Error(fmt.Errorf("error marking bare metal 'tags' flag required: %v", err)) + os.Exit(1) + } + + // IPv4 Addresses + ipv4 := &cobra.Command{ + Use: "ipv4 ", + Short: "List the IPv4 information of a bare metal server", + Long: ipv4Long, + Example: ipv4Example, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + ipv4, meta, err := o.getIPv4Addresses() + if err != nil { + printer.Error(fmt.Errorf("error retrieving bare metal IPv4 information : %v", err)) + os.Exit(1) + } + data := &ip.IPv4sPrinter{IPv4s: ipv4, Meta: meta} + o.Base.Printer.Display(data, nil) + }, + } + + // IPv6 Addresses + ipv6 := &cobra.Command{ + Use: "ipv6 ", + Short: "list the IPv6 information of a bare metal server.", + Long: ipv6Long, + Example: ipv6Example, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + ipv6, meta, err := o.getIPv6Addresses() + if err != nil { + printer.Error(fmt.Errorf("error retrieving bare metal IPv6 information : %v", err)) + os.Exit(1) + } + data := &ip.IPv6sPrinter{IPv6s: ipv6, Meta: meta} + o.Base.Printer.Display(data, nil) + }, + } + + // VPC2 + vpc2 := &cobra.Command{ + Use: "vpc2", + Short: "commands to manage vpc 2.0 on bare metal servers", + Long: vpc2Long, + } + + // VPC2 List + vpc2List := &cobra.Command{ + Use: "list ", + Short: "List all VPC2 networks attached to a server", + Aliases: []string{"l"}, + Long: vpc2ListLong, + Example: vpc2ListExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + vpc2s, err := o.vpc2NetworksList() + if err != nil { + printer.Error(fmt.Errorf("error retrieving bare metal vpc2 information : %v", err)) + os.Exit(1) + } + data := &BareMetalVPC2sPrinter{VPC2s: vpc2s} + o.Base.Printer.Display(data, nil) + }, + } + + // VPC2 Attach + vpc2Attach := &cobra.Command{ + Use: "attach ", + Short: "Attach a VPC2 network to a server", + Long: vpc2AttachLong, + Example: vpc2AttachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + vpcID, errID := cmd.Flags().GetString("vpc-id") + if errID != nil { + printer.Error(fmt.Errorf("error parsing vpc-id flag for bare metal VPC2 attach : %v", errID)) + os.Exit(1) + } + + IPAddress, errIP := cmd.Flags().GetString("ip-address") + if errIP != nil { + printer.Error(fmt.Errorf("error parsing ip-address flag for bare metal VPC2 attach : %v", errIP)) + os.Exit(1) + } + + o.VPC2Req = &govultr.AttachVPC2Req{ + VPCID: vpcID, + IPAddress: &IPAddress, + } + + if err := o.vpc2NetworksAttach(); err != nil { + printer.Error(fmt.Errorf("error attaching bare metal to VPC2 : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("bare metal server has been attached to VPC2 network"), nil) + }, + } + + vpc2Attach.Flags().StringP("vpc-id", "v", "", "the ID of the VPC 2.0 network you wish to attach") + vpc2Attach.Flags().StringP("ip-address", "i", "", "the IP address to use for this server on the attached VPC 2.0 network") + if errVPC := vpc2Attach.MarkFlagRequired("vpc-id"); errVPC != nil { + printer.Error(fmt.Errorf("error marking bare metal 'vpc-id' flag required for attach : %v", errVPC)) + os.Exit(1) + } + + // VPC2 Detach + vpc2Detach := &cobra.Command{ + Use: "detach ", + Short: "Detach a VPC2 network from a server", + Long: vpc2DetachLong, + Example: vpc2DetachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a bare metal ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + vpcID, errID := cmd.Flags().GetString("vpc-id") + if errID != nil { + printer.Error(fmt.Errorf("error parsing vpc-id flag for bare metal VPC2 detach : %v", errID)) + os.Exit(1) + } + + o.VPC2ID = vpcID + if err := o.vpc2NetworksDetach(); err != nil { + printer.Error(fmt.Errorf("error detaching bare metal VPC2 : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("bare metal server has been detached from VPC2 network"), nil) + }, + } + + vpc2Detach.Flags().StringP("vpc-id", "v", "", "the ID of the VPC 2.0 network you wish to detach") + if errVPC2 := vpc2Detach.MarkFlagRequired("vpc-id"); errVPC2 != nil { + printer.Error(fmt.Errorf("error marking bare metal 'vpc-id' flag required for detach : %v", errVPC2)) + os.Exit(1) + } + + vpc2.AddCommand(vpc2List, vpc2Attach, vpc2Detach) + + cmd.AddCommand( + get, + list, + create, + del, + halt, + start, + reboot, + reinstall, + application, + image, + operatingSystem, + userData, + vnc, + bandwidth, + tags, + ipv4, + ipv6, + vpc2, + ) + + return cmd +} + +type options struct { + Base *cli.Base + CreateReq *govultr.BareMetalCreate + UpdateReq *govultr.BareMetalUpdate + VPC2Req *govultr.AttachVPC2Req + VPC2ID string +} + +func (b *options) list() ([]govultr.BareMetalServer, *govultr.Meta, error) { + bms, meta, _, err := b.Base.Client.BareMetalServer.List(b.Base.Context, b.Base.Options) + return bms, meta, err +} + +func (b *options) get() (*govultr.BareMetalServer, error) { + bm, _, err := b.Base.Client.BareMetalServer.Get(b.Base.Context, b.Base.Args[0]) + return bm, err +} + +func (b *options) create() (*govultr.BareMetalServer, error) { + bm, _, err := b.Base.Client.BareMetalServer.Create(b.Base.Context, b.CreateReq) + return bm, err +} + +func (b *options) update() (*govultr.BareMetalServer, error) { + bm, _, err := b.Base.Client.BareMetalServer.Update(b.Base.Context, b.Base.Args[0], b.UpdateReq) + return bm, err +} + +func (b *options) del() error { + return b.Base.Client.BareMetalServer.Delete(b.Base.Context, b.Base.Args[0]) +} + +func (b *options) halt() error { + return b.Base.Client.BareMetalServer.Halt(b.Base.Context, b.Base.Args[0]) +} + +func (b *options) start() error { + return b.Base.Client.BareMetalServer.Start(b.Base.Context, b.Base.Args[0]) +} + +func (b *options) reboot() error { + return b.Base.Client.BareMetalServer.Reboot(b.Base.Context, b.Base.Args[0]) +} + +func (b *options) reinstall() error { + _, _, err := b.Base.Client.BareMetalServer.Reinstall(b.Base.Context, b.Base.Args[0]) + return err +} + +func (b *options) getUpgrades() (*govultr.Upgrades, error) { + list, _, err := b.Base.Client.BareMetalServer.GetUpgrades(b.Base.Context, b.Base.Args[0]) + return list, err +} + +func (b *options) getUserData() (*govultr.UserData, error) { + ud, _, err := b.Base.Client.BareMetalServer.GetUserData(b.Base.Context, b.Base.Args[0]) + return ud, err +} + +func (b *options) getVNCURL() (*govultr.VNCUrl, error) { + url, _, err := b.Base.Client.BareMetalServer.GetVNCUrl(b.Base.Context, b.Base.Args[0]) + return url, err +} + +func (b *options) getBandwidth() (*govultr.Bandwidth, error) { + bw, _, err := b.Base.Client.BareMetalServer.GetBandwidth(b.Base.Context, b.Base.Args[0]) + return bw, err +} + +func (b *options) getIPv4Addresses() ([]govultr.IPv4, *govultr.Meta, error) { + ips, meta, _, err := b.Base.Client.BareMetalServer.ListIPv4s(b.Base.Context, b.Base.Args[0], b.Base.Options) + return ips, meta, err +} + +func (b *options) getIPv6Addresses() ([]govultr.IPv6, *govultr.Meta, error) { + ips, meta, _, err := b.Base.Client.BareMetalServer.ListIPv6s(b.Base.Context, b.Base.Args[0], b.Base.Options) + return ips, meta, err +} + +func (b *options) vpc2NetworksList() ([]govultr.VPC2Info, error) { + vpc2s, _, err := b.Base.Client.BareMetalServer.ListVPC2Info(b.Base.Context, b.Base.Args[0]) + return vpc2s, err +} + +func (b *options) vpc2NetworksAttach() error { + return b.Base.Client.BareMetalServer.AttachVPC2(b.Base.Context, b.Base.Args[0], b.VPC2Req) +} + +func (b *options) vpc2NetworksDetach() error { + return b.Base.Client.BareMetalServer.DetachVPC2(b.Base.Context, b.Base.Args[0], b.VPC2ID) +} + +// ============================ + +func parseCreateFlags(cmd *cobra.Command) *govultr.BareMetalCreate { //nolint:funlen,gocyclo + region, err := cmd.Flags().GetString("region") + if err != nil { + printer.Error(fmt.Errorf("error parsing region flag for bare metal create : %v", err)) + os.Exit(1) + } + + plan, err := cmd.Flags().GetString("plan") + if err != nil { + printer.Error(fmt.Errorf("error parsing plan flag for bare metal create : %v", err)) + os.Exit(1) + } + + osID, err := cmd.Flags().GetInt("os") + if err != nil { + printer.Error(fmt.Errorf("error parsing os flag for bare metal create : %v", err)) + os.Exit(1) + } + + script, err := cmd.Flags().GetString("script") + if err != nil { + printer.Error(fmt.Errorf("error parsing script flag bare metal create : %v", err)) + os.Exit(1) + } + + snapshot, err := cmd.Flags().GetString("snapshot") + if err != nil { + printer.Error(fmt.Errorf("error parsing snapshot flag for bare metal create : %v", err)) + os.Exit(1) + } + + ipv6, err := cmd.Flags().GetString("ipv6") + if err != nil { + printer.Error(fmt.Errorf("error parsing ipv6 flag for bare metal create : %v", err)) + os.Exit(1) + } + + label, err := cmd.Flags().GetString("label") + if err != nil { + printer.Error(fmt.Errorf("error parsing label flag for bare metal create : %v", err)) + os.Exit(1) + } + + sshKeys, err := cmd.Flags().GetStringSlice("ssh") + if err != nil { + printer.Error(fmt.Errorf("error parsing ssh flag for bare metal create : %v", err)) + os.Exit(1) + } + + app, err := cmd.Flags().GetInt("app") + if err != nil { + printer.Error(fmt.Errorf("error parsing app flag for bare metal create : %v", err)) + os.Exit(1) + } + + userdata, err := cmd.Flags().GetString("userdata") + if err != nil { + printer.Error(fmt.Errorf("error parsing userdata flag for bare metal create : %v", err)) + os.Exit(1) + } + + notify, err := cmd.Flags().GetString("notify") + if err != nil { + printer.Error(fmt.Errorf("error parsing notify flag for bare metal create : %v", err)) + os.Exit(1) + } + + hostname, err := cmd.Flags().GetString("hostname") + if err != nil { + printer.Error(fmt.Errorf("error parsing hostname flag for bare metal create : %v", err)) + os.Exit(1) + } + + tags, err := cmd.Flags().GetStringSlice("tags") + if err != nil { + printer.Error(fmt.Errorf("error parsing tags flag for bare metal create : %v", err)) + os.Exit(1) + } + + ripv4, err := cmd.Flags().GetString("ripv4") + if err != nil { + printer.Error(fmt.Errorf("error parsing ripv4 flag for bare metal create : %v", err)) + os.Exit(1) + } + + pxe, err := cmd.Flags().GetBool("persistenterrpxe") + if err != nil { + printer.Error(fmt.Errorf("error parsing persistenterrpxe flag for bare metal create : %v", err)) + os.Exit(1) + } + + image, err := cmd.Flags().GetString("image") + if err != nil { + printer.Error(fmt.Errorf("error parsing image flag for bare metal create : %v", err)) + os.Exit(1) + } + + options := &govultr.BareMetalCreate{ + StartupScriptID: script, + Plan: plan, + SnapshotID: snapshot, + AppID: app, + OsID: osID, + ImageID: image, + Label: label, + SSHKeyIDs: sshKeys, + Hostname: hostname, + Tags: tags, + ReservedIPv4: ripv4, + Region: region, + PersistentPxe: govultr.BoolToBoolPtr(pxe), + } + if userdata != "" { + options.UserData = base64.StdEncoding.EncodeToString([]byte(userdata)) + } + + if notify == "yes" { + options.ActivationEmail = govultr.BoolToBoolPtr(true) + } + + if ipv6 == "yes" { + options.EnableIPv6 = govultr.BoolToBoolPtr(true) + } + + return options +} + +func parseUpdateFlags(cmd *cobra.Command) *govultr.BareMetalUpdate { //nolint:unused + osID, err := cmd.Flags().GetInt("os") + if err != nil { + printer.Error(fmt.Errorf("error parsing os flag for bare metal update : %v", err)) + os.Exit(1) + } + + label, err := cmd.Flags().GetString("label") + if err != nil { + printer.Error(fmt.Errorf("error parsing label flag for bare metal update : %v", err)) + os.Exit(1) + } + + app, err := cmd.Flags().GetInt("app") + if err != nil { + printer.Error(fmt.Errorf("error parsing app flag for bare metal update : %v", err)) + os.Exit(1) + } + + userdata, err := cmd.Flags().GetString("userdata") + if err != nil { + printer.Error(fmt.Errorf("error parsing userdata flag for bare metal update : %v", err)) + os.Exit(1) + } + + tags, err := cmd.Flags().GetStringSlice("tags") + if err != nil { + printer.Error(fmt.Errorf("error parsing tags flag for bare metal update : %v", err)) + os.Exit(1) + } + + image, err := cmd.Flags().GetString("image") + if err != nil { + printer.Error(fmt.Errorf("error parsing image flag for bare metal update : %v", err)) + os.Exit(1) + } + + options := &govultr.BareMetalUpdate{ + AppID: app, + OsID: osID, + ImageID: image, + Label: label, + Tags: tags, + } + if userdata != "" { + options.UserData = base64.StdEncoding.EncodeToString([]byte(userdata)) + } + + return options +} diff --git a/cmd/baremetal/printer.go b/cmd/baremetal/printer.go new file mode 100644 index 00000000..1a67c367 --- /dev/null +++ b/cmd/baremetal/printer.go @@ -0,0 +1,262 @@ +package baremetal + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// BareMetalsPrinter ... +type BareMetalsPrinter struct { + BareMetals []govultr.BareMetalServer `json:"bare_metals"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (b *BareMetalsPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BareMetalsPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BareMetalsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "IP", + "TAG", + "MAC ADDRESS", + "LABEL", + "OS", + "STATUS", + "REGION", + "CPU", + "RAM", + "DISK", + "FEATURES", + "TAGS", + }} +} + +// Data ... +func (b *BareMetalsPrinter) Data() [][]string { + var data [][]string + for i := range b.BareMetals { + data = append(data, []string{ + b.BareMetals[i].ID, + b.BareMetals[i].MainIP, + b.BareMetals[i].Tag, //nolint: staticcheck + strconv.Itoa(b.BareMetals[i].MacAddress), + b.BareMetals[i].Label, + b.BareMetals[i].Os, + b.BareMetals[i].Status, + b.BareMetals[i].Region, + strconv.Itoa(b.BareMetals[i].CPUCount), + b.BareMetals[i].RAM, + b.BareMetals[i].Disk, + printer.ArrayOfStringsToString(b.BareMetals[i].Features), + printer.ArrayOfStringsToString(b.BareMetals[i].Tags), + }) + } + return data +} + +// Paging ... +func (b *BareMetalsPrinter) Paging() [][]string { + return printer.NewPaging(b.Meta.Total, &b.Meta.Links.Next, &b.Meta.Links.Prev).Compose() +} + +// ====================================== + +// BareMetalPrinter ... +type BareMetalPrinter struct { + BareMetal govultr.BareMetalServer `json:"bare_metal"` +} + +// JSON ... +func (b *BareMetalPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BareMetalPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BareMetalPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "IP", + "TAG", + "MAC ADDRESS", + "LABEL", + "OS", + "STATUS", + "REGION", + "CPU", + "RAM", + "DISK", + "FEATURES", + "TAGS", + }} +} + +// Data ... +func (b *BareMetalPrinter) Data() [][]string { + return [][]string{0: { + b.BareMetal.ID, + b.BareMetal.MainIP, + b.BareMetal.Tag, //nolint: staticcheck + strconv.Itoa(b.BareMetal.MacAddress), + b.BareMetal.Label, + b.BareMetal.Os, + b.BareMetal.Status, + b.BareMetal.Region, + strconv.Itoa(b.BareMetal.CPUCount), + b.BareMetal.RAM, + b.BareMetal.Disk, + printer.ArrayOfStringsToString(b.BareMetal.Features), + printer.ArrayOfStringsToString(b.BareMetal.Tags), + }} +} + +// Paging ... +func (b *BareMetalPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// BareMetalVNCPrinter ... +type BareMetalVNCPrinter struct { + VNC govultr.VNCUrl `json:"vnc"` +} + +// JSON ... +func (b *BareMetalVNCPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BareMetalVNCPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BareMetalVNCPrinter) Columns() [][]string { + return [][]string{0: { + "URL", + }} +} + +// Data ... +func (b *BareMetalVNCPrinter) Data() [][]string { + return [][]string{0: { + b.VNC.URL, + }} +} + +// Paging ... +func (b *BareMetalVNCPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// BareMetalBandwidthPrinter ... +type BareMetalBandwidthPrinter struct { + Bandwidth govultr.Bandwidth `json:"all_bandwidth"` +} + +// JSON ... +func (b *BareMetalBandwidthPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BareMetalBandwidthPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BareMetalBandwidthPrinter) Columns() [][]string { + return [][]string{0: { + "DATE", + "INCOMING BYTES", + "OUTGOING BYTES", + }} +} + +// Data ... +func (b *BareMetalBandwidthPrinter) Data() [][]string { + var data [][]string + for k := range b.Bandwidth.Bandwidth { + data = append(data, []string{ + k, + strconv.Itoa(b.Bandwidth.Bandwidth[k].IncomingBytes), + strconv.Itoa(b.Bandwidth.Bandwidth[k].OutgoingBytes), + }) + } + + return data +} + +// Paging ... +func (b *BareMetalBandwidthPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// BareMetalVPC2sPrinter ... +type BareMetalVPC2sPrinter struct { + VPC2s []govultr.VPC2Info `json:"vpcs"` +} + +// JSON ... +func (b *BareMetalVPC2sPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BareMetalVPC2sPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BareMetalVPC2sPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "MAC ADDRESS", + "IP ADDRESS", + }} +} + +// Data ... +func (b *BareMetalVPC2sPrinter) Data() [][]string { + var data [][]string + + if len(b.VPC2s) == 0 { + return [][]string{0: {"---", "---", "---"}} + } + + for i := range b.VPC2s { + data = append(data, []string{ + b.VPC2s[i].ID, + b.VPC2s[i].MacAddress, + b.VPC2s[i].IPAddress, + }) + } + + return data +} + +// Paging ... +func (b *BareMetalVPC2sPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/billing/billing.go b/cmd/billing/billing.go new file mode 100644 index 00000000..f618721e --- /dev/null +++ b/cmd/billing/billing.go @@ -0,0 +1,275 @@ +// Package billing provides the billing commands for the CLI +package billing + +import ( + "errors" + "fmt" + "os" + "strconv" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Get all available commands for billing` + example = ` + # Full example + vultr-cli billing + ` + + historyLong = `Get all available commands for billing history` + historyExample = ` + # Full example + vultr-cli billing history + + # Shortened with alias commands + vultr-cli billing h + ` + + historyListLong = `Retrieve a list of all billing history on your account` + historyListExample = ` + # Full example + vultr-cli billing history list + + # Shortened with alias commands + vultr-cli billing h l + ` + + invoicesLong = `Get all available commands for billing invoices` + invoicesExample = ` + # Full example + vultr-cli billing invoice + + # Shortened with alias commands + vultr-cli billing i + ` + + invoiceListLong = `Retrieve a list of all invoices on your account` + invoiceListExample = ` + # Full example + vultr-cli billing invoice list + + # Shortened with alias commands + vultr-cli billing i l + ` + + invoiceGetLong = `Get a specific invoice on your account` + invoiceGetExample = ` + # Full example + vultr-cli billing invoice get 123456 + + # Shortened with alias commands + vultr-cli billing i g 123456 + ` + + invoiceItemsListLong = `Retrieve a list of invoice items from a specific invoice on your account` + invoiceItemsListExample = ` + # Full example + vultr-cli billing invoice items 123456 + + # Shortened with alias commands + vultr-cli billing i i 123456 + ` +) + +func NewCmdBilling(base *cli.Base) *cobra.Command { + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "billing", + Short: "display billing information", + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // Invoice + invoice := &cobra.Command{ + Use: "invoice", + Aliases: []string{"i"}, + Short: "display invoice information", + Long: invoicesLong, + Example: invoicesExample, + } + + // Invoice List + invoicesList := &cobra.Command{ + Use: "list", + Short: "list billing invoices", + Aliases: []string{"l"}, + Long: invoiceListLong, + Example: invoiceListExample, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + invs, meta, err := o.listInvoices() + if err != nil { + printer.Error(fmt.Errorf("error retrieving billing invoice list : %v", err)) + os.Exit(1) + } + data := &BillingInvoicesPrinter{Invoices: invs, Meta: meta} + o.Base.Printer.Display(data, err) + }, + } + + invoicesList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + invoicesList.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + "(optional) Number of items requested per page. Default is 100 and Max is 500.", + ) + + // Invoice Get + invoiceGet := &cobra.Command{ + Use: "get", + Short: "get invoice", + Aliases: []string{"g"}, + Long: invoiceGetLong, + Example: invoiceGetExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an invoice ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + inv, err := o.get() + if err != nil { + printer.Error(fmt.Errorf("error getting invoice : %v", err)) + os.Exit(1) + } + + data := &BillingInvoicePrinter{Invoice: *inv} + o.Base.Printer.Display(data, err) + }, + } + + // Invoice Items List + invoiceItemsList := &cobra.Command{ + Use: "items ", + Short: "list invoice items", + Aliases: []string{"i"}, + Long: invoiceItemsListLong, + Example: invoiceItemsListExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an invoice ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + id, errConv := strconv.Atoi(args[0]) + if errConv != nil { + printer.Error(fmt.Errorf("error converting invoice item id : %v", errConv)) + os.Exit(1) + } + + o.InvoiceItemID = id + + items, meta, err := o.listInvoiceItems() + if err != nil { + printer.Error(fmt.Errorf("error retrieving billing invoice item list : %v", err)) + os.Exit(1) + } + data := &BillingInvoiceItemsPrinter{InvoiceItems: items, Meta: meta} + o.Base.Printer.Display(data, err) + }, + } + + invoiceItemsList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + invoiceItemsList.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + invoice.AddCommand( + invoicesList, + invoiceGet, + invoiceItemsList, + ) + + // History + history := &cobra.Command{ + Use: "history", + Aliases: []string{"h"}, + Short: "display billing history information", + Long: historyLong, + Example: historyExample, + } + + // History List + historyList := &cobra.Command{ + Use: "list", + Short: "list billing history", + Aliases: []string{"l"}, + Long: historyListLong, + Example: historyListExample, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + hs, meta, err := o.listHistory() + if err != nil { + printer.Error(fmt.Errorf("error retrieving billing history list : %v", err)) + os.Exit(1) + } + data := &BillingHistoryPrinter{Billing: hs, Meta: meta} + o.Base.Printer.Display(data, err) + }, + } + + historyList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + historyList.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + "(optional) Number of items requested per page. Default is 100 and Max is 500.", + ) + + history.AddCommand( + historyList, + ) + + cmd.AddCommand( + history, + invoice, + ) + + return cmd +} + +type options struct { + Base *cli.Base + InvoiceItemID int +} + +func (b *options) listHistory() ([]govultr.History, *govultr.Meta, error) { + hs, meta, _, err := b.Base.Client.Billing.ListHistory(b.Base.Context, b.Base.Options) + return hs, meta, err +} + +func (b *options) get() (*govultr.Invoice, error) { + inv, _, err := b.Base.Client.Billing.GetInvoice(b.Base.Context, b.Base.Args[0]) + return inv, err +} + +func (b *options) listInvoices() ([]govultr.Invoice, *govultr.Meta, error) { + invs, meta, _, err := b.Base.Client.Billing.ListInvoices(b.Base.Context, b.Base.Options) + return invs, meta, err +} + +func (b *options) listInvoiceItems() ([]govultr.InvoiceItem, *govultr.Meta, error) { + items, meta, _, err := b.Base.Client.Billing.ListInvoiceItems(b.Base.Context, b.InvoiceItemID, b.Base.Options) + return items, meta, err +} diff --git a/cmd/billing/printer.go b/cmd/billing/printer.go new file mode 100644 index 00000000..fdb9a030 --- /dev/null +++ b/cmd/billing/printer.go @@ -0,0 +1,221 @@ +// Package billing provides the account billing operations and +// functionality for the CLI +package billing + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" +) + +// BillingHistoryPrinter ... +type BillingHistoryPrinter struct { + Billing []govultr.History `json:"billing_history"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (b *BillingHistoryPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BillingHistoryPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BillingHistoryPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "DATE", + "TYPE", + "DESCRIPTION", + "AMOUNT", + "BALANCE", + }} +} + +// Data ... +func (b *BillingHistoryPrinter) Data() [][]string { + if len(b.Billing) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range b.Billing { + data = append(data, []string{ + strconv.Itoa(b.Billing[i].ID), + b.Billing[i].Date, + b.Billing[i].Type, + b.Billing[i].Description, + strconv.FormatFloat(float64(b.Billing[i].Amount), 'f', utils.DecimalPrecision, 32), + strconv.FormatFloat(float64(b.Billing[i].Balance), 'f', utils.DecimalPrecision, 32), + }) + } + return data +} + +// Paging ... +func (b *BillingHistoryPrinter) Paging() [][]string { + return printer.NewPaging(b.Meta.Total, &b.Meta.Links.Next, &b.Meta.Links.Prev).Compose() +} + +// ====================================== + +// BillingInvoicesPrinter ... +type BillingInvoicesPrinter struct { + Invoices []govultr.Invoice `json:"billing_invoices"` + Meta *govultr.Meta +} + +// JSON ... +func (b *BillingInvoicesPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BillingInvoicesPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BillingInvoicesPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "DATE", + "DESCRIPTION", + "AMOUNT", + "BALANCE", + }} +} + +// Data ... +func (b *BillingInvoicesPrinter) Data() [][]string { + if len(b.Invoices) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range b.Invoices { + data = append(data, []string{ + strconv.Itoa(b.Invoices[i].ID), + b.Invoices[i].Date, + b.Invoices[i].Description, + strconv.FormatFloat(float64(b.Invoices[i].Amount), 'f', utils.DecimalPrecision, 32), + strconv.FormatFloat(float64(b.Invoices[i].Balance), 'f', utils.DecimalPrecision, 32), + }) + } + return data +} + +// Paging ... +func (b *BillingInvoicesPrinter) Paging() [][]string { + return printer.NewPaging(b.Meta.Total, &b.Meta.Links.Next, &b.Meta.Links.Prev).Compose() +} + +// ====================================== + +// BillingInvoicePrinter ... +type BillingInvoicePrinter struct { + Invoice govultr.Invoice `json:"billing_invoice"` +} + +// JSON ... +func (b *BillingInvoicePrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BillingInvoicePrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BillingInvoicePrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "DATE", + "DESCRIPTION", + "AMOUNT", + "BALANCE", + }} +} + +// Data ... +func (b *BillingInvoicePrinter) Data() [][]string { + return [][]string{0: { + strconv.Itoa(b.Invoice.ID), + b.Invoice.Date, + b.Invoice.Description, + strconv.FormatFloat(float64(b.Invoice.Amount), 'f', utils.DecimalPrecision, 32), + strconv.FormatFloat(float64(b.Invoice.Balance), 'f', utils.DecimalPrecision, 32), + }} +} + +// Paging ... +func (b *BillingInvoicePrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// BillingInvoiceItemsPrinter ... +type BillingInvoiceItemsPrinter struct { + InvoiceItems []govultr.InvoiceItem `json:"invoice_items"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (b *BillingInvoiceItemsPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BillingInvoiceItemsPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BillingInvoiceItemsPrinter) Columns() [][]string { + return [][]string{0: { + "DESCRIPTION", + "PRODUCT", + "START DATE", + "END DATE", + "UNITS", + "UNIT TYPE", + "UNIT PRICE", + "TOTAL", + }} +} + +// Data ... +func (b *BillingInvoiceItemsPrinter) Data() [][]string { + if len(b.InvoiceItems) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range b.InvoiceItems { + data = append(data, []string{ + b.InvoiceItems[i].Description, + b.InvoiceItems[i].Product, + b.InvoiceItems[i].StartDate, + b.InvoiceItems[i].EndDate, + strconv.Itoa(b.InvoiceItems[i].Units), + b.InvoiceItems[i].UnitType, + strconv.FormatFloat(float64(b.InvoiceItems[i].UnitPrice), 'f', utils.DecimalPrecision, 32), + strconv.FormatFloat(float64(b.InvoiceItems[i].Total), 'f', utils.DecimalPrecision, 32), + }) + } + + return data +} + +// Paging ... +func (b *BillingInvoiceItemsPrinter) Paging() [][]string { + return printer.NewPaging(b.Meta.Total, &b.Meta.Links.Next, &b.Meta.Links.Prev).Compose() +} diff --git a/cmd/blockStorage.go b/cmd/blockStorage.go deleted file mode 100644 index 25e04fed..00000000 --- a/cmd/blockStorage.go +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// BlockStorageCmd represents the blockStorage command -func BlockStorageCmd() *cobra.Command { - - bsCmd := &cobra.Command{ - Use: "block-storage", - Aliases: []string{"bs"}, - Short: "block storage commands", - Long: `block-storage is used to interact with the block-storage api`, - } - - bsCmd.AddCommand(bsAttach, bsCreate, bsDelete, bsDetach, bsLabelSet, bsList, bsGet, bsResize) - - // List - bsList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - bsList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - // Attach - bsAttach.Flags().StringP("instance", "i", "", "instance id you want to attach to") - bsAttach.Flags().Bool("live", false, "attach Block Storage without restarting the Instance.") - bsAttach.MarkFlagRequired("instance") - - // Detach - bsDetach.Flags().Bool("live", false, "detach block storage from instance without a restart") - - // Create - bsCreate.Flags().StringP("region", "r", "", "regionID you want to create the block storage in") - bsCreate.MarkFlagRequired("region") - - bsCreate.Flags().IntP("size", "s", 0, "size of the block storage you want to create") - bsCreate.MarkFlagRequired("size") - - bsCreate.Flags().StringP("label", "l", "", "label you want to give the block storage") - - // Label - bsLabelSet.Flags().StringP("label", "l", "", "label you want your block storage to have") - bsLabelSet.MarkFlagRequired("label") - - // Resize - bsResize.Flags().IntP("size", "s", 0, "size you want your block storage to be") - bsResize.MarkFlagRequired("size") - - return bsCmd -} - -var bsAttach = &cobra.Command{ - Use: "attach ", - Short: "attaches a block storage to an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a blockStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - instance, _ := cmd.Flags().GetString("instance") - live, _ := cmd.Flags().GetBool("live") - - bsAttach := &govultr.BlockStorageAttach{ - InstanceID: instance, - Live: govultr.BoolToBoolPtr(live), - } - - if err := client.BlockStorage.Attach(context.Background(), id, bsAttach); err != nil { - fmt.Printf("error attaching block storage : %v\n", err) - os.Exit(1) - } - - fmt.Println("attached block storage") - }, -} - -var bsCreate = &cobra.Command{ - Use: "create", - Short: "create a new block storage", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - region, _ := cmd.Flags().GetString("region") - size, _ := cmd.Flags().GetInt("size") - label, _ := cmd.Flags().GetString("label") - - bsCreate := &govultr.BlockStorageCreate{ - Region: region, - SizeGB: size, - Label: label, - } - - bs, err := client.BlockStorage.Create(context.Background(), bsCreate) - if err != nil { - fmt.Printf("error creating block storage : %v\n", err) - os.Exit(1) - } - - printer.SingleBlockStorage(bs) - - }, -} - -var bsDelete = &cobra.Command{ - Use: "delete ", - Short: "delete a block storage", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a blockStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.BlockStorage.Delete(context.Background(), id); err != nil { - fmt.Printf("error deleting block storage : %v\n", err) - os.Exit(1) - } - - fmt.Println("deleted block storage") - }, -} - -var bsDetach = &cobra.Command{ - Use: "detach ", - Short: "detaches a block storage from an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a blockStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - live, _ := cmd.Flags().GetBool("live") - - bsDetach := &govultr.BlockStorageDetach{ - Live: govultr.BoolToBoolPtr(live), - } - - if err := client.BlockStorage.Detach(context.Background(), id, bsDetach); err != nil { - fmt.Printf("error detaching block storage : %v\n", err) - os.Exit(1) - } - - fmt.Println("detached block storage") - }, -} - -var bsLabelSet = &cobra.Command{ - Use: "label ", - Short: "sets a label for a block storage", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a blockStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - label, _ := cmd.Flags().GetString("label") - - options := &govultr.BlockStorageUpdate{ - Label: label, - } - - if err := client.BlockStorage.Update(context.Background(), id, options); err != nil { - fmt.Printf("error setting label : %v\n", err) - os.Exit(1) - } - - fmt.Printf("set label on block storage : %s\n", id) - }, -} - -// List all of individual block storage -var bsList = &cobra.Command{ - Use: "list", - Short: "retrieves a list of active block storage", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - bs, meta, err := client.BlockStorage.List(context.Background(), options) - if err != nil { - fmt.Printf("error getting block storage : %v\n", err) - os.Exit(1) - } - - printer.BlockStorage(bs, meta) - }, -} - -// Get a block storage -var bsGet = &cobra.Command{ - Use: "get ", - Short: "retrieves a block storage", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a blockStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - bs, err := client.BlockStorage.Get(context.Background(), id) - if err != nil { - fmt.Printf("error getting block storage : %v\n", err) - os.Exit(1) - } - - printer.SingleBlockStorage(bs) - }, -} - -var bsResize = &cobra.Command{ - Use: "resize ", - Short: "resize a block storage", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a blockStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - size, _ := cmd.Flags().GetInt("size") - - options := &govultr.BlockStorageUpdate{ - SizeGB: size, - } - - if err := client.BlockStorage.Update(context.Background(), id, options); err != nil { - fmt.Printf("error resizing block storage : %v\n", err) - os.Exit(1) - } - - fmt.Println("resized block storage") - }, -} diff --git a/cmd/blockstorage/blockstorage.go b/cmd/blockstorage/blockstorage.go new file mode 100644 index 00000000..967896b3 --- /dev/null +++ b/cmd/blockstorage/blockstorage.go @@ -0,0 +1,473 @@ +// Package blockstorage provides the block storage functionality for +// the CLI +package blockstorage + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + attachLong = `Attaches a block storage resource to an specified instance` + attachExample = ` + #Full example + vultr-cli block-storage attach 67181686-5455-4ebb-81eb-7299f3506e2c --instance=a7898453-dd9e-4b47-bdab-9dd7a3448f1f + + #Shortened with aliased commands + vultr-cli bs a 67181686-5455-4ebb-81eb-7299f3506e2c -i=a7898453-dd9e-4b47-bdab-9dd7a3448f1f + ` + + createLong = `Create a new block storage resource in a specified region` + createExample = ` + #Full example + vultr-cli block-storage create --region='lax' --size=10 --label='your-label' + + #Full example with block-type + vultr-cli block-storage create --region='lax' --size=10 --block-type='high_perf' + + #Shortened with aliased commands + vultr-cli bs c -r='lax' -s=10 -l='your-label' + + #Shortened with aliased commands and block-type + vultr-cli bs c -r='lax' -s=10 -b='high_perf' + ` + + deleteLong = `Delete a block storage resource` + deleteExample = ` + #Full example + vultr-cli block-storage delete 67181686-5455-4ebb-81eb-7299f3506e2c + + #Shortened with aliased commands + vultr-cli bs d 67181686-5455-4ebb-81eb-7299f3506e2c + ` + + detachLong = `Detach a block storage resource from an instance` + detachExample = ` + #Full example + vultr-cli block-storage detach 67181686-5455-4ebb-81eb-7299f3506e2c + + #Shortened with aliased commands + vultr-cli bs detach 67181686-5455-4ebb-81eb-7299f3506e2c + ` + + labelLong = `Set a label for a block storage resource` + labelExample = ` + #Full example + vultr-cli block-storage label 67181686-5455-4ebb-81eb-7299f3506e2c --label="Example Label" + + #Shortened with aliased commands + vultr-cli bs label 67181686-5455-4ebb-81eb-7299f3506e2c -l="Example Label" + ` + + listLong = `Retrieves a list of active block storage resources` + listExample = ` + #Full example + vultr-cli block-storage list + + #Shortened with aliased commands + vultr-cli bs l + ` + + getLong = `Retrieves a specified block storage resource` + getExample = ` + #Full example + vultr-cli block-storage get 67181686-5455-4ebb-81eb-7299f3506e2c + + #Shortened with aliased commands + vultr-cli bs g 67181686-5455-4ebb-81eb-7299f3506e2c + ` + + resizeLong = `Resizes a specified block storage resource` + resizeExample = ` + #Full example + vultr-cli block-storage resize 67181686-5455-4ebb-81eb-7299f3506e2c --size=20 + + #Shortened with aliased commands + vultr-cli bs r 67181686-5455-4ebb-81eb-7299f3506e2c -s=20 + ` +) + +// NewCmdBlockStorage provides the command for block storage to the CLI +func NewCmdBlockStorage(base *cli.Base) *cobra.Command { //nolint:gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "block-storage", + Aliases: []string{"bs"}, + Short: "block storage commands", + Long: `block-storage is used to interact with the block-storage api`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "List block storage", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + bss, meta, err := o.list() + if err != nil { + printer.Error(fmt.Errorf("error retrieving block storage list : %v", err)) + os.Exit(1) + } + + data := &BlockStoragesPrinter{BlockStorages: bss, Meta: meta} + o.Base.Printer.Display(data, nil) + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "Retrieve a block storage", + Aliases: []string{"g"}, + Long: getLong, + Example: getExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a block storage ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + bs, err := o.get() + if err != nil { + printer.Error(fmt.Errorf("error retrieving block storage : %v", err)) + os.Exit(1) + } + + data := &BlockStoragePrinter{BlockStorage: bs} + o.Base.Printer.Display(data, nil) + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "Create a new block storage", + Aliases: []string{"c"}, + Long: createLong, + Example: createExample, + Run: func(cmd *cobra.Command, args []string) { + reg, errRg := cmd.Flags().GetString("region") + if errRg != nil { + printer.Error(fmt.Errorf("error parsing 'region' flag for block storage create : %v", errRg)) + os.Exit(1) + } + + size, errSz := cmd.Flags().GetInt("size") + if errSz != nil { + printer.Error(fmt.Errorf("error parsing 'size' flag for block storage create : %v", errSz)) + os.Exit(1) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + printer.Error(fmt.Errorf("error parsing 'label' flag for block storage create : %v", errLa)) + os.Exit(1) + } + + blockType, errBt := cmd.Flags().GetString("block-type") + if errBt != nil { + printer.Error(fmt.Errorf("error parsing 'block-type' flag for block storage create : %v", errBt)) + os.Exit(1) + } + + o.CreateReq = &govultr.BlockStorageCreate{ + Region: reg, + SizeGB: size, + Label: label, + BlockType: blockType, + } + + bs, err := o.create() + if err != nil { + printer.Error(fmt.Errorf("error creating block storage : %v", err)) + os.Exit(1) + } + + data := &BlockStoragePrinter{BlockStorage: bs} + o.Base.Printer.Display(data, nil) + }, + } + + create.Flags().StringP("region", "r", "", "ID of the region in which to create the block storage") + if err := create.MarkFlagRequired("region"); err != nil { + fmt.Printf("error marking block storage create 'region' flag required: %v\n", err) + os.Exit(1) + } + + create.Flags().IntP("size", "s", 0, "size of the block storage you want to create") + if err := create.MarkFlagRequired("size"); err != nil { + fmt.Printf("error marking block storage create 'size' flag required: %v\n", err) + os.Exit(1) + } + + create.Flags().StringP("label", "l", "", "label you want to give the block storage") + + create.Flags().StringP( + "block-type", + "b", + "", + `(optional) Block type you want to give the block storage. + Possible values: 'high_perf', 'storage_opt'. Currently defaults to 'high_perf'.`, + ) + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "Delete a block storage", + Aliases: []string{"d", "destroy"}, + Long: deleteLong, + Example: deleteExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a block storage ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.del(); err != nil { + printer.Error(fmt.Errorf("error deleting block storage : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("block storage has been deleted"), nil) + }, + } + + // Attach + attach := &cobra.Command{ + Use: "attach ", + Short: "Attach a block storage to an instance", + Aliases: []string{"a"}, + Long: attachLong, + Example: attachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a block storage ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + instance, errIe := cmd.Flags().GetString("instance") + if errIe != nil { + printer.Error(fmt.Errorf("error parsing 'instance' flag for block storage attach : %v", errIe)) + os.Exit(1) + } + + live, errLe := cmd.Flags().GetBool("live") + if errLe != nil { + printer.Error(fmt.Errorf("error parsing 'live' flag for block storage attach : %v", errLe)) + os.Exit(1) + } + + o.AttachReq = &govultr.BlockStorageAttach{ + InstanceID: instance, + Live: govultr.BoolToBoolPtr(live), + } + + if err := o.attach(); err != nil { + printer.Error(fmt.Errorf("error attaching block storage : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("block storage has been attached"), nil) + }, + } + + attach.Flags().StringP("instance", "i", "", "instance ID to which to attach the block storage") + if err := attach.MarkFlagRequired("instance"); err != nil { + fmt.Printf("error marking block storage attach 'instance' flag required: %v\n", err) + os.Exit(1) + } + + attach.Flags().Bool("live", false, "attach block storage without restarting the instance") + + // Detach + detach := &cobra.Command{ + Use: "detach ", + Short: "Detach a block storage from an instance", + Long: detachLong, + Example: detachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a block storage ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + live, errLe := cmd.Flags().GetBool("live") + if errLe != nil { + printer.Error(fmt.Errorf("error parsing 'live' flag for block storage detach : %v", errLe)) + os.Exit(1) + } + + o.DetachReq = &govultr.BlockStorageDetach{ + Live: govultr.BoolToBoolPtr(live), + } + + if err := o.detach(); err != nil { + printer.Error(fmt.Errorf("error detaching block storage : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("block storage has been detached"), nil) + }, + } + + detach.Flags().Bool("live", false, "detach block storage without a restarting instance") + + // Label + label := &cobra.Command{ + Use: "label ", + Short: "Label a block storage", + Long: labelLong, + Example: labelExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a block storage ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + label, errLl := cmd.Flags().GetString("label") + if errLl != nil { + printer.Error(fmt.Errorf("error parsing 'label' flag for block storage : %v", errLl)) + os.Exit(1) + } + + o.UpdateReq = &govultr.BlockStorageUpdate{ + Label: label, + } + + if err := o.update(); err != nil { + printer.Error(fmt.Errorf("error updating block storage label : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("block storage label has been updated"), nil) + }, + } + + label.Flags().StringP("label", "l", "", "the label to apply to the block storage") + if err := label.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking block storage label set 'label' flag required: %v\n", err) + os.Exit(1) + } + + // Resize + resize := &cobra.Command{ + Use: "resize ", + Short: "Resize a block storage", + Aliases: []string{"r"}, + Long: resizeLong, + Example: resizeExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a block storage ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + size, errSz := cmd.Flags().GetInt("size") + if errSz != nil { + printer.Error(fmt.Errorf("error parsing 'size' flag for block storage resize : %v", errSz)) + os.Exit(1) + } + + o.UpdateReq = &govultr.BlockStorageUpdate{ + SizeGB: size, + } + + if err := o.update(); err != nil { + printer.Error(fmt.Errorf("error resizing block storage : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("block storage has been resized"), nil) + }, + } + + resize.Flags().IntP("size", "s", 0, "size you want your block storage to be") + if err := resize.MarkFlagRequired("size"); err != nil { + fmt.Printf("error marking block storage resize 'size' flag required: %v\n", err) + os.Exit(1) + } + + cmd.AddCommand( + list, + get, + create, + del, + label, + attach, + detach, + resize, + ) + + return cmd +} + +type options struct { + Base *cli.Base + CreateReq *govultr.BlockStorageCreate + UpdateReq *govultr.BlockStorageUpdate + AttachReq *govultr.BlockStorageAttach + DetachReq *govultr.BlockStorageDetach +} + +func (o *options) list() ([]govultr.BlockStorage, *govultr.Meta, error) { + bs, meta, _, err := o.Base.Client.BlockStorage.List(o.Base.Context, o.Base.Options) + return bs, meta, err +} + +func (o *options) get() (*govultr.BlockStorage, error) { + bs, _, err := o.Base.Client.BlockStorage.Get(o.Base.Context, o.Base.Args[0]) + return bs, err +} + +func (o *options) create() (*govultr.BlockStorage, error) { + bs, _, err := o.Base.Client.BlockStorage.Create(o.Base.Context, o.CreateReq) + return bs, err +} + +func (o *options) del() error { + return o.Base.Client.BlockStorage.Delete(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) update() error { + return o.Base.Client.BlockStorage.Update(o.Base.Context, o.Base.Args[0], o.UpdateReq) +} + +func (o *options) attach() error { + return o.Base.Client.BlockStorage.Attach(o.Base.Context, o.Base.Args[0], o.AttachReq) +} + +func (o *options) detach() error { + return o.Base.Client.BlockStorage.Detach(o.Base.Context, o.Base.Args[0], o.DetachReq) +} diff --git a/cmd/blockstorage/printer.go b/cmd/blockstorage/printer.go new file mode 100644 index 00000000..6ed35044 --- /dev/null +++ b/cmd/blockstorage/printer.go @@ -0,0 +1,125 @@ +package blockstorage + +import ( + "fmt" + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// BlockStoragesPrinter ... +type BlockStoragesPrinter struct { + BlockStorages []govultr.BlockStorage `json:"blocks"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (b *BlockStoragesPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BlockStoragesPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BlockStoragesPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "REGION ID", + "INSTANCE ID", + "SIZE GB", + "STATUS", + "LABEL", + "BLOCK TYPE", + "DATE CREATED", + "MONTHLY COST", + "MOUNT ID", + }} +} + +// Data ... +func (b *BlockStoragesPrinter) Data() [][]string { + if len(b.BlockStorages) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range b.BlockStorages { + data = append(data, []string{ + b.BlockStorages[i].ID, + b.BlockStorages[i].Region, + b.BlockStorages[i].AttachedToInstance, + strconv.Itoa(b.BlockStorages[i].SizeGB), + b.BlockStorages[i].Status, + b.BlockStorages[i].Label, + b.BlockStorages[i].BlockType, + b.BlockStorages[i].DateCreated, + fmt.Sprintf("$%v", b.BlockStorages[i].Cost), + b.BlockStorages[i].MountID, + }) + } + + return data +} + +// Paging ... +func (b *BlockStoragesPrinter) Paging() [][]string { + return printer.NewPaging(b.Meta.Total, &b.Meta.Links.Next, &b.Meta.Links.Prev).Compose() +} + +// ====================================== + +// BlockStoragePrinter ... +type BlockStoragePrinter struct { + BlockStorage *govultr.BlockStorage `json:"block"` +} + +// JSON ... +func (b *BlockStoragePrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BlockStoragePrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BlockStoragePrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "REGION ID", + "INSTANCE ID", + "SIZE GB", + "STATUS", + "LABEL", + "BLOCK TYPE", + "DATE CREATED", + "MONTHLY COST", + "MOUNT ID", + }} +} + +// Data ... +func (b *BlockStoragePrinter) Data() [][]string { + return [][]string{0: { + b.BlockStorage.ID, + b.BlockStorage.Region, + b.BlockStorage.AttachedToInstance, + strconv.Itoa(b.BlockStorage.SizeGB), + b.BlockStorage.Status, + b.BlockStorage.Label, + b.BlockStorage.BlockType, + b.BlockStorage.DateCreated, + fmt.Sprintf("$%v", b.BlockStorage.Cost), + b.BlockStorage.MountID, + }} +} + +// Paging ... +func (b *BlockStoragePrinter) Paging() [][]string { + return nil +} diff --git a/cmd/containerregistry/containerregistry.go b/cmd/containerregistry/containerregistry.go new file mode 100644 index 00000000..6a3ac6c3 --- /dev/null +++ b/cmd/containerregistry/containerregistry.go @@ -0,0 +1,696 @@ +// Package containerregistry provides functionality for the CLI to control +// container registries +package containerregistry + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Access information about container registries on the account and perform CRUD operations` + example = ` + # Full example + vultr-cli container-registry + ` + createLong = `Create a new container registry with specified options` + createExample = ` + # Full example + vultr-cli container-registry create --region="sjc" --name="my-registry" --public=true --plan="start_up" + + all flags are required + + # Shortened example with aliases + vultr-cli cr c -i="sjc" -n="my-registry" -p=true -l="start_up" + ` + + getLong = `Display information for a specific VPC` + getExample = ` + # Full example + vultr-cli container-registry get e8ba183d-df3b-487a-acbf-f6c06aa32468 + + # Shortened example with aliases + vultr-cli cr g e8ba183d-df3b-487a-acbf-f6c06aa32468 + ` + updateLong = `Update an existing container registry` + updateExample = ` + # Full example + vultr-cli container-registry update 835fd402-e0eb-47aa-a5a9-a9885feea1cf --plan="premium" --public="true" + + # Shortened example with aliases + vultr-cli cr u 835fd402-e0eb-47aa-a5a9-a9885feea1cf -p="premium" -b="true" + ` + deleteLong = `Delete a container registry` + deleteExample = ` + #Full example + vultr-cli container-registry delete b20fa61e-4abb-46c5-92c3-8700150e1f9a + + #Shortened example with aliases + vultr-cli cr d b20fa61e-4abb-46c5-92c3-8700150e1f9a + ` + listLong = `List all container registries on the account` + listExample = ` + # Full example + vultr-cli container-registry list + + # Shortened example with aliases + vultr-cli cr l + ` + credentialsLong = `Commands for accessing the credentials on registries` + //nolint:gosec + credentialsExample = ` + # Full example + vultr-cli container-registry credentials + ` + credentialsDockerLong = `Create the credential string used by docker` + //nolint:gosec + credentialsDockerExample = ` + # Full example + vultr-cli container-registry credentials docker d24cfdcc-0534-4700-bf88-8ee48f20064e + ` + repoLong = `Access commands for individual repositories on a container registry` + repoExample = ` + # Full example + vultr-cli container-registry repository + + # Shortened example with aliases + vultr-cli cr r + ` + repoUpdateLong = `Update the details of registry's repository` + repoUpdateExample = ` + # Full example + vultr-cli container-registry repository update 4dcdc52e-9c63-401e-8c5f-1582490fe09c --image-name="my-thing" --description="new description" + + # Shortened example with aliases + vultr-cli cr r u 4dcdc52e-9c63-401e-8c5f-1582490fe09c -i="my-thing" -d="new description" + ` + repoDeleteLong = `Delete a repository in a registry` + repoDeleteExample = ` + # Full example + vultr-cli container-registry repository delete 4dcdc52e-9c63-401e-8c5f-1582490fe09c --image-name="my-thing" + + # Shortened example with aliases + vultr-cli cr r d 4dcdc52e-9c63-401e-8c5f-1582490fe09c -i="my-thing" + ` + plansLong = `Retrieve the current plan details for container registry` + plansExample = ` + # Full example + vultr-cli container-registry plans + + # Shortened example with aliases + vultr-cli cr p + ` + regionsLong = `Retrieve the available regions for container registries` + regionsExample = ` + # Full example + vultr-cli container-registry regions + + # Shortened example with aliases + vultr-cli cr r + ` +) + +// NewCmdContainerRegistry provides the CLI command functionality for container registry +func NewCmdContainerRegistry(base *cli.Base) *cobra.Command { //nolint:funlen,gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "container-registry", + Short: "Commands to interact with container registries", + Aliases: []string{"cr"}, + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "List all container registries", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + regs, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving container registry list : %v", err) + } + + data := &ContainerRegistriesPrinter{Registries: regs, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "Get a container registry", + Aliases: []string{"g"}, + Long: getLong, + Example: getExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a container registry ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + reg, err := o.get() + if err != nil { + return fmt.Errorf("error retrieving container registry info : %v", err) + } + + data := &ContainerRegistryPrinter{Registry: reg} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "Create a container registry", + Aliases: []string{"c"}, + Long: createLong, + Example: createExample, + RunE: func(cmd *cobra.Command, args []string) error { + name, errNa := cmd.Flags().GetString("name") + if errNa != nil { + return fmt.Errorf("error parsing 'name' flag for container registry create : %v", errNa) + } + + region, errRe := cmd.Flags().GetString("region") + if errRe != nil { + return fmt.Errorf("error parsing 'region' flag for container registry create : %v", errRe) + } + + public, errPu := cmd.Flags().GetBool("public") + if errPu != nil { + return fmt.Errorf("error parsing 'public' flag for container registry create : %v", errPu) + } + + plan, errPl := cmd.Flags().GetString("plan") + if errPl != nil { + return fmt.Errorf("error parsing 'plan' flag for container registry create : %v", errPl) + } + + o.CreateReq = &govultr.ContainerRegistryReq{ + Name: name, + Region: region, + Public: public, + Plan: plan, + } + + reg, err := o.create() + if err != nil { + return fmt.Errorf("error creating container registry : %v", err) + } + + data := &ContainerRegistryPrinter{Registry: reg} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + create.Flags().StringP("name", "n", "", "The name to use for the container registry") + if err := create.MarkFlagRequired("name"); err != nil { + printer.Error(fmt.Errorf("error marking container registry create 'name' flag required: %v", err)) + os.Exit(1) + } + + create.Flags().StringP("region", "i", "", "The ID of the region in which to create the container registry") + if err := create.MarkFlagRequired("region"); err != nil { + printer.Error(fmt.Errorf("error marking container registry create 'region' flag required: %v", err)) + os.Exit(1) + } + + create.Flags().BoolP("public", "p", false, "If the registry is publicly available. Should be true | false (default is false)") + if err := create.MarkFlagRequired("public"); err != nil { + printer.Error(fmt.Errorf("error marking container registry create 'public' flag required: %v", err)) + os.Exit(1) + } + + create.Flags().StringP("plan", "l", "", "The type of plan to use for the container registry") + if err := create.MarkFlagRequired("plan"); err != nil { + printer.Error(fmt.Errorf("error marking container registry create 'plan' flag required: %v", err)) + os.Exit(1) + } + + // Update + update := &cobra.Command{ + Use: "update ", + Short: "Update a container registry", + Aliases: []string{"u"}, + Long: updateLong, + Example: updateExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a container registry ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + public, errPu := cmd.Flags().GetBool("public") + if errPu != nil { + return fmt.Errorf("error parsing 'public' flag for container registry update : %v", errPu) + } + + plan, errPl := cmd.Flags().GetString("plan") + if errPl != nil { + return fmt.Errorf("error parsing 'plan' flag for container registry update : %v", errPl) + } + + o.UpdateReq = &govultr.ContainerRegistryUpdateReq{ + Plan: govultr.StringToStringPtr(plan), + } + + if cmd.Flags().Changed("public") { + o.UpdateReq.Public = govultr.BoolToBoolPtr(public) + } + + if err := o.update(); err != nil { + return fmt.Errorf("error updating container registry : %v", err) + } + + o.Base.Printer.Display(printer.Info("container registry has been updated"), nil) + + return nil + }, + } + + update.Flags().StringP("plan", "p", "", "Name of the plan used for the container registry") + update.Flags().BoolP("public", "b", false, "The container registry availability status") + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "Delete a container registry", + Aliases: []string{"destroy", "d"}, + Long: deleteLong, + Example: deleteExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a container registry ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.del(); err != nil { + return fmt.Errorf("error deleting container registry : %v", err) + } + + o.Base.Printer.Display(printer.Info("container registry has been deleted"), nil) + + return nil + }, + } + + // Plans + plans := &cobra.Command{ + Use: "plans", + Short: "List the plan names for container registry", + Aliases: []string{"p"}, + Long: plansLong, + Example: plansExample, + RunE: func(cmd *cobra.Command, args []string) error { + plans, err := o.plans() + if err != nil { + return fmt.Errorf("error retrieving plans for container registry : %v", err) + } + + data := &ContainerRegistryPlansPrinter{Plans: plans.Plans} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Regions + regions := &cobra.Command{ + Use: "regions", + Short: "List the available regions for container registry", + Aliases: []string{"i"}, + Long: regionsLong, + Example: regionsExample, + RunE: func(cmd *cobra.Command, args []string) error { + regs, meta, err := o.regions() + if err != nil { + return fmt.Errorf("error retrieving regions for container registry : %v", err) + } + + data := &ContainerRegistryRegionsPrinter{Regions: regs, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Repository + repository := &cobra.Command{ + Use: "repository", + Short: "Interact with container registry repositories", + Aliases: []string{"r", "repo"}, + Long: repoLong, + Example: repoExample, + } + + // Repository List + repoList := &cobra.Command{ + Use: "list ", + Short: "List all container registries", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a container registry ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + repos, meta, err := o.repositoryList() + if err != nil { + return fmt.Errorf("error retrieving repositories for container registry : %v", err) + } + + data := &ContainerRegistryRepositoriesPrinter{Repositories: repos, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Repository Get + repoGet := &cobra.Command{ + Use: "get ", + Short: "Get a container registry repository", + Aliases: []string{"g"}, + Long: getLong, + Example: getExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a container registry ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + name, errIm := cmd.Flags().GetString("image-name") + if errIm != nil { + return fmt.Errorf("error parsing 'image-name' flag for container registry repository get : %v", errIm) + } + + o.RepoName = name + + repo, err := o.repositoryGet() + if err != nil { + return fmt.Errorf("error getting container registry repository : %v", err) + } + + data := &ContainerRegistryRepositoryPrinter{Repository: repo} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + repoGet.Flags().StringP("image-name", "i", "", "The name of the image/repo") + if err := repoGet.MarkFlagRequired("image-name"); err != nil { + printer.Error(fmt.Errorf("error marking get container registry repository 'image-name' flag required: %v", err)) + os.Exit(1) + } + + // Repository Update + repoUpdate := &cobra.Command{ + Use: "update ", + Short: "Update a container registry repository", + Aliases: []string{"u"}, + Long: repoUpdateLong, + Example: repoUpdateExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a container registry ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + name, errIm := cmd.Flags().GetString("image-name") + if errIm != nil { + return fmt.Errorf("error parsing 'image-name' flag for container registry repository update : %v", errIm) + } + + description, errDe := cmd.Flags().GetString("description") + if errDe != nil { + return fmt.Errorf("error parsing 'description' flag for container registry repository update : %v", errDe) + } + + o.RepoName = name + o.RepoUpdateReq = &govultr.ContainerRegistryRepoUpdateReq{ + Description: description, + } + + if err := o.repositoryUpdate(); err != nil { + return fmt.Errorf("error updating container registry repository : %v", err) + } + + o.Base.Printer.Display(printer.Info("container registry repository has been updated"), nil) + + return nil + }, + } + + repoUpdate.Flags().StringP("image-name", "i", "", "The name of the image/repo") + if err := repoUpdate.MarkFlagRequired("image-name"); err != nil { + printer.Error(fmt.Errorf("error marking update container registry repository 'image-name' flag required: %v", err)) + os.Exit(1) + } + + repoUpdate.Flags().StringP("description", "d", "", "The description of the image/repo") + if err := repoUpdate.MarkFlagRequired("description"); err != nil { + printer.Error(fmt.Errorf("error marking update container registry repository 'description' flag required: %v", err)) + os.Exit(1) + } + + // Repository Delete + repoDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete a container registry repository", + Aliases: []string{"destroy", "d"}, + Long: repoDeleteLong, + Example: repoDeleteExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a container registry ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + name, errIm := cmd.Flags().GetString("image-name") + if errIm != nil { + return fmt.Errorf("error parsing 'image-name' flag for container registry repository delete : %v", errIm) + } + + o.RepoName = name + + if err := o.repositoryDelete(); err != nil { + return fmt.Errorf("error deleting container registry repository : %v", err) + } + + o.Base.Printer.Display(printer.Info("container registry repository has been deleted"), nil) + + return nil + }, + } + + repoDelete.Flags().StringP("image-name", "i", "", "The name of the image/repo") + if err := repoDelete.MarkFlagRequired("image-name"); err != nil { + printer.Error(fmt.Errorf("error marking delete container registry repository 'image-name' flag required: %v", err)) + os.Exit(1) + } + + repository.AddCommand( + repoGet, + repoList, + repoUpdate, + repoDelete, + ) + + // Credentials + credentials := &cobra.Command{ + Use: "credentials", + Short: "Commands for container registry credentials", + Aliases: []string{""}, + Long: credentialsLong, + Example: credentialsExample, + } + + // Credentials Docker + credentialsDocker := &cobra.Command{ + Use: "docker ", + Short: "Create Docker credentials for a container registry", + Aliases: []string{"d"}, + Long: credentialsDockerLong, + Example: credentialsDockerExample, + RunE: func(cmd *cobra.Command, args []string) error { + expiry, errEx := cmd.Flags().GetInt("expiry-seconds") + if errEx != nil { + return fmt.Errorf("error parsing 'expiry-seconds' flag for container registry docker creds : %v", errEx) + } + + access, errAc := cmd.Flags().GetBool("read-write") + if errAc != nil { + return fmt.Errorf("error parsing 'read-write' flag for container registry docker creds : %v", errAc) + } + + o.CredentialsDockerReq = &govultr.DockerCredentialsOpt{ + ExpirySeconds: govultr.IntToIntPtr(expiry), + WriteAccess: govultr.BoolToBoolPtr(access), + } + + cred, err := o.credentialsDocker() + if err != nil { + return fmt.Errorf("error generating container registry repository docker credentials : %v", err) + } + + data := &ContainerRegistryCredentialDockerPrinter{Credential: cred} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + credentialsDocker.Flags().IntP( + "expiry-seconds", + "e", + 0, + "(optional) The seconds until these credentials expire. Default is 0, never", + ) + + credentialsDocker.Flags().BoolP( + "read-write", + "w", + false, + "(optional) Whether or not these credentials have write access. Should be true or false. Default is false", + ) + + credentials.AddCommand( + credentialsDocker, + ) + + cmd.AddCommand( + list, + get, + create, + update, + del, + plans, + regions, + repository, + credentials, + ) + + return cmd +} + +type options struct { + Base *cli.Base + CreateReq *govultr.ContainerRegistryReq + UpdateReq *govultr.ContainerRegistryUpdateReq + RepoName string + RepoUpdateReq *govultr.ContainerRegistryRepoUpdateReq + CredentialsDockerReq *govultr.DockerCredentialsOpt +} + +func (o *options) list() ([]govultr.ContainerRegistry, *govultr.Meta, error) { + cr, meta, _, err := o.Base.Client.ContainerRegistry.List(o.Base.Context, o.Base.Options) + return cr, meta, err +} + +func (o *options) get() (*govultr.ContainerRegistry, error) { + cr, _, err := o.Base.Client.ContainerRegistry.Get(o.Base.Context, o.Base.Args[0]) + return cr, err +} + +func (o *options) create() (*govultr.ContainerRegistry, error) { + cr, _, err := o.Base.Client.ContainerRegistry.Create(o.Base.Context, o.CreateReq) + return cr, err +} + +func (o *options) update() error { + _, _, err := o.Base.Client.ContainerRegistry.Update(o.Base.Context, o.Base.Args[0], o.UpdateReq) + return err +} + +func (o *options) del() error { + return o.Base.Client.ContainerRegistry.Delete(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) plans() (*govultr.ContainerRegistryPlans, error) { + plans, _, err := o.Base.Client.ContainerRegistry.ListPlans(o.Base.Context) + return plans, err +} + +func (o *options) regions() ([]govultr.ContainerRegistryRegion, *govultr.Meta, error) { + regions, meta, _, err := o.Base.Client.ContainerRegistry.ListRegions(o.Base.Context) + return regions, meta, err +} + +func (o *options) repositoryList() ([]govultr.ContainerRegistryRepo, *govultr.Meta, error) { + repos, meta, _, err := o.Base.Client.ContainerRegistry.ListRepositories(o.Base.Context, o.Base.Args[0], o.Base.Options) + return repos, meta, err +} + +func (o *options) repositoryGet() (*govultr.ContainerRegistryRepo, error) { + repo, _, err := o.Base.Client.ContainerRegistry.GetRepository(o.Base.Context, o.Base.Args[0], o.RepoName) + return repo, err +} + +func (o *options) repositoryUpdate() error { + _, _, err := o.Base.Client.ContainerRegistry.UpdateRepository( + o.Base.Context, + o.Base.Args[0], + o.RepoName, + o.RepoUpdateReq, + ) + + return err +} + +func (o *options) repositoryDelete() error { + return o.Base.Client.ContainerRegistry.DeleteRepository(o.Base.Context, o.Base.Args[0], o.RepoName) +} + +func (o *options) credentialsDocker() (*govultr.ContainerRegistryDockerCredentials, error) { + cred, _, err := o.Base.Client.ContainerRegistry.CreateDockerCredentials( + o.Base.Context, + o.Base.Args[0], + o.CredentialsDockerReq, + ) + return cred, err +} diff --git a/cmd/containerregistry/printer.go b/cmd/containerregistry/printer.go new file mode 100644 index 00000000..098fc94e --- /dev/null +++ b/cmd/containerregistry/printer.go @@ -0,0 +1,376 @@ +package containerregistry + +import ( + "fmt" + "reflect" + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" +) + +// ContainerRegistryPrinter ... +type ContainerRegistryPrinter struct { + Registry *govultr.ContainerRegistry `json:"registry"` +} + +// JSON ... +func (c *ContainerRegistryPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ContainerRegistryPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ContainerRegistryPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (c *ContainerRegistryPrinter) Data() [][]string { + return [][]string{ + 0: {"ID", c.Registry.ID}, + 1: {"NAME", c.Registry.Name}, + 2: {"PUBLIC", strconv.FormatBool(c.Registry.Public)}, + 3: {"URN", c.Registry.URN}, + 4: {"REGION", c.Registry.Metadata.Region.Name}, + 5: {" "}, + 6: {"ROOT USER"}, + 7: {"ID", strconv.Itoa(c.Registry.RootUser.ID)}, + 8: {"USER NAME", c.Registry.RootUser.UserName}, + 9: {"PASSWORD", c.Registry.RootUser.Password}, + 10: {"CREATED", c.Registry.RootUser.DateCreated}, + 11: {"MODIFIED", c.Registry.RootUser.DateModified}, + 12: {" "}, + 13: {"STORAGE"}, + 14: {"USED", fmt.Sprintf("%vGB", c.Registry.Storage.Used.GigaBytes)}, + 15: {"ALLOWED", fmt.Sprintf("%vGB", c.Registry.Storage.Allowed.GigaBytes)}, + 16: {" "}, + 17: {"BILLING"}, + 18: {"PRICE", + strconv.FormatFloat( + float64(c.Registry.Metadata.Subscription.Billing.MonthlyPrice), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + ), + }, + 19: {"CHARGES", + strconv.FormatFloat( + float64(c.Registry.Metadata.Subscription.Billing.PendingCharges), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + ), + }, + 20: {" "}, + 21: {"CREATED", c.Registry.DateCreated}, + } +} + +// Paging ... +func (c *ContainerRegistryPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// ContainerRegistriesPrinter ... +type ContainerRegistriesPrinter struct { + Registries []govultr.ContainerRegistry `json:"registries"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (c *ContainerRegistriesPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ContainerRegistriesPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ContainerRegistriesPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "NAME", + "URN", + "USED/ALLOWED", + "REGION ID", + "REGION NAME", + "PUBLIC", + }} +} + +// Data ... +func (c *ContainerRegistriesPrinter) Data() [][]string { + if len(c.Registries) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range c.Registries { + usage := fmt.Sprintf("%vGB / %vGB", c.Registries[i].Storage.Used.GigaBytes, c.Registries[i].Storage.Allowed.GigaBytes) + data = append(data, []string{ + c.Registries[i].ID, + c.Registries[i].Name, + c.Registries[i].URN, + usage, + strconv.Itoa(c.Registries[i].Metadata.Region.ID), + c.Registries[i].Metadata.Region.Name, + strconv.FormatBool(c.Registries[i].Public), + }) + } + + return data +} + +// Paging ... +func (c *ContainerRegistriesPrinter) Paging() [][]string { + return printer.NewPaging(c.Meta.Total, &c.Meta.Links.Next, &c.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ContainerRegistryPlansPrinter ... +type ContainerRegistryPlansPrinter struct { + Plans govultr.ContainerRegistryPlanTypes `json:"plans"` +} + +// JSON ... +func (c *ContainerRegistryPlansPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ContainerRegistryPlansPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ContainerRegistryPlansPrinter) Columns() [][]string { + return [][]string{0: { + "NAME", + "MAX STORAGE", + "MONTHLY PRICE", + }} +} + +// Data ... +func (c *ContainerRegistryPlansPrinter) Data() [][]string { + var data [][]string + topVals := reflect.ValueOf(c.Plans) + for i := 0; i < topVals.NumField(); i++ { + botVals := reflect.ValueOf(topVals.Field(i).Interface()) + + data = append(data, []string{ + botVals.FieldByName("VanityName").String(), + fmt.Sprintf("%vGB", botVals.FieldByName("MaxStorageMB").Int()/1024), //nolint:gomnd + strconv.FormatInt(botVals.FieldByName("MonthlyPrice").Int(), 10), + }) + } + return data +} + +// Paging ... +func (c *ContainerRegistryPlansPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// ContainerRegistryRegionsPrinter ... +type ContainerRegistryRegionsPrinter struct { + Regions []govultr.ContainerRegistryRegion `json:"regions"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (c *ContainerRegistryRegionsPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ContainerRegistryRegionsPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ContainerRegistryRegionsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "NAME", + "URN", + "COUNTRY", + "REGION", + }} +} + +// Data ... +func (c *ContainerRegistryRegionsPrinter) Data() [][]string { + if len(c.Regions) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range c.Regions { + data = append(data, []string{ + strconv.Itoa(c.Regions[i].ID), + c.Regions[i].Name, + c.Regions[i].URN, + c.Regions[i].DataCenter.Country, + c.Regions[i].DataCenter.Region, + }) + } + + return data +} + +// Paging ... +func (c *ContainerRegistryRegionsPrinter) Paging() [][]string { + return printer.NewPaging(c.Meta.Total, &c.Meta.Links.Next, &c.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ContainerRegistryRepositoryPrinter ... +type ContainerRegistryRepositoryPrinter struct { + Repository *govultr.ContainerRegistryRepo `json:"repository"` +} + +// JSON ... +func (c *ContainerRegistryRepositoryPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ContainerRegistryRepositoryPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ContainerRegistryRepositoryPrinter) Columns() [][]string { + return [][]string{0: { + "NAME", + "IMAGE", + "DESCRIPTION", + "DATE CREATED", + "DATE MODIFIED", + "PULLS", + "ARTIFACTS", + }} +} + +// Data ... +func (c *ContainerRegistryRepositoryPrinter) Data() [][]string { + return [][]string{0: { + c.Repository.Name, + c.Repository.Image, + c.Repository.Description, + c.Repository.DateCreated, + c.Repository.DateModified, + strconv.Itoa(c.Repository.PullCount), + strconv.Itoa(c.Repository.ArtifactCount), + }} +} + +// Paging ... +func (c *ContainerRegistryRepositoryPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// ContainerRegistryRepositoriesPrinter ... +type ContainerRegistryRepositoriesPrinter struct { + Repositories []govultr.ContainerRegistryRepo `json:"repositories"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (c *ContainerRegistryRepositoriesPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ContainerRegistryRepositoriesPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ContainerRegistryRepositoriesPrinter) Columns() [][]string { + return [][]string{0: { + "NAME", + "IMAGE", + "DESCRIPTION", + "DATE CREATED", + "DATE MODIFIED", + "PULLS", + "ARTIFACTS", + }} +} + +// Data ... +func (c *ContainerRegistryRepositoriesPrinter) Data() [][]string { + if len(c.Repositories) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range c.Repositories { + data = append(data, []string{ + c.Repositories[i].Name, + c.Repositories[i].Image, + c.Repositories[i].Description, + c.Repositories[i].DateCreated, + c.Repositories[i].DateModified, + strconv.Itoa(c.Repositories[i].PullCount), + strconv.Itoa(c.Repositories[i].ArtifactCount), + }) + } + + return data +} + +// Paging ... +func (c *ContainerRegistryRepositoriesPrinter) Paging() [][]string { + return printer.NewPaging(c.Meta.Total, &c.Meta.Links.Next, &c.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ContainerRegistryCredentialDockerPrinter ... +type ContainerRegistryCredentialDockerPrinter struct { + Credential *govultr.ContainerRegistryDockerCredentials `json:"docker_credentials"` +} + +// JSON ... +func (c *ContainerRegistryCredentialDockerPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ContainerRegistryCredentialDockerPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ContainerRegistryCredentialDockerPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (c *ContainerRegistryCredentialDockerPrinter) Data() [][]string { + return [][]string{0: {c.Credential.String()}} +} + +// Paging ... +func (c *ContainerRegistryCredentialDockerPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/database/database.go b/cmd/database/database.go new file mode 100644 index 00000000..fa989276 --- /dev/null +++ b/cmd/database/database.go @@ -0,0 +1,2612 @@ +// Package database is used by the CLI to control databases +package database + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Get commands available to database` + example = ` + # Full example + vultr-cli database + ` + listLong = `Get all databases on your Vultr account` + listExample = ` + # Full example + vultr-cli database list + + # Summarized view + vultr-cli database list --summarize + ` + createLong = `Create a new Managed Database with specified plan, region, and database engine/version` + createExample = ` + # Full example + vultr-cli database create --database-engine="mysql" --database-engine-version="8" --region="ewr" \ + --plan="vultr-dbaas-startup-cc-1-55-2" --label="example-db" + + # Full example with custom MySQL settings + vultr-cli database create --database-engine="mysql" --database-engine-version="8" --region="ewr" \ + --plan="vultr-dbaas-startup-cc-1-55-2" --label="example-db" --mysql-slow-query-log="true" --mysql-long-query-time="2" + ` + updateLong = `Updates a Managed Database with the supplied information` + updateExample = ` + # Full example + vultr-cli database update --region="sea" --plan="vultr-dbaas-startup-cc-2-80-4" + + # Full example with custom MySQL settings + vultr-cli database update --mysql-slow-query-log="true" --mysql-long-query-time="2" + ` +) + +// NewCmdDatabase provides the CLI command for database functions +func NewCmdDatabase(base *cli.Base) *cobra.Command { //nolint:funlen,gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "database", + Short: "Access database commands", + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "List databases", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + RunE: func(cmd *cobra.Command, args []string) error { + summarize, errSu := cmd.Flags().GetBool("summarize") + if errSu != nil { + return fmt.Errorf("error parsing flag 'summarize' for database list : %v", errSu) + } + + dbs, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving database list : %v", err) + } + + var data printer.ResourceOutput + if summarize { + data = &DBsSummaryPrinter{DBs: dbs, Meta: meta} + } else { + data = &DBsPrinter{DBs: dbs, Meta: meta} + } + + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().BoolP("summarize", "", false, "(optional) Summarize the list output. One line per database") + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "Retrieve a database", + Aliases: []string{"g"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + db, err := o.get() + if err != nil { + return fmt.Errorf("error retrieving database : %v", err) + } + + data := &DBPrinter{DB: db} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "Create database", + Aliases: []string{"c"}, + Long: createLong, + Example: createExample, + RunE: func(cmd *cobra.Command, args []string) error { + engine, errEn := cmd.Flags().GetString("database-engine") + if errEn != nil { + return fmt.Errorf("error parsing flag 'database-engine' for database create : %v", errEn) + } + + engineVersion, errEg := cmd.Flags().GetString("database-engine-version") + if errEg != nil { + return fmt.Errorf("error parsing flag 'database-engine-version' for database create : %v", errEg) + } + + region, errRe := cmd.Flags().GetString("region") + if errRe != nil { + return fmt.Errorf("error parsing flag 'region' for database create : %v", errRe) + } + + plan, errPl := cmd.Flags().GetString("plan") + if errPl != nil { + return fmt.Errorf("error parsing flag 'plan' for database create : %v", errPl) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for database create : %v", errLa) + } + + // Optional + tag, errTa := cmd.Flags().GetString("tag") + if errTa != nil { + return fmt.Errorf("error parsing flag 'tag' for database create : %v", errTa) + } + + vpc, errVp := cmd.Flags().GetString("vpc-id") + if errVp != nil { + return fmt.Errorf("error parsing flag 'vpc-id' for database create : %v", errVp) + } + + maintenanceDOW, errMa := cmd.Flags().GetString("maintenance-dow") + if errMa != nil { + return fmt.Errorf("error parsing flag 'maintenance-dow' for database create : %v", errMa) + } + + maintenanceTime, errMt := cmd.Flags().GetString("maintenance-time") + if errMt != nil { + return fmt.Errorf("error parsing flag 'maintenance-time' for database create : %v", errMt) + } + + trustedIPs, errTr := cmd.Flags().GetStringSlice("trusted-ips") + if errTr != nil { + return fmt.Errorf("error parsing flag 'trusted-ips' for database create : %v", errTr) + } + + mysqlSQLModes, errMy := cmd.Flags().GetStringSlice("mysql-sql-modes") + if errMy != nil { + return fmt.Errorf("error parsing flag 'mysql-sql-modes' for database create : %v", errMy) + } + + mysqlRequirePrimaryKey, errMq := cmd.Flags().GetBool("mysql-require-primary-key") + if errMq != nil { + return fmt.Errorf("error parsing flag 'mysql-require-primary-key' for database create : %v", errMq) + } + + mySQLSlowQueryLog, errMl := cmd.Flags().GetBool("mysql-slow-query-log") + if errMl != nil { + return fmt.Errorf("error parsing flag 'mysql-slow-query-log' for database create : %v", errMl) + } + + mySQLLongQueryTime, errMt := cmd.Flags().GetInt("mysql-long-query-time") + if errMt != nil { + return fmt.Errorf("error parsing flag 'mysql-long-query-time' for database create : %v", errMt) + } + + redisEvictionPolicy, errEe := cmd.Flags().GetString("redis-eviction-policy") + if errEe != nil { + return fmt.Errorf("error parsing flag 'redis-eviction-policy' for database create : %v", errEe) + } + + o.CreateReq = &govultr.DatabaseCreateReq{ + DatabaseEngine: engine, + DatabaseEngineVersion: engineVersion, + Region: region, + Plan: plan, + Label: label, + Tag: tag, + VPCID: vpc, + MaintenanceDOW: maintenanceDOW, + MaintenanceTime: maintenanceTime, + TrustedIPs: trustedIPs, + MySQLSQLModes: mysqlSQLModes, + MySQLRequirePrimaryKey: &mysqlRequirePrimaryKey, + MySQLSlowQueryLog: &mySQLSlowQueryLog, + MySQLLongQueryTime: mySQLLongQueryTime, + RedisEvictionPolicy: redisEvictionPolicy, + } + + db, err := o.create() + if err != nil { + return fmt.Errorf("error creating database : %v", err) + } + + data := &DBPrinter{DB: db} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + create.Flags().StringP("database-engine", "e", "", "database engine for the new manaaged database") + if err := create.MarkFlagRequired("database-engine"); err != nil { + fmt.Printf("error marking database create 'database-engine' flag required: %v", err) + os.Exit(1) + } + + create.Flags().StringP("database-engine-version", "v", "", "database engine version for the new manaaged database") + if err := create.MarkFlagRequired("database-engine-version"); err != nil { + fmt.Printf("error marking database create 'database-engine-version' flag required: %v", err) + os.Exit(1) + } + + create.Flags().StringP("region", "r", "", "region id for the new managed database") + if err := create.MarkFlagRequired("region"); err != nil { + fmt.Printf("error marking database create 'region' flag required: %v", err) + os.Exit(1) + } + + create.Flags().StringP("plan", "p", "", "plan id for the new managed database") + if err := create.MarkFlagRequired("plan"); err != nil { + fmt.Printf("error marking database create 'plan' flag required: %v", err) + os.Exit(1) + } + + create.Flags().StringP("label", "l", "", "label for the new managed database") + if err := create.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking database create 'label' flag required: %v", err) + os.Exit(1) + } + + create.Flags().String("tag", "t", "tag for the new managed database") + create.Flags().String("vpc-id", "", "vpc id for the new managed database") + create.Flags().String("maintenance-dow", "", "maintenance day of week for the new managed database") + create.Flags().String("maintenance-time", "", "maintenance time for the new managed database") + create.Flags().StringSlice( + "trusted-ips", + []string{}, + "comma-separated list of trusted ip addresses for the new managed database", + ) + create.Flags().StringSlice("mysql-sql-modes", []string{}, "comma-separated list of sql modes for the new managed database") + create.Flags().Bool("mysql-require-primary-key", true, "enable requiring primary keys for the new mysql managed database") + create.Flags().Bool("mysql-slow-query-log", false, "enable slow query logging for the new mysql managed database") + create.Flags().Int( + "mysql-long-query-time", + 0, + "long query time for the new mysql managed database when slow query logging is enabled", + ) + create.Flags().String("redis-eviction-policy", "", "eviction policy for the new redis managed database") + + // Update + update := &cobra.Command{ + Use: "update ", + Short: "Update a database", + Aliases: []string{"u"}, + Long: updateLong, + Example: updateExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + region, errRe := cmd.Flags().GetString("region") + if errRe != nil { + return fmt.Errorf("error parsing flag 'region' for database update : %v", errRe) + } + + plan, errPl := cmd.Flags().GetString("plan") + if errPl != nil { + return fmt.Errorf("error parsing flag 'plan' for database update : %v", errPl) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for database update : %v", errLa) + } + + tag, errTa := cmd.Flags().GetString("tag") + if errTa != nil { + return fmt.Errorf("error parsing flag 'tag' for database update : %v", errTa) + } + + maintenanceDOW, errMa := cmd.Flags().GetString("maintenance-dow") + if errMa != nil { + return fmt.Errorf("error parsing flag 'maintenance-dow' for database update : %v", errMa) + } + + maintenanceTime, errMt := cmd.Flags().GetString("maintenance-time") + if errMt != nil { + return fmt.Errorf("error parsing flag 'maintenance-time' for database update : %v", errMt) + } + + clusterTimeZone, errTz := cmd.Flags().GetString("cluster-time-zone") + if errTz != nil { + return fmt.Errorf("error parsing flag 'cluster-time-zone' for database update : %v", errTz) + } + + trustedIPs, errTr := cmd.Flags().GetStringSlice("trusted-ips") + if errTr != nil { + return fmt.Errorf("error parsing flag 'trusted-ips' for database update : %v", errTr) + } + + mysqlSQLModes, errMy := cmd.Flags().GetStringSlice("mysql-sql-modes") + if errMy != nil { + return fmt.Errorf("error parsing flag 'mysql-sql-modes' for database update : %v", errMy) + } + + mySQLLongQueryTime, errMt := cmd.Flags().GetInt("mysql-long-query-time") + if errMt != nil { + return fmt.Errorf("error parsing flag 'mysql-long-query-time' for database update : %v", errMt) + } + + redisEvictionPolicy, errEe := cmd.Flags().GetString("redis-eviction-policy") + if errEe != nil { + return fmt.Errorf("error parsing flag 'redis-eviction-policy' for database update : %v", errEe) + } + + o.UpdateReq = &govultr.DatabaseUpdateReq{} + + if cmd.Flags().Changed("region") { + o.UpdateReq.Region = region + } + + if cmd.Flags().Changed("plan") { + o.UpdateReq.Plan = plan + } + + if cmd.Flags().Changed("label") { + o.UpdateReq.Label = label + } + + if cmd.Flags().Changed("tag") { + o.UpdateReq.Tag = tag + } + + if cmd.Flags().Changed("maintenance-dow") { + o.UpdateReq.MaintenanceDOW = maintenanceDOW + } + + if cmd.Flags().Changed("maintenance-time") { + o.UpdateReq.MaintenanceTime = maintenanceTime + } + + if cmd.Flags().Changed("cluster-time-zone") { + o.UpdateReq.ClusterTimeZone = clusterTimeZone + } + + if cmd.Flags().Changed("trusted-ips") { + o.UpdateReq.TrustedIPs = trustedIPs + } + + if cmd.Flags().Changed("mysql-sql-modes") { + o.UpdateReq.MySQLSQLModes = mysqlSQLModes + } + + if cmd.Flags().Changed("mysql-long-query-time") { + o.UpdateReq.MySQLLongQueryTime = mySQLLongQueryTime + } + + if cmd.Flags().Changed("redis-eviction-policy") { + o.UpdateReq.RedisEvictionPolicy = redisEvictionPolicy + } + + db, err := o.update() + if err != nil { + return fmt.Errorf("error updating database : %v", err) + } + + data := &DBPrinter{DB: db} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + update.Flags().StringP("region", "r", "", "region id for the managed database") + update.Flags().StringP("plan", "p", "", "plan id for the managed database") + update.Flags().StringP("label", "l", "", "label for the managed database") + update.Flags().StringP("tag", "t", "", "tag for the managed database") + update.Flags().String("vpc-id", "", "vpc id for the managed database") + update.Flags().String("maintenance-dow", "", "maintenance day of week for the managed database") + update.Flags().String("maintenance-time", "", "maintenance time for the managed database") + update.Flags().String("cluster-time-zone", "", "configured time zone for the managed database") + update.Flags().StringSlice( + "trusted-ips", + []string{}, + "comma-separated list of trusted ip addresses for the managed database", + ) + update.Flags().StringSlice( + "mysql-sql-modes", + []string{}, + "comma-separated list of sql modes for the managed database", + ) + update.Flags().Bool("mysql-require-primary-key", true, "enable requiring primary keys for the mysql managed database") + update.Flags().Bool("mysql-slow-query-log", false, "enable slow query logging for the mysql managed database") + update.Flags().String( + "mysql-long-query-time", + "", + "long query time for the mysql managed database when slow query logging is enabled", + ) + update.Flags().String("redis-eviction-policy", "", "eviction policy for the redis managed database") + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "Delete a database", + Aliases: []string{"destroy", "d"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.del(); err != nil { + return fmt.Errorf("error deleting database : %v", err) + } + + o.Base.Printer.Display(printer.Info("Database has been deleted"), nil) + + return nil + }, + } + + // Plan + plan := &cobra.Command{ + Use: "plan", + Short: "Commands to access database plans", + } + + // Plan List + planList := &cobra.Command{ + Use: "list", + Short: "List database plans", + Aliases: []string{"l"}, + RunE: func(cmd *cobra.Command, args []string) error { + plans, meta, err := o.listPlans() + if err != nil { + return fmt.Errorf("error retrieving database plans : %v", err) + } + + data := &PlansPrinter{Plans: plans, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + plan.AddCommand( + planList, + ) + + // User + user := &cobra.Command{ + Use: "user", + Short: "Commands to handle database users", + } + + // User List + userList := &cobra.Command{ + Use: "list ", + Short: "List database users", + RunE: func(cmd *cobra.Command, args []string) error { + us, meta, err := o.listUsers() + if err != nil { + return fmt.Errorf("error retrieving database users : %v", err) + } + + data := &UsersPrinter{Users: us, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // User Get + userGet := &cobra.Command{ + Use: "get ", + Short: "Get a database user", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("please provide a database ID and a user name") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + us, err := o.getUser() + if err != nil { + return fmt.Errorf("error retrieving database user : %v", err) + } + + data := &UserPrinter{User: us} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // User Create + userCreate := &cobra.Command{ + Use: "create ", + Short: "Create a database user", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + username, errUs := cmd.Flags().GetString("username") + if errUs != nil { + return fmt.Errorf("error parsing flag 'username' for database user create : %v", errUs) + } + + password, errPa := cmd.Flags().GetString("password") + if errPa != nil { + return fmt.Errorf("error parsing flag 'password' for database user create : %v", errPa) + } + + encryption, errEn := cmd.Flags().GetString("encryption") + if errEn != nil { + return fmt.Errorf("error parsing flag 'encryption' for database user create : %v", errEn) + } + + o.UserCreateReq = &govultr.DatabaseUserCreateReq{ + Username: username, + Password: password, + Encryption: encryption, + } + + us, err := o.createUser() + if err != nil { + return fmt.Errorf("error creating database user : %v", err) + } + + data := &UserPrinter{User: us} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + userCreate.Flags().StringP("username", "u", "", "username for the new manaaged database user") + userCreate.Flags().StringP( + "password", + "p", + "", + "password for the new manaaged database user (omit or leave empty to generate a random secure password)", + ) + userCreate.Flags().StringP("encryption", "e", "", "encryption type for the new managed database user (MySQL only)") + + // User Update + userUpdate := &cobra.Command{ + Use: "update ", + Short: "Update a database user", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("please provide a database ID and a user name") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + password, errPa := cmd.Flags().GetString("password") + if errPa != nil { + return fmt.Errorf("error parsing flag 'password' for database user create : %v", errPa) + } + + o.UserUpdateReq = &govultr.DatabaseUserUpdateReq{ + Password: password, + } + + us, err := o.updateUser() + if err != nil { + return fmt.Errorf("error updating database user : %v", err) + } + + data := &UserPrinter{User: us} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + userUpdate.Flags().StringP( + "password", + "p", + "", + "password for the new manaaged database user (omit or leave empty to generate a random secure password)", + ) + + // User Delete + userDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete a database user", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("please provide a database ID and a user name") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.delUser(); err != nil { + return fmt.Errorf("error deleting database user : %v", err) + } + + o.Base.Printer.Display(printer.Info("User deleted"), nil) + + return nil + }, + } + + // User ACL + userACL := &cobra.Command{ + Use: "acl", + Short: "commands to handle managed database user access control (Redis only)", + } + + // User ACL Update + userACLUpdate := &cobra.Command{ + Use: "update ", + Short: "Update a database user ACL", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("please provide a database ID and a user name") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + categories, errCa := cmd.Flags().GetStringSlice("redis-acl-categories") + if errCa != nil { + return fmt.Errorf("error parsing flag 'redis-acl-categories' for database user create : %v", errCa) + } + + channels, errCh := cmd.Flags().GetStringSlice("redis-acl-channels") + if errCh != nil { + return fmt.Errorf("error parsing flag 'redis-acl-channels' for database user create : %v", errCh) + } + + commands, errCo := cmd.Flags().GetStringSlice("redis-acl-commands") + if errCo != nil { + return fmt.Errorf("error parsing flag 'redis-acl-commands' for database user create : %v", errCo) + } + + keys, errKe := cmd.Flags().GetStringSlice("redis-acl-keys") + if errKe != nil { + return fmt.Errorf("error parsing flag 'redis-acl-keys' for database user create : %v", errKe) + } + + o.UserUpdateACLReq = &govultr.DatabaseUserACLReq{} + + if cmd.Flags().Changed("redis-acl-categories") { + o.UserUpdateACLReq.RedisACLCategories = &categories + } + + if cmd.Flags().Changed("redis-acl-channels") { + o.UserUpdateACLReq.RedisACLChannels = &channels + } + + if cmd.Flags().Changed("redis-acl-commands") { + o.UserUpdateACLReq.RedisACLCommands = &commands + } + + if cmd.Flags().Changed("redis-acl-keys") { + o.UserUpdateACLReq.RedisACLKeys = &keys + } + + us, err := o.updateUserACL() + if err != nil { + return fmt.Errorf("error updating database user acl : %v", err) + } + + data := &UserPrinter{User: us} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + userACLUpdate.Flags().StringSlice( + "redis-acl-categories", + []string{}, + "list of rules for command categories", + ) + userACLUpdate.Flags().StringSlice( + "redis-acl-channels", + []string{}, + "list of publish/subscribe channel patterns", + ) + userACLUpdate.Flags().StringSlice( + "redis-acl-commands", + []string{}, + "list of rules for individual commands", + ) + userACLUpdate.Flags().StringSlice( + "redis-acl-keys", + []string{}, + "list of key access rules", + ) + + userACLUpdate.MarkFlagsOneRequired( + "redis-acl-categories", + "redis-acl-channels", + "redis-acl-commands", + "redis-acl-keys", + ) + + userACL.AddCommand( + userACLUpdate, + ) + + user.AddCommand( + userList, + userGet, + userCreate, + userUpdate, + userDelete, + userACL, + ) + + // Logical Database + db := &cobra.Command{ + Use: "db", + Short: "Commands to handle database logical dbs", + } + + // Logical DB List + dbList := &cobra.Command{ + Use: "list ", + Short: "List logical databases", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + dbs, meta, err := o.listDBs() + if err != nil { + return fmt.Errorf("error retrieving logical databases: %v", err) + } + + data := &LogicalDBsPrinter{DBs: dbs, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Logical DB Create + dbCreate := &cobra.Command{ + Use: "create ", + Short: "Create a logical database ", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + name, errNa := cmd.Flags().GetString("name") + if errNa != nil { + return fmt.Errorf("error parsing flag 'name' for logical database create : %v", errNa) + } + + o.DBCreateReq = &govultr.DatabaseDBCreateReq{ + Name: name, + } + + db, err := o.createDB() + if err != nil { + return fmt.Errorf("error creating a logical database : %v", err) + } + + data := &LogicalDBPrinter{DB: db} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + dbCreate.Flags().StringP("name", "n", "", "name of the new logical database within the manaaged database") + if err := dbCreate.MarkFlagRequired("name"); err != nil { + fmt.Printf("error marking logical database create 'name' flag required: %v", err) + os.Exit(1) + } + + // Logical DB Delete + dbDel := &cobra.Command{ + Use: "delete ", + Short: "Delete a logical database ", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("please provide a database ID and a DB name") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.delDB(); err != nil { + return fmt.Errorf("error deleting logical database : %v", err) + } + + o.Base.Printer.Display(printer.Info("Logical DB deleted"), nil) + + return nil + }, + } + + db.AddCommand( + dbList, + dbCreate, + dbDel, + ) + + // Usage + usage := &cobra.Command{ + Use: "usage", + Short: "Commands to display database usage information", + } + + // Usage Get + usageGet := &cobra.Command{ + Use: "get ", + Short: "Get database usage", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + us, err := o.getUsage() + if err != nil { + return fmt.Errorf("error retrieving database usage : %v", err) + } + + data := &UsagePrinter{Usage: us} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + usage.AddCommand( + usageGet, + ) + + // Maintenance + maintenance := &cobra.Command{ + Use: "maintenance", + Short: "Commands to handle database maintenance updates", + } + + // Maintenance List + maintenanceList := &cobra.Command{ + Use: "list ", + Short: "List maintenance updates for a database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + upds, err := o.listMaintUpdates() + if err != nil { + return fmt.Errorf("error retrieving database maintenance updates : %v", err) + } + + data := &UpdatesPrinter{Updates: upds} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Maintenance Start + maintenanceStart := &cobra.Command{ + Use: "start ", + Short: "Start database maintenance update", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + message, err := o.startMaintUpdate() + if err != nil { + return fmt.Errorf("error starting database maintenance update: %v", err) + } + + o.Base.Printer.Display(printer.Info(message), nil) + + return nil + }, + } + + maintenance.AddCommand( + maintenanceList, + maintenanceStart, + ) + + // Alert + alert := &cobra.Command{ + Use: "alert", + Short: "Commands to handle database alerts", + } + + // Alert List + alertList := &cobra.Command{ + Use: "list ", + Short: "List database alerts", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + period, errPe := cmd.Flags().GetString("period") + if errPe != nil { + return fmt.Errorf("error parsing flag 'period' for alert list : %v", errPe) + } + + o.AlertsReq = &govultr.DatabaseListAlertsReq{ + Period: period, + } + + als, err := o.listAlerts() + if err != nil { + return fmt.Errorf("error retrieving database alerts : %v", err) + } + + data := &AlertsPrinter{Alerts: als} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + alertList.Flags().StringP( + "period", + "p", + "", + "period (day, week, month, year) for viewing service alerts for a manaaged database", + ) + if err := alertList.MarkFlagRequired("period"); err != nil { + fmt.Printf("error marking alert list 'period' flag required: %v", err) + os.Exit(1) + } + + alert.AddCommand( + alertList, + ) + + // Migration + migration := &cobra.Command{ + Use: "migration", + Short: "Commands to handle database migrations", + } + + // Migration Get + migrationGet := &cobra.Command{ + Use: "get ", + Short: "Get migration status of a database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + mig, err := o.getMigrationStatus() + if err != nil { + return fmt.Errorf("error retrieving database migration status : %v", err) + } + + data := &MigrationPrinter{Migration: mig} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Migration Start + migrationStart := &cobra.Command{ + Use: "start ", + Short: "Get migration status of a database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + host, errHo := cmd.Flags().GetString("host") + if errHo != nil { + return fmt.Errorf("error parsing flag 'encryption' for migration start : %v", errHo) + } + + port, errPo := cmd.Flags().GetInt("port") + if errPo != nil { + return fmt.Errorf("error parsing flag 'encryption' for migration start : %v", errPo) + } + + username, errUs := cmd.Flags().GetString("username") + if errUs != nil { + return fmt.Errorf("error parsing flag 'encryption' for migration start : %v", errUs) + } + + password, errPa := cmd.Flags().GetString("password") + if errPa != nil { + return fmt.Errorf("error parsing flag 'encryption' for migration start : %v", errPa) + } + + database, errDa := cmd.Flags().GetString("database") + if errDa != nil { + return fmt.Errorf("error parsing flag 'encryption' for migration start : %v", errDa) + } + + ignored, errIg := cmd.Flags().GetString("ignored-dbs") + if errIg != nil { + return fmt.Errorf("error parsing flag 'encryption' for migration start : %v", errIg) + } + + ssl, errSs := cmd.Flags().GetBool("ssl") + if errSs != nil { + return fmt.Errorf("error parsing flag 'encryption' for migration start : %v", errSs) + } + + o.MigrationReq = &govultr.DatabaseMigrationStartReq{ + Host: host, + Port: port, + Username: username, + Password: password, + Database: database, + IgnoredDatabases: ignored, + SSL: &ssl, + } + + mig, err := o.startMigration() + if err != nil { + return fmt.Errorf("error retrieving database migration status : %v", err) + } + + data := &MigrationPrinter{Migration: mig} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + migrationStart.Flags().String("host", "", "source host for the manaaged database migration") + migrationStart.Flags().Int("port", 0, "source port for the manaaged database migration") + migrationStart.Flags().String( + "username", + "", + "source username for the manaaged database migration (uses `default` for Redis if omitted)", + ) + migrationStart.Flags().String("password", "", "source password for the manaaged database migration") + migrationStart.Flags().String( + "database", + "", + "source database for the manaaged database migration (MySQL/PostgreSQL only)", + ) + migrationStart.Flags().String( + "ignored-dbs", + "", + "comma-separated list of ignored databases for the manaaged database migration (MySQL/PostgreSQL only)", + ) + migrationStart.Flags().Bool("ssl", true, "source ssl requirement for the manaaged database migration") + + // Migration Detach + migrationDetach := &cobra.Command{ + Use: "detach ", + Short: "Detach a migration from a database ", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.detachMigration(); err != nil { + return fmt.Errorf("error detaching migration from database : %v", err) + } + + o.Base.Printer.Display(printer.Info("Migration detached"), nil) + + return nil + }, + } + + migration.AddCommand( + migrationGet, + migrationStart, + migrationDetach, + ) + + // Read Replica + readReplica := &cobra.Command{ + Use: "read-replica", + Short: "Commands to handle database read replicas", + } + + // Read Replica Add + readReplicaCreate := &cobra.Command{ + Use: "create ", + Short: "Create a read-only replica of a database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + region, errRe := cmd.Flags().GetString("region") + if errRe != nil { + return fmt.Errorf("error parsing flag 'region' for read-replica create : %v", errRe) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for read-replica create : %v", errLa) + } + + o.ReadReplicaCreateReq = &govultr.DatabaseAddReplicaReq{ + Region: region, + Label: label, + } + + rr, err := o.createReadReplica() + if err != nil { + return fmt.Errorf("error creating database read replica: %v", err) + } + + data := &DBPrinter{DB: rr} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + readReplicaCreate.Flags().StringP("region", "r", "", "region id for the new managed database read replica") + if err := readReplicaCreate.MarkFlagRequired("region"); err != nil { + fmt.Printf("error marking read replica create 'region' flag required: %v", err) + os.Exit(1) + } + + readReplicaCreate.Flags().StringP("label", "l", "", "label for the new managed database read replica") + if err := readReplicaCreate.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking read replica create 'label' flag required: %v", err) + os.Exit(1) + } + + // Read Replica Promote + readReplicaPromote := &cobra.Command{ + Use: "promote ", + Short: "Promote a read-only replica of a database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.promoteReadReplica(); err != nil { + return fmt.Errorf("error promoting database read replica: %v", err) + } + + o.Base.Printer.Display(printer.Info("Read replica has been promoted"), nil) + + return nil + }, + } + + readReplica.AddCommand( + readReplicaCreate, + readReplicaPromote, + ) + + // Backup + backup := &cobra.Command{ + Use: "backup", + Short: "Commands to handle database backups, restores and forks", + } + + // Backup Get + backupGet := &cobra.Command{ + Use: "get ", + Short: "Get get latest and oldest database backup", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + bk, err := o.getBackup() + if err != nil { + return fmt.Errorf("error retrieving database backups : %v", err) + } + + data := &BackupPrinter{Backup: bk} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Backup Restore + backupRestore := &cobra.Command{ + Use: "restore ", + Short: "Restore a database backup ", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for backup restore : %v", errLa) + } + + rtype, errRt := cmd.Flags().GetString("type") + if errRt != nil { + return fmt.Errorf("error parsing flag 'type' for backup restore : %v", errRt) + } + + date, errDa := cmd.Flags().GetString("date") + if errDa != nil { + return fmt.Errorf("error parsing flag 'date' for backup restore : %v", errDa) + } + + time, errTi := cmd.Flags().GetString("time") + if errTi != nil { + return fmt.Errorf("error parsing flag 'time' for backup restore : %v", errTi) + } + + o.BackupReq = &govultr.DatabaseBackupRestoreReq{ + Label: label, + Type: rtype, + Date: date, + Time: time, + } + + bk, err := o.restoreBackup() + if err != nil { + return fmt.Errorf("error restoring database from backup : %v", err) + } + + data := &DBPrinter{DB: bk} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + backupRestore.Flags().String("label", "", "label for the new managed database restored from backup") + if err := backupRestore.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking backup restore 'label' flag required: %v", err) + os.Exit(1) + } + + backupRestore.Flags().String( + "type", + "", + "restoration type: `pitr` for point-in-time recovery or `basebackup` for latest backup (default)", + ) + backupRestore.Flags().String("date", "", "backup date to use for point-in-time recovery") + backupRestore.Flags().String("time", "", "backup time to use for point-in-time recovery") + + // Backup Fork + backupFork := &cobra.Command{ + Use: "fork ", + Short: "Fork a database from backup", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + region, errDa := cmd.Flags().GetString("region") + if errDa != nil { + return fmt.Errorf("error parsing flag 'region' for backup fork: %v", errDa) + } + + plan, errPl := cmd.Flags().GetString("plan") + if errPl != nil { + return fmt.Errorf("error parsing flag 'time' for backup fork: %v", errPl) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for backup fork : %v", errLa) + } + + rtype, errRt := cmd.Flags().GetString("type") + if errRt != nil { + return fmt.Errorf("error parsing flag 'type' for backup fork: %v", errRt) + } + + date, errDa := cmd.Flags().GetString("date") + if errDa != nil { + return fmt.Errorf("error parsing flag 'date' for backup fork: %v", errDa) + } + + time, errTi := cmd.Flags().GetString("time") + if errTi != nil { + return fmt.Errorf("error parsing flag 'time' for backup fork: %v", errTi) + } + + o.ForkReq = &govultr.DatabaseForkReq{ + Label: label, + Region: region, + Plan: plan, + Type: rtype, + Date: date, + Time: time, + } + + db, err := o.fork() + if err != nil { + return fmt.Errorf("error forking database from backup : %v", err) + } + + data := &DBPrinter{DB: db} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + backupFork.Flags().String("label", "", "label for the new managed database forked from the backup") + if err := backupFork.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking backup fork 'label' flag required: %v", err) + os.Exit(1) + } + + backupFork.Flags().String("region", "", "region id for the new managed database forked from the backup") + if err := backupFork.MarkFlagRequired("region"); err != nil { + fmt.Printf("error marking backup fork 'region' flag required: %v", err) + os.Exit(1) + } + + backupFork.Flags().String("plan", "", "plan id for the new managed database forked from the backup") + if err := backupFork.MarkFlagRequired("plan"); err != nil { + fmt.Printf("error marking backup fork 'label' flag required: %v", err) + os.Exit(1) + } + + backupFork.Flags().String( + "type", + "", + "restoration type: `pitr` for point-in-time recovery or `basebackup` for latest backup (default)", + ) + backupFork.Flags().String("date", "", "backup date to use for point-in-time recovery") + backupFork.Flags().String("time", "", "backup time to use for point-in-time recovery") + + backup.AddCommand( + backupGet, + backupRestore, + backupFork, + ) + + // Connection Pool + connectionPool := &cobra.Command{ + Use: "connection-pool", + Short: "Commands to handle PostgreSQL database connection pools", + } + + // Connection Pool List + connectionPoolList := &cobra.Command{ + Use: "list ", + Short: "List connection pools within a PostgreSQL managed database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + cns, pools, meta, err := o.listConnectionPools() + if err != nil { + return fmt.Errorf("error retrieving connection pool data : %v", err) + } + + data := &ConnectionsPrinter{Connections: cns, ConnectionPools: pools, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Connection Pool Get + connectionPoolGet := &cobra.Command{ + Use: "get ", + Short: "Get a database connection pool", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("please provide a database ID and a pool name") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + cnp, err := o.getConnectionPool() + if err != nil { + return fmt.Errorf("error retrieving connection pool: %v", err) + } + + data := &ConnectionPoolPrinter{ConnectionPool: cnp} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Connection Pool Create + connectionPoolCreate := &cobra.Command{ + Use: "create ", + Short: "Create a database connection pool", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + name, errNa := cmd.Flags().GetString("name") + if errNa != nil { + return fmt.Errorf("error parsing flag 'name' for connection pool create : %v", errNa) + } + + database, errDa := cmd.Flags().GetString("database") + if errDa != nil { + return fmt.Errorf("error parsing flag 'database' for connection pool create : %v", errDa) + } + + username, errUs := cmd.Flags().GetString("username") + if errUs != nil { + return fmt.Errorf("error parsing flag 'username' for connection pool create : %v", errUs) + } + + mode, errMo := cmd.Flags().GetString("mode") + if errMo != nil { + return fmt.Errorf("error parsing flag 'mode' for connection pool create : %v", errMo) + } + + size, errSi := cmd.Flags().GetInt("size") + if errSi != nil { + return fmt.Errorf("error parsing flag 'size' for connection pool create : %v", errSi) + } + + o.ConnectionPoolCreateReq = &govultr.DatabaseConnectionPoolCreateReq{ + Name: name, + Database: database, + Username: username, + Mode: mode, + Size: size, + } + + cnp, err := o.createConnectionPool() + if err != nil { + return fmt.Errorf("error creating connection pool: %v", err) + } + + data := &ConnectionPoolPrinter{ConnectionPool: cnp} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + connectionPoolCreate.Flags().StringP("name", "n", "", "name for the new managed database connection pool") + if err := connectionPoolCreate.MarkFlagRequired("name"); err != nil { + fmt.Printf("error marking connection pool create 'name' flag required: %v", err) + os.Exit(1) + } + + connectionPoolCreate.Flags().StringP("database", "d", "", "database for the new managed database connection pool") + if err := connectionPoolCreate.MarkFlagRequired("database"); err != nil { + fmt.Printf("error marking connection pool create 'database' flag required: %v", err) + os.Exit(1) + } + + connectionPoolCreate.Flags().StringP("username", "u", "", "username for the new managed database connection pool") + if err := connectionPoolCreate.MarkFlagRequired("username"); err != nil { + fmt.Printf("error marking connection pool create 'username' flag required: %v", err) + os.Exit(1) + } + + connectionPoolCreate.Flags().StringP("mode", "m", "", "mode for the new managed database connection pool") + if err := connectionPoolCreate.MarkFlagRequired("mode"); err != nil { + fmt.Printf("error marking connection pool create 'mode' flag required: %v", err) + os.Exit(1) + } + + connectionPoolCreate.Flags().IntP("size", "s", 0, "size for the new managed database connection pool") + if err := connectionPoolCreate.MarkFlagRequired("size"); err != nil { + fmt.Printf("error marking connection pool create 'size' flag required: %v", err) + os.Exit(1) + } + + // Connection Pool Update + connectionPoolUpdate := &cobra.Command{ + Use: "update ", + Short: "Update a database connection pool", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("please provide a database ID pool name") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + database, errDa := cmd.Flags().GetString("database") + if errDa != nil { + return fmt.Errorf("error parsing flag 'database' for connection pool update : %v", errDa) + } + + username, errUs := cmd.Flags().GetString("username") + if errUs != nil { + return fmt.Errorf("error parsing flag 'username' for connection pool update : %v", errUs) + } + + mode, errMo := cmd.Flags().GetString("mode") + if errMo != nil { + return fmt.Errorf("error parsing flag 'mode' for connection pool update : %v", errMo) + } + + size, errSi := cmd.Flags().GetInt("size") + if errSi != nil { + return fmt.Errorf("error parsing flag 'size' for connection pool update : %v", errSi) + } + + o.ConnectionPoolCreateReq = &govultr.DatabaseConnectionPoolCreateReq{} + + if cmd.Flags().Changed("database") { + o.ConnectionPoolCreateReq.Database = database + } + + if cmd.Flags().Changed("username") { + o.ConnectionPoolCreateReq.Username = username + } + + if cmd.Flags().Changed("mode") { + o.ConnectionPoolCreateReq.Mode = mode + } + + if cmd.Flags().Changed("size") { + o.ConnectionPoolCreateReq.Size = size + } + + cnp, err := o.updateConnectionPool() + if err != nil { + return fmt.Errorf("error updating connection pool : %v", err) + } + + data := &ConnectionPoolPrinter{ConnectionPool: cnp} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + connectionPoolUpdate.Flags().StringP("database", "d", "", "database for the managed database connection pool") + connectionPoolUpdate.Flags().StringP("username", "u", "", "username for the managed database connection pool") + connectionPoolUpdate.Flags().StringP("mode", "m", "", "mode for the managed database connection pool") + connectionPoolUpdate.Flags().IntP("size", "s", 0, "size for the managed database connection pool") + + connectionPoolUpdate.MarkFlagsOneRequired( + "database", + "username", + "mode", + "size", + ) + + // Connection Pool Delete + connectionPoolDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete a database connection pool", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("please provide a database ID and a pool name") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.delConnectionPool(); err != nil { + return fmt.Errorf("error deleting connection pool : %v", err) + } + + o.Base.Printer.Display(printer.Info("Connection pool has been deleted"), nil) + + return nil + }, + } + + connectionPool.AddCommand( + connectionPoolList, + connectionPoolGet, + connectionPoolCreate, + connectionPoolUpdate, + connectionPoolDelete, + ) + + // Advanced Option + advancedOption := &cobra.Command{ + Use: "advanced-option", + Short: "Commands to handle PostgreSQL database advanced options", + } + + // Advanced Option List + advancedOptionList := &cobra.Command{ + Use: "list ", + Short: "List all available and configured advanced options for a PostgreSQL database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + cur, avail, err := o.listAdvancedOptions() + if err != nil { + return fmt.Errorf("error retrieving database options : %v", err) + } + + data := &AdvancedOptionsPrinter{Configured: cur, Available: avail} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Advanced Option Update + advancedOptionUpdate := &cobra.Command{ + Use: "update ", + Short: "Update advanced options for a PostgreSQL managed database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + autovacuumAnalyzeScaleFactor, errAu := cmd.Flags().GetFloat32("autovacuum-analyze-scale-factor") + if errAu != nil { + return fmt.Errorf("error parsing flag 'autovacuum-analyze-scale-factor' for advanced options update : %v", errAu) + } + + autovacuumAnalyzeThreshold, errAt := cmd.Flags().GetInt("autovacuum-analyze-threshold") + if errAt != nil { + return fmt.Errorf("error parsing flag 'autovacuum-analyze-threshold' for advanced options update : %v", errAt) + } + + autovacuumFreezeMaxAge, errAo := cmd.Flags().GetInt("autovacuum-freeze-max-age") + if errAo != nil { + return fmt.Errorf("error parsing flag 'autovacuum-freeze-max-age' for advanced options update : %v", errAo) + } + + autovacuumMaxWorkers, errAv := cmd.Flags().GetInt("autovacuum-max-workers") + if errAv != nil { + return fmt.Errorf("error parsing flag 'autovacuum-max-workers' for advanced options update : %v", errAv) + } + + autovacuumNaptime, errAa := cmd.Flags().GetInt("autovacuum-naptime") + if errAa != nil { + return fmt.Errorf("error parsing flag 'autovacuum-naptime' for advanced options update : %v", errAa) + } + + autovacuumVacuumCostDelay, errAc := cmd.Flags().GetInt("autovacuum-vacuum-cost-delay") + if errAc != nil { + return fmt.Errorf("error parsing flag 'autovacuum-vacuum-cost-delay' for advanced options update : %v", errAc) + } + + autovacuumVacuumCostLimit, errAm := cmd.Flags().GetInt("autovacuum-vacuum-cost-limit") + if errAm != nil { + return fmt.Errorf("error parsing flag 'autovacuum-vacuum-cost-limit' for advanced options update : %v", errAm) + } + + autovacuumVacuumScaleFactor, errAb := cmd.Flags().GetFloat32("autovacuum-vacuum-scale-factor") + if errAb != nil { + return fmt.Errorf("error parsing flag 'autovacuum-vacuum-scale-factor' for advanced options update : %v", errAb) + } + + autovacuumVacuumThreshold, errAz := cmd.Flags().GetInt("autovacuum-vacuum-threshold") + if errAz != nil { + return fmt.Errorf("error parsing flag 'autovacuum-vacuum-threshold' for advanced options update : %v", errAz) + } + + bgwriterDelay, errBg := cmd.Flags().GetInt("bgwriter-delay") + if errBg != nil { + return fmt.Errorf("error parsing flag 'bgwriter-delay' for advanced options update : %v", errBg) + } + + bgwriterFlushAfter, errBw := cmd.Flags().GetInt("bgwriter-flush-after") + if errBw != nil { + return fmt.Errorf("error parsing flag 'bgwriter-flush-after' for advanced options update : %v", errBw) + } + + bgwriterLruMaxpages, errBr := cmd.Flags().GetInt("bgwriter-lru-maxpages") + if errBr != nil { + return fmt.Errorf("error parsing flag 'bgwriter-lru-maxpages' for advanced options update : %v", errBr) + } + + bgwriterLruMultiplier, errBi := cmd.Flags().GetFloat32("bgwriter-lru-multiplier") + if errBi != nil { + return fmt.Errorf("error parsing flag 'bgwriter-lru-multiplier' for advanced options update : %v", errBi) + } + + deadlockTimeout, errDe := cmd.Flags().GetInt("deadlock-timeout") + if errDe != nil { + return fmt.Errorf("error parsing flag 'deadlock-timeout' for advanced options update : %v", errDe) + } + + defaultToastCompression, errDf := cmd.Flags().GetString("default-toast-compression") + if errDf != nil { + return fmt.Errorf("error parsing flag 'default-toast-compression' for advanced options update : %v", errDf) + } + + idleInTransactionSessionTimeout, errIl := cmd.Flags().GetInt("idle-in-transaction-session-timeout") + if errIl != nil { + return fmt.Errorf("error parsing flag 'idle-in-transaction-session-timeout' for advanced options update : %v", errIl) + } + + jit, errJi := cmd.Flags().GetBool("jit") + if errJi != nil { + return fmt.Errorf("error parsing flag 'jit' for advanced options update : %v", errJi) + } + + logAutovacuumMinDuration, errLo := cmd.Flags().GetInt("log-autovacuum-min-duration") + if errLo != nil { + return fmt.Errorf("error parsing flag 'log-autovacuum-min-duration' for advanced options update : %v", errLo) + } + + logErrorVerbosity, errLg := cmd.Flags().GetString("log-error-verbosity") + if errLg != nil { + return fmt.Errorf("error parsing flag 'log-error-verbosity' for advanced options update : %v", errLg) + } + + logLinePrefix, errLl := cmd.Flags().GetString("log-line-prefix") + if errLl != nil { + return fmt.Errorf("error parsing flag 'log-line-prefix' for advanced options update : %v", errLl) + } + + logMinDurationStatement, errLm := cmd.Flags().GetInt("log-min-duration-statement") + if errLm != nil { + return fmt.Errorf("error parsing flag 'log-min-duration-statement' for advanced options update : %v", errLm) + } + + maxFilesPerProcess, errMa := cmd.Flags().GetInt("max-files-per-process") + if errMa != nil { + return fmt.Errorf("error parsing flag 'max-files-per-process' for advanced options update : %v", errMa) + } + + maxLocksPerTransaction, errMx := cmd.Flags().GetInt("max-locks-per-transaction") + if errMx != nil { + return fmt.Errorf("error parsing flag 'max-locks-per-transaction' for advanced options update : %v", errMx) + } + + maxLogicalReplicationWorkers, errMl := cmd.Flags().GetInt("max-logical-replication-workers") + if errMl != nil { + return fmt.Errorf("error parsing flag 'max-logical-replication-workers' for advanced options update : %v", errMl) + } + + maxParallelWorkers, errMo := cmd.Flags().GetInt("max-parallel-workers") + if errMo != nil { + return fmt.Errorf("error parsing flag 'max-parallel-workers' for advanced options update : %v", errMo) + } + + maxParallelWorkersPerGather, errMp := cmd.Flags().GetInt("max-parallel-workers-per-gather") + if errMp != nil { + return fmt.Errorf("error parsing flag 'max-parallel-workers-per-gather' for advanced options update : %v", errMp) + } + + maxPredLocksPerTransaction, errMr := cmd.Flags().GetInt("max-pred-locks-per-transaction") + if errMr != nil { + return fmt.Errorf("error parsing flag 'max-pred-locks-per-transaction' for advanced options update : %v", errMr) + } + + maxPreparedTransactions, errMe := cmd.Flags().GetInt("max-prepared-transactions") + if errMe != nil { + return fmt.Errorf("error parsing flag 'max-prepared-transactions' for advanced options update : %v", errMe) + } + + maxReplicationSlots, errMi := cmd.Flags().GetInt("max-replication-slots") + if errMi != nil { + return fmt.Errorf("error parsing flag 'max-replication-slots' for advanced options update : %v", errMi) + } + + maxStackDepth, errMs := cmd.Flags().GetInt("max-stack-depth") + if errMs != nil { + return fmt.Errorf("error parsing flag 'max-stack-depth' for advanced options update : %v", errMs) + } + + maxStandbyArchiveDelay, errMv := cmd.Flags().GetInt("max-standby-archive-delay") + if errMv != nil { + return fmt.Errorf("error parsing flag 'max-standby-archive-delay' for advanced options update : %v", errMv) + } + + maxStandbyStreamingDelay, errMy := cmd.Flags().GetInt("max-standby-streaming-delay") + if errMy != nil { + return fmt.Errorf("error parsing flag 'max-standby-streaming-delay' for advanced options update : %v", errMy) + } + + maxWalSenders, errMd := cmd.Flags().GetInt("max-wal-senders") + if errMd != nil { + return fmt.Errorf("error parsing flag 'max-wal-senders' for advanced options update : %v", errMd) + } + + maxWorkerProcesses, errMs := cmd.Flags().GetInt("max-worker-processes") + if errMs != nil { + return fmt.Errorf("error parsing flag 'max-worker-processes' for advanced options update : %v", errMs) + } + + pgPartmanBGWInterval, errPg := cmd.Flags().GetInt("pg-partman-bgw-interval") + if errPg != nil { + return fmt.Errorf("error parsing flag 'pg-partman-bgw-interval' for advanced options update : %v", errPg) + } + + pgPartmanBGWRole, errPp := cmd.Flags().GetString("pg-partman-bgw-role") + if errPp != nil { + return fmt.Errorf("error parsing flag 'pg-partman-bgw-role' for advanced options update : %v", errPp) + } + + pgStatStatementsTrack, errPs := cmd.Flags().GetString("pg-stat-statements-track") + if errPs != nil { + return fmt.Errorf("error parsing flag 'pg-stat-statements-track' for advanced options update : %v", errPs) + } + + tempFileLimit, errTe := cmd.Flags().GetInt("temp-file-limit") + if errTe != nil { + return fmt.Errorf("error parsing flag 'temp-file-limit' for advanced options update : %v", errTe) + } + + trackActivityQuerySize, errTr := cmd.Flags().GetInt("track-activity-query-size") + if errTr != nil { + return fmt.Errorf("error parsing flag 'track-activity-query-size' for advanced options update : %v", errTr) + } + + trackCommitTimestamp, errTa := cmd.Flags().GetString("track-commit-timestamp") + if errTa != nil { + return fmt.Errorf("error parsing flag 'track-commit-timestamp' for advanced options update : %v", errTa) + } + + trackFunctions, errTc := cmd.Flags().GetString("track-functions") + if errTc != nil { + return fmt.Errorf("error parsing flag 'track-functions' for advanced options update : %v", errTc) + } + + trackIOTiming, errTi := cmd.Flags().GetString("track-io-timing") + if errTi != nil { + return fmt.Errorf("error parsing flag 'track-io-timing' for advanced options update : %v", errTi) + } + + walSenderTimeout, errWa := cmd.Flags().GetInt("wal-sender-timeout") + if errWa != nil { + return fmt.Errorf("error parsing flag 'wal-sender-timeout' for advanced options update : %v", errWa) + } + + walWriterDelay, errWl := cmd.Flags().GetInt("wal-writer-delay") + if errWl != nil { + return fmt.Errorf("error parsing flag 'wal-writer-delay' for advanced options update : %v", errWl) + } + + o.AdvancedOptionsReq = &govultr.DatabaseAdvancedOptions{} + + if cmd.Flags().Changed("autovacuum-analyze-scale-factor") { + o.AdvancedOptionsReq.AutovacuumAnalyzeScaleFactor = autovacuumAnalyzeScaleFactor + } + + if cmd.Flags().Changed("autovacuum-analyze-threshold") { + o.AdvancedOptionsReq.AutovacuumAnalyzeThreshold = autovacuumAnalyzeThreshold + } + + if cmd.Flags().Changed("autovacuum-freeze-max-age") { + o.AdvancedOptionsReq.AutovacuumFreezeMaxAge = autovacuumFreezeMaxAge + } + + if cmd.Flags().Changed("autovacuum-max-workers") { + o.AdvancedOptionsReq.AutovacuumMaxWorkers = autovacuumMaxWorkers + } + + if cmd.Flags().Changed("autovacuum-naptime") { + o.AdvancedOptionsReq.AutovacuumNaptime = autovacuumNaptime + } + + if cmd.Flags().Changed("autovacuum-vacuum-cost-delay") { + o.AdvancedOptionsReq.AutovacuumVacuumCostDelay = autovacuumVacuumCostDelay + } + + if cmd.Flags().Changed("autovacuum-vacuum-cost-limit") { + o.AdvancedOptionsReq.AutovacuumVacuumCostLimit = autovacuumVacuumCostLimit + } + + if cmd.Flags().Changed("autovacuum-vacuum-scale-factor") { + o.AdvancedOptionsReq.AutovacuumVacuumScaleFactor = autovacuumVacuumScaleFactor + } + + if cmd.Flags().Changed("autovacuum-vacuum-threshold") { + o.AdvancedOptionsReq.AutovacuumVacuumThreshold = autovacuumVacuumThreshold + } + + if cmd.Flags().Changed("bgwriter-delay") { + o.AdvancedOptionsReq.BGWRITERDelay = bgwriterDelay + } + + if cmd.Flags().Changed("bgwriter-flush-after") { + o.AdvancedOptionsReq.BGWRITERFlushAFter = bgwriterFlushAfter + } + + if cmd.Flags().Changed("bgwriter-lru-maxpages") { + o.AdvancedOptionsReq.BGWRITERLRUMaxPages = bgwriterLruMaxpages + } + + if cmd.Flags().Changed("bgwriter-lru-multiplier") { + o.AdvancedOptionsReq.BGWRITERLRUMultiplier = bgwriterLruMultiplier + } + + if cmd.Flags().Changed("deadlock-timeout") { + o.AdvancedOptionsReq.DeadlockTimeout = deadlockTimeout + } + + if cmd.Flags().Changed("default-toast-compression") { + o.AdvancedOptionsReq.DefaultToastCompression = defaultToastCompression + } + + if cmd.Flags().Changed("idle-in-transaction-session-timeout") { + o.AdvancedOptionsReq.IdleInTransactionSessionTimeout = idleInTransactionSessionTimeout + } + + if cmd.Flags().Changed("jit") { + o.AdvancedOptionsReq.Jit = nil + } + + if cmd.Flags().Changed("log-autovacuum-min-duration") { + o.AdvancedOptionsReq.LogAutovacuumMinDuration = logAutovacuumMinDuration + } + + if cmd.Flags().Changed("log-error-verbosity") { + o.AdvancedOptionsReq.LogErrorVerbosity = logErrorVerbosity + } + + if cmd.Flags().Changed("log-line-prefix") { + o.AdvancedOptionsReq.LogLinePrefix = logLinePrefix + } + + if cmd.Flags().Changed("log-min-duration-statement") { + o.AdvancedOptionsReq.LogMinDurationStatement = logMinDurationStatement + } + + if cmd.Flags().Changed("max-files-per-process") { + o.AdvancedOptionsReq.MaxFilesPerProcess = maxFilesPerProcess + } + + if cmd.Flags().Changed("max-locks-per-transaction") { + o.AdvancedOptionsReq.MaxLocksPerTransaction = maxLocksPerTransaction + } + + if cmd.Flags().Changed("max-logical-replication-workers") { + o.AdvancedOptionsReq.MaxLogicalReplicationWorkers = maxLogicalReplicationWorkers + } + + if cmd.Flags().Changed("max-parallel-workers") { + o.AdvancedOptionsReq.MaxParallelWorkers = maxParallelWorkers + } + + if cmd.Flags().Changed("max-parallel-workers-per-gather") { + o.AdvancedOptionsReq.MaxParallelWorkersPerGather = maxParallelWorkersPerGather + } + + if cmd.Flags().Changed("max-pred-locks-per-transaction") { + o.AdvancedOptionsReq.MaxPredLocksPerTransaction = maxPredLocksPerTransaction + } + + if cmd.Flags().Changed("max-prepared-transactions") { + o.AdvancedOptionsReq.MaxPreparedTransactions = maxPreparedTransactions + } + + if cmd.Flags().Changed("max-replication-slots") { + o.AdvancedOptionsReq.MaxReplicationSlots = maxReplicationSlots + } + + if cmd.Flags().Changed("max-stack-depth") { + o.AdvancedOptionsReq.MaxStackDepth = maxStackDepth + } + + if cmd.Flags().Changed("max-standby-archive-delay") { + o.AdvancedOptionsReq.MaxStandbyArchiveDelay = maxStandbyArchiveDelay + } + + if cmd.Flags().Changed("max-standby-streaming-delay") { + o.AdvancedOptionsReq.MaxStandbyStreamingDelay = maxStandbyStreamingDelay + } + + if cmd.Flags().Changed("max-wal-senders") { + o.AdvancedOptionsReq.MaxWalSenders = maxWalSenders + } + + if cmd.Flags().Changed("max-worker-processes") { + o.AdvancedOptionsReq.MaxWorkerProcesses = maxWorkerProcesses + } + + if cmd.Flags().Changed("pg-partman-bgw-interval") { + o.AdvancedOptionsReq.PGPartmanBGWInterval = pgPartmanBGWInterval + } + + if cmd.Flags().Changed("pg-partman-bgw-role") { + o.AdvancedOptionsReq.PGPartmanBGWRole = pgPartmanBGWRole + } + + if cmd.Flags().Changed("pg-stat-statements-track") { + o.AdvancedOptionsReq.PGStateStatementsTrack = pgStatStatementsTrack + } + + if cmd.Flags().Changed("temp-file-limit") { + o.AdvancedOptionsReq.TempFileLimit = tempFileLimit + } + + if cmd.Flags().Changed("track-activity-query-size") { + o.AdvancedOptionsReq.TrackActivityQuerySize = trackActivityQuerySize + } + + if cmd.Flags().Changed("track-commit-timestamp") { + o.AdvancedOptionsReq.TrackCommitTimestamp = trackCommitTimestamp + } + + if cmd.Flags().Changed("track-functions") { + o.AdvancedOptionsReq.TrackFunctions = trackFunctions + } + + if cmd.Flags().Changed("track-io-timing") { + o.AdvancedOptionsReq.TrackIOTiming = trackIOTiming + } + + if cmd.Flags().Changed("wal-sender-timeout") { + o.AdvancedOptionsReq.WALSenderTImeout = walSenderTimeout + } + + if cmd.Flags().Changed("wal-writer-delay") { + o.AdvancedOptionsReq.WALWriterDelay = walWriterDelay + } + + if cmd.Flags().Changed("jit") { + o.AdvancedOptionsReq.Jit = &jit + } + + cur, avail, err := o.updateAdvancedOptions() + if err != nil { + return fmt.Errorf("error updating database advanced options : %v", err) + } + + data := &AdvancedOptionsPrinter{Configured: cur, Available: avail} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + advancedOptionUpdate.Flags().Float32( + "autovacuum-analyze-scale-factor", + 0, + "set the managed postgresql configuration value for autovacuum_analyze_scale_factor", + ) + advancedOptionUpdate.Flags().Int( + "autovacuum-analyze-threshold", + 0, + "set the managed postgresql configuration value for autovacuum_analyze_threshold", + ) + advancedOptionUpdate.Flags().Int( + "autovacuum-freeze-max-age", + 0, + "set the managed postgresql configuration value for autovacuum_freeze_max_age", + ) + advancedOptionUpdate.Flags().Int( + "autovacuum-max-workers", + 0, + "set the managed postgresql configuration value for autovacuum_max_workers", + ) + advancedOptionUpdate.Flags().Int( + "autovacuum-naptime", + 0, + "set the managed postgresql configuration value for autovacuum_naptime", + ) + advancedOptionUpdate.Flags().Int( + "autovacuum-vacuum-cost-delay", + 0, + "set the managed postgresql configuration value for autovacuum_vacuum_cost_delay", + ) + advancedOptionUpdate.Flags().Int( + "autovacuum-vacuum-cost-limit", + 0, + "set the managed postgresql configuration value for autovacuum_vacuum_cost_limit", + ) + advancedOptionUpdate.Flags().Float32( + "autovacuum-vacuum-scale-factor", + 0, + "set the managed postgresql configuration value for autovacuum_vacuum_scale_factor", + ) + advancedOptionUpdate.Flags().Int( + "autovacuum-vacuum-threshold", + 0, + "set the managed postgresql configuration value for autovacuum_vacuum_threshold", + ) + advancedOptionUpdate.Flags().Int( + "bgwriter-delay", + 0, + "set the managed postgresql configuration value for bgwriter_delay", + ) + advancedOptionUpdate.Flags().Int( + "bgwriter-flush-after", + 0, + "set the managed postgresql configuration value for bgwriter_flush_after", + ) + advancedOptionUpdate.Flags().Int( + "bgwriter-lru-maxpages", + 0, + "set the managed postgresql configuration value for bgwriter_lru_maxpages", + ) + advancedOptionUpdate.Flags().Float32( + "bgwriter-lru-multiplier", + 0, + "set the managed postgresql configuration value for bgwriter_lru_multiplier", + ) + advancedOptionUpdate.Flags().Int( + "deadlock-timeout", + 0, + "set the managed postgresql configuration value for deadlock_timeout", + ) + advancedOptionUpdate.Flags().String( + "default-toast-compression", + "", + "set the managed postgresql configuration value for default_toast_compression", + ) + advancedOptionUpdate.Flags().Int( + "idle-in-transaction-session-timeout", + 0, + "set the managed postgresql configuration value for idle_in_transaction_session_timeout", + ) + advancedOptionUpdate.Flags().Bool( + "jit", + false, + "set the managed postgresql configuration value for jit", + ) + advancedOptionUpdate.Flags().Int( + "log-autovacuum-min-duration", + 0, + "set the managed postgresql configuration value for log_autovacuum_min_duration", + ) + advancedOptionUpdate.Flags().String( + "log-error-verbosity", + "", + "set the managed postgresql configuration value for log_error_verbosity", + ) + advancedOptionUpdate.Flags().String( + "log-line-prefix", + "", + "set the managed postgresql configuration value for log_line_prefix", + ) + advancedOptionUpdate.Flags().Int( + "log-min-duration-statement", + 0, + "set the managed postgresql configuration value for log_min_duration_statement", + ) + advancedOptionUpdate.Flags().Int( + "max-files-per-process", + 0, + "set the managed postgresql configuration value for max_files_per_process", + ) + advancedOptionUpdate.Flags().Int( + "max-locks-per-transaction", + 0, + "set the managed postgresql configuration value for max_locks_per_transaction", + ) + advancedOptionUpdate.Flags().Int( + "max-logical-replication-workers", + 0, + "set the managed postgresql configuration value for max_logical_replication_workers", + ) + advancedOptionUpdate.Flags().Int( + "max-parallel-workers", + 0, + "set the managed postgresql configuration value for max_parallel_workers", + ) + advancedOptionUpdate.Flags().Int( + "max-parallel-workers-per-gather", + 0, + "set the managed postgresql configuration value for max_parallel_workers_per_gather", + ) + advancedOptionUpdate.Flags().Int( + "max-pred-locks-per-transaction", + 0, + "set the managed postgresql configuration value for max_pred_locks_per_transaction", + ) + advancedOptionUpdate.Flags().Int( + "max-prepared-transactions", + 0, + "set the managed postgresql configuration value for max_prepared_transactions", + ) + advancedOptionUpdate.Flags().Int( + "max-replication-slots", + 0, + "set the managed postgresql configuration value for max_replication_slots", + ) + advancedOptionUpdate.Flags().Int( + "max-stack-depth", + 0, + "set the managed postgresql configuration value for max_stack_depth", + ) + advancedOptionUpdate.Flags().Int( + "max-standby-archive-delay", + 0, + "set the managed postgresql configuration value for max_standby_archive_delay", + ) + advancedOptionUpdate.Flags().Int( + "max-standby-streaming-delay", + 0, + "set the managed postgresql configuration value for max_standby_streaming_delay", + ) + advancedOptionUpdate.Flags().Int( + "max-wal-senders", + 0, + "set the managed postgresql configuration value for max_wal_senders", + ) + advancedOptionUpdate.Flags().Int( + "max-worker-processes", + 0, + "set the managed postgresql configuration value for max_worker_processes", + ) + advancedOptionUpdate.Flags().Int( + "pg-partman-bgw-interval", + 0, + "set the managed postgresql configuration value for pg_partman_bgw.interval", + ) + advancedOptionUpdate.Flags().String( + "pg-partman-bgw-role", + "", + "set the managed postgresql configuration value for pg_partman_bgw.role", + ) + advancedOptionUpdate.Flags().String( + "pg-stat-statements-track", + "", + "set the managed postgresql configuration value for pg_stat_statements.track", + ) + advancedOptionUpdate.Flags().Int( + "temp-file-limit", + 0, + "set the managed postgresql configuration value for temp_file_limit", + ) + advancedOptionUpdate.Flags().Int( + "track-activity-query-size", + 0, + "set the managed postgresql configuration value for track_activity_query_size", + ) + advancedOptionUpdate.Flags().String( + "track-commit-timestamp", + "", + "set the managed postgresql configuration value for track_commit_timestamp", + ) + advancedOptionUpdate.Flags().String( + "track-functions", + "", + "set the managed postgresql configuration value for track_functions", + ) + advancedOptionUpdate.Flags().String( + "track-io-timing", + "", + "set the managed postgresql configuration value for track_io_timing", + ) + advancedOptionUpdate.Flags().Int( + "wal-sender-timeout", + 0, + "set the managed postgresql configuration value for wal_sender_timeout", + ) + advancedOptionUpdate.Flags().Int( + "wal-writer-delay", + 0, + "set the managed postgresql configuration value for wal_writer_delay", + ) + + advancedOption.AddCommand( + advancedOptionList, + advancedOptionUpdate, + ) + + // Version + version := &cobra.Command{ + Use: "version", + Short: "Commands to handle database version upgrades", + } + + // Version List + versionList := &cobra.Command{ + Use: "list ", + Short: "List all version upgrades for a database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + vs, err := o.listVersions() + if err != nil { + return fmt.Errorf("error retrieving database versions : %v", err) + } + + data := &VersionsPrinter{Versions: vs} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Version Upgrade + versionUpgrade := &cobra.Command{ + Use: "upgrade ", + Short: "Start a version upgrade on a database", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("please provide a database ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + version, errVe := cmd.Flags().GetString("version") + if errVe != nil { + return fmt.Errorf("error parsing flag 'version' for database upgrade : %v", errVe) + } + + o.UpgradeReq = &govultr.DatabaseVersionUpgradeReq{ + Version: version, + } + + msg, err := o.upgradeVersion() + if err != nil { + return fmt.Errorf("error starting database version upgrade : %v", err) + } + + o.Base.Printer.Display(printer.Info(msg), nil) + + return nil + }, + } + + versionUpgrade.Flags().StringP("version", "v", "", "version of the manaaged database to upgrade to") + if err := versionUpgrade.MarkFlagRequired("version"); err != nil { + fmt.Printf("error marking version upgrade 'version' flag required: %v", err) + os.Exit(1) + } + + version.AddCommand( + versionList, + versionUpgrade, + ) + + cmd.AddCommand( + list, + get, + create, + update, + del, + user, + db, + usage, + maintenance, + plan, + alert, + migration, + readReplica, + backup, + connectionPool, + advancedOption, + version, + ) + + return cmd +} + +type options struct { + Base *cli.Base + CreateReq *govultr.DatabaseCreateReq + UpdateReq *govultr.DatabaseUpdateReq + UserCreateReq *govultr.DatabaseUserCreateReq + UserUpdateReq *govultr.DatabaseUserUpdateReq + UserUpdateACLReq *govultr.DatabaseUserACLReq + DBCreateReq *govultr.DatabaseDBCreateReq + AlertsReq *govultr.DatabaseListAlertsReq + MigrationReq *govultr.DatabaseMigrationStartReq + ReadReplicaCreateReq *govultr.DatabaseAddReplicaReq + BackupReq *govultr.DatabaseBackupRestoreReq + ForkReq *govultr.DatabaseForkReq + ConnectionPoolCreateReq *govultr.DatabaseConnectionPoolCreateReq + ConnectionPoolUpdateReq *govultr.DatabaseConnectionPoolUpdateReq + AdvancedOptionsReq *govultr.DatabaseAdvancedOptions + UpgradeReq *govultr.DatabaseVersionUpgradeReq +} + +func (o *options) list() ([]govultr.Database, *govultr.Meta, error) { + dbs, meta, _, err := o.Base.Client.Database.List(o.Base.Context, nil) + return dbs, meta, err +} + +func (o *options) get() (*govultr.Database, error) { + db, _, err := o.Base.Client.Database.Get(o.Base.Context, o.Base.Args[0]) + return db, err +} + +func (o *options) create() (*govultr.Database, error) { + db, _, err := o.Base.Client.Database.Create(o.Base.Context, o.CreateReq) + return db, err +} + +func (o *options) update() (*govultr.Database, error) { + db, _, err := o.Base.Client.Database.Update(o.Base.Context, o.Base.Args[0], o.UpdateReq) + return db, err +} + +func (o *options) del() error { + return o.Base.Client.Database.Delete(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) listPlans() ([]govultr.DatabasePlan, *govultr.Meta, error) { + plans, meta, _, err := o.Base.Client.Database.ListPlans(o.Base.Context, nil) + return plans, meta, err +} + +func (o *options) listUsers() ([]govultr.DatabaseUser, *govultr.Meta, error) { + users, meta, _, err := o.Base.Client.Database.ListUsers(o.Base.Context, o.Base.Args[0]) + return users, meta, err +} + +func (o *options) getUser() (*govultr.DatabaseUser, error) { + user, _, err := o.Base.Client.Database.GetUser(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) + return user, err +} + +func (o *options) createUser() (*govultr.DatabaseUser, error) { + user, _, err := o.Base.Client.Database.CreateUser(o.Base.Context, o.Base.Args[0], o.UserCreateReq) + return user, err +} + +func (o *options) updateUser() (*govultr.DatabaseUser, error) { + user, _, err := o.Base.Client.Database.UpdateUser(o.Base.Context, o.Base.Args[0], o.Base.Args[1], o.UserUpdateReq) + return user, err +} + +func (o *options) delUser() error { + return o.Base.Client.Database.DeleteUser(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) updateUserACL() (*govultr.DatabaseUser, error) { + user, _, err := o.Base.Client.Database.UpdateUserACL(o.Base.Context, o.Base.Args[0], o.Base.Args[1], o.UserUpdateACLReq) + return user, err +} + +func (o *options) listDBs() ([]govultr.DatabaseDB, *govultr.Meta, error) { + dbs, meta, _, err := o.Base.Client.Database.ListDBs(o.Base.Context, o.Base.Args[0]) + return dbs, meta, err +} + +func (o *options) createDB() (*govultr.DatabaseDB, error) { + db, _, err := o.Base.Client.Database.CreateDB(o.Base.Context, o.Base.Args[0], o.DBCreateReq) + return db, err +} + +func (o *options) delDB() error { + return o.Base.Client.Database.DeleteDB(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) getUsage() (*govultr.DatabaseUsage, error) { + usage, _, err := o.Base.Client.Database.GetUsage(o.Base.Context, o.Base.Args[0]) + return usage, err +} + +func (o *options) listMaintUpdates() ([]string, error) { + updates, _, err := o.Base.Client.Database.ListMaintenanceUpdates(o.Base.Context, o.Base.Args[0]) + return updates, err +} + +func (o *options) startMaintUpdate() (string, error) { + updates, _, err := o.Base.Client.Database.StartMaintenance(o.Base.Context, o.Base.Args[0]) + return updates, err +} + +func (o *options) listAlerts() ([]govultr.DatabaseAlert, error) { + alerts, _, err := o.Base.Client.Database.ListServiceAlerts(o.Base.Context, o.Base.Args[0], o.AlertsReq) + return alerts, err +} + +func (o *options) getMigrationStatus() (*govultr.DatabaseMigration, error) { + status, _, err := o.Base.Client.Database.GetMigrationStatus(o.Base.Context, o.Base.Args[0]) + return status, err +} + +func (o *options) startMigration() (*govultr.DatabaseMigration, error) { + status, _, err := o.Base.Client.Database.StartMigration(o.Base.Context, o.Base.Args[0], o.MigrationReq) + return status, err +} + +func (o *options) detachMigration() error { + return o.Base.Client.Database.DetachMigration(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) createReadReplica() (*govultr.Database, error) { + db, _, err := o.Base.Client.Database.AddReadOnlyReplica(o.Base.Context, o.Base.Args[0], o.ReadReplicaCreateReq) + return db, err +} + +func (o *options) promoteReadReplica() error { + return o.Base.Client.Database.PromoteReadReplica(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) getBackup() (*govultr.DatabaseBackups, error) { + backup, _, err := o.Base.Client.Database.GetBackupInformation(o.Base.Context, o.Base.Args[0]) + return backup, err +} + +func (o *options) restoreBackup() (*govultr.Database, error) { + db, _, err := o.Base.Client.Database.RestoreFromBackup(o.Base.Context, o.Base.Args[0], o.BackupReq) + return db, err +} + +func (o *options) fork() (*govultr.Database, error) { + db, _, err := o.Base.Client.Database.Fork(o.Base.Context, o.Base.Args[0], o.ForkReq) + return db, err +} + +func (o *options) listConnectionPools() (*govultr.DatabaseConnections, []govultr.DatabaseConnectionPool, *govultr.Meta, error) { + cons, pool, meta, _, err := o.Base.Client.Database.ListConnectionPools(o.Base.Context, o.Base.Args[0]) + return cons, pool, meta, err +} + +func (o *options) getConnectionPool() (*govultr.DatabaseConnectionPool, error) { + pool, _, err := o.Base.Client.Database.GetConnectionPool(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) + return pool, err +} + +func (o *options) createConnectionPool() (*govultr.DatabaseConnectionPool, error) { + pool, _, err := o.Base.Client.Database.CreateConnectionPool(o.Base.Context, o.Base.Args[0], o.ConnectionPoolCreateReq) + return pool, err +} + +func (o *options) updateConnectionPool() (*govultr.DatabaseConnectionPool, error) { + pool, _, err := o.Base.Client.Database.UpdateConnectionPool(o.Base.Context, o.Base.Args[0], o.Base.Args[1], o.ConnectionPoolUpdateReq) + return pool, err +} + +func (o *options) delConnectionPool() error { + return o.Base.Client.Database.DeleteConnectionPool(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) listAdvancedOptions() (*govultr.DatabaseAdvancedOptions, []govultr.AvailableOption, error) { + cur, avail, _, err := o.Base.Client.Database.ListAdvancedOptions(o.Base.Context, o.Base.Args[0]) + return cur, avail, err +} + +func (o *options) updateAdvancedOptions() (*govultr.DatabaseAdvancedOptions, []govultr.AvailableOption, error) { + cur, avail, _, err := o.Base.Client.Database.UpdateAdvancedOptions(o.Base.Context, o.Base.Args[0], o.AdvancedOptionsReq) + return cur, avail, err +} + +func (o *options) listVersions() ([]string, error) { + vers, _, err := o.Base.Client.Database.ListAvailableVersions(o.Base.Context, o.Base.Args[0]) + return vers, err +} + +func (o *options) upgradeVersion() (string, error) { + up, _, err := o.Base.Client.Database.StartVersionUpgrade(o.Base.Context, o.Base.Args[0], o.UpgradeReq) + return up, err +} diff --git a/cmd/database/printer.go b/cmd/database/printer.go new file mode 100644 index 00000000..ccd9f1f6 --- /dev/null +++ b/cmd/database/printer.go @@ -0,0 +1,1318 @@ +package database + +import ( + "reflect" + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" +) + +// DBsPrinter ... +type DBsPrinter struct { + DBs []govultr.Database `json:"databases"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (d *DBsPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DBsPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DBsPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (d *DBsPrinter) Data() [][]string { //nolint:funlen,gocyclo + if len(d.DBs) == 0 { + return [][]string{0: {"No databases"}} + } + + var data [][]string + for i := range d.DBs { + data = append(data, + []string{"ID", d.DBs[i].ID}, + []string{"DATE CREATED", d.DBs[i].DateCreated}, + []string{"PLAN", d.DBs[i].Plan}, + []string{"PLAN DISK", strconv.Itoa(d.DBs[i].PlanDisk)}, + []string{"PLAN RAM", strconv.Itoa(d.DBs[i].PlanRAM)}, + []string{"PLAN VCPUS", strconv.Itoa(d.DBs[i].PlanVCPUs)}, + []string{"PLAN REPLICAS", strconv.Itoa(d.DBs[i].PlanReplicas)}, + []string{"REGION", d.DBs[i].Region}, + []string{"DATABASE ENGINE", d.DBs[i].DatabaseEngine}, + []string{"DATABASE ENGINE VERSION", d.DBs[i].DatabaseEngineVersion}, + []string{"VPC ID", d.DBs[i].VPCID}, + []string{"STATUS", d.DBs[i].Status}, + []string{"LABEL", d.DBs[i].Label}, + []string{"TAG", d.DBs[i].Tag}, + []string{"DB NAME", d.DBs[i].DBName}, + ) + + if d.DBs[i].DatabaseEngine == "ferretpg" { + data = append(data, + []string{" "}, + []string{"FERRETDB CREDENTIALS"}, + []string{"HOST", d.DBs[i].FerretDBCredentials.Host}, + []string{"PORT", strconv.Itoa(d.DBs[i].FerretDBCredentials.Port)}, + []string{"USER", d.DBs[i].FerretDBCredentials.User}, + []string{"PASSWORD", d.DBs[i].FerretDBCredentials.Password}, + []string{"PUBLIC IP", d.DBs[i].FerretDBCredentials.PublicIP}, + ) + + if d.DBs[i].FerretDBCredentials.PrivateIP != "" { + data = append(data, + []string{"PRIVATE IP", d.DBs[i].FerretDBCredentials.PrivateIP}, + ) + } + + data = append(data, []string{" "}) + } + + data = append(data, []string{"HOST", d.DBs[i].Host}) + + if d.DBs[i].PublicHost != "" { + data = append(data, []string{"PUBLIC HOST", d.DBs[i].PublicHost}) + } + + data = append(data, + []string{"USER", d.DBs[i].User}, + []string{"PASSWORD", d.DBs[i].Password}, + []string{"PORT", d.DBs[i].Port}, + []string{"MAINTENANCE DOW", d.DBs[i].MaintenanceDOW}, + []string{"MAINTENANCE TIME", d.DBs[i].MaintenanceTime}, + []string{"LATEST BACKUP", d.DBs[i].LatestBackup}, + []string{"TRUSTED IPS", printer.ArrayOfStringsToString(d.DBs[i].TrustedIPs)}, + ) + + if d.DBs[i].DatabaseEngine == "mysql" { + data = append(data, + []string{"MYSQL SQL MODES", printer.ArrayOfStringsToString(d.DBs[i].MySQLSQLModes)}, + []string{"MYSQL REQUIRE PRIMARY KEY", strconv.FormatBool(*d.DBs[i].MySQLRequirePrimaryKey)}, + []string{"MYSQL SLOW QUERY LOG", strconv.FormatBool(*d.DBs[i].MySQLSlowQueryLog)}, + ) + + if *d.DBs[i].MySQLSlowQueryLog { + data = append(data, + []string{"MYSQL LONG QUERY TIME", strconv.Itoa(d.DBs[i].MySQLLongQueryTime)}, + ) + } + } + + if d.DBs[i].DatabaseEngine == "pg" && len(d.DBs[i].PGAvailableExtensions) > 0 { + data = append(data, + []string{" "}, + []string{"PG AVAILABLE EXTENSIONS"}, + []string{"NAME", "VERSIONS"}, + ) + + for j := range d.DBs[i].PGAvailableExtensions { + if len(d.DBs[i].PGAvailableExtensions[j].Versions) > 0 { + data = append(data, []string{ + d.DBs[i].PGAvailableExtensions[j].Name, + printer.ArrayOfStringsToString(d.DBs[i].PGAvailableExtensions[j].Versions)}) + } else { + data = append(data, []string{d.DBs[i].PGAvailableExtensions[j].Name, ""}) + } + } + data = append(data, []string{" "}) + } + + if d.DBs[i].DatabaseEngine == "redis" { + data = append(data, []string{"REDIS EVICTION POLICY", d.DBs[i].RedisEvictionPolicy}) + } + + data = append(data, []string{"CLUSTER TIME ZONE", d.DBs[i].ClusterTimeZone}) + + if len(d.DBs[i].ReadReplicas) > 0 { + data = append(data, + []string{" "}, + []string{"READ REPLICAS"}, + ) + + for j := range d.DBs[i].ReadReplicas { + data = append(data, + []string{"ID", d.DBs[i].ReadReplicas[j].ID}, + []string{"DATE CREATED", d.DBs[i].ReadReplicas[j].DateCreated}, + []string{"PLAN", d.DBs[i].ReadReplicas[j].Plan}, + []string{"PLAN DISK", strconv.Itoa(d.DBs[i].ReadReplicas[j].PlanDisk)}, + []string{"PLAN RAM", strconv.Itoa(d.DBs[i].ReadReplicas[j].PlanRAM)}, + []string{"PLAN VCPUS", strconv.Itoa(d.DBs[i].ReadReplicas[j].PlanVCPUs)}, + []string{"PLAN REPLICAS", strconv.Itoa(d.DBs[i].ReadReplicas[j].PlanReplicas)}, + []string{"REGION", d.DBs[i].ReadReplicas[j].Region}, + []string{"DATABASE ENGINE", d.DBs[i].ReadReplicas[j].DatabaseEngine}, + []string{"DATABASE ENGINE VERSION", d.DBs[i].ReadReplicas[j].DatabaseEngineVersion}, + []string{"VPC ID", d.DBs[i].ReadReplicas[j].VPCID}, + []string{"STATUS", d.DBs[i].ReadReplicas[j].Status}, + []string{"LABEL", d.DBs[i].ReadReplicas[j].Label}, + []string{"TAG", d.DBs[i].ReadReplicas[j].Tag}, + []string{"DB NAME", d.DBs[i].ReadReplicas[j].DBName}, + ) + + if d.DBs[i].ReadReplicas[j].DatabaseEngine == "ferretpg" { + data = append(data, + []string{" "}, + + []string{"FERRETDB CREDENTIALS"}, + []string{"HOST", d.DBs[i].ReadReplicas[j].FerretDBCredentials.Host}, + []string{"PORT", strconv.Itoa(d.DBs[i].ReadReplicas[j].FerretDBCredentials.Port)}, + []string{"USER", d.DBs[i].ReadReplicas[j].FerretDBCredentials.User}, + []string{"PASSWORD", d.DBs[i].ReadReplicas[j].FerretDBCredentials.Password}, + []string{"PUBLIC IP", d.DBs[i].ReadReplicas[j].FerretDBCredentials.PublicIP}, + ) + + if d.DBs[i].ReadReplicas[j].FerretDBCredentials.PrivateIP != "" { + data = append(data, + []string{"PRIVATE IP", d.DBs[i].ReadReplicas[j].FerretDBCredentials.PrivateIP}, + ) + } + + data = append(data, []string{" "}) + } + + data = append(data, []string{"HOST", d.DBs[i].ReadReplicas[j].Host}) + + if d.DBs[i].ReadReplicas[j].PublicHost != "" { + data = append(data, []string{"PUBLIC HOST", d.DBs[i].ReadReplicas[j].PublicHost}) + } + + data = append(data, + []string{"USER", d.DBs[i].ReadReplicas[j].User}, + []string{"PASSWORD", d.DBs[i].ReadReplicas[j].Password}, + []string{"PORT", d.DBs[i].ReadReplicas[j].Port}, + []string{"MAINTENANCE DOW", d.DBs[i].ReadReplicas[j].MaintenanceDOW}, + []string{"MAINTENANCE TIME", d.DBs[i].ReadReplicas[j].MaintenanceTime}, + []string{"LATEST BACKUP", d.DBs[i].ReadReplicas[j].LatestBackup}, + []string{"TRUSTED IPS", printer.ArrayOfStringsToString(d.DBs[i].ReadReplicas[j].TrustedIPs)}, + ) + + if d.DBs[i].ReadReplicas[j].DatabaseEngine == "mysql" { + data = append(data, + []string{"MYSQL SQL MODES", printer.ArrayOfStringsToString(d.DBs[i].ReadReplicas[j].MySQLSQLModes)}, + []string{"MYSQL REQUIRE PRIMARY KEY", strconv.FormatBool(*d.DBs[i].ReadReplicas[j].MySQLRequirePrimaryKey)}, + []string{"MYSQL SLOW QUERY LOG", strconv.FormatBool(*d.DBs[i].ReadReplicas[j].MySQLSlowQueryLog)}, + ) + + if *d.DBs[i].ReadReplicas[j].MySQLSlowQueryLog { + data = append(data, []string{"MYSQL LONG QUERY TIME", strconv.Itoa(d.DBs[i].ReadReplicas[j].MySQLLongQueryTime)}) + } + } + + if d.DBs[i].ReadReplicas[j].DatabaseEngine == "pg" && len(d.DBs[i].ReadReplicas[j].PGAvailableExtensions) > 0 { + data = append(data, + []string{" "}, + []string{"PG AVAILABLE EXTENSIONS"}, + []string{"NAME", "VERSIONS"}, + ) + + for k := range d.DBs[i].ReadReplicas[j].PGAvailableExtensions { + if len(d.DBs[i].ReadReplicas[j].PGAvailableExtensions[k].Versions) > 0 { + data = append(data, []string{ + d.DBs[i].ReadReplicas[j].PGAvailableExtensions[k].Name, + printer.ArrayOfStringsToString(d.DBs[i].ReadReplicas[j].PGAvailableExtensions[k].Versions), + }) + } else { + data = append(data, []string{d.DBs[i].ReadReplicas[j].PGAvailableExtensions[k].Name, ""}) + } + } + + data = append(data, []string{" "}) + } + + if d.DBs[i].ReadReplicas[j].DatabaseEngine == "redis" { + data = append(data, []string{"REDIS EVICTION POLICY", d.DBs[i].ReadReplicas[j].RedisEvictionPolicy}) + } + + data = append(data, []string{"CLUSTER TIME ZONE", d.DBs[i].ReadReplicas[j].ClusterTimeZone}) + } + } + + data = append(data, []string{"---------------------------"}) + } + + return data +} + +// Paging ... +func (d *DBsPrinter) Paging() [][]string { + paging := &printer.Total{Total: d.Meta.Total} + return paging.Compose() +} + +// ====================================== + +// DBPrinter ... +type DBPrinter struct { + DB *govultr.Database `json:"database"` +} + +// JSON ... +func (d *DBPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DBPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DBPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (d *DBPrinter) Data() [][]string { //nolint:funlen,gocyclo + var data [][]string + data = append(data, + []string{"ID", d.DB.ID}, + []string{"DATE CREATED", d.DB.DateCreated}, + []string{"PLAN", d.DB.Plan}, + []string{"PLAN DISK", strconv.Itoa(d.DB.PlanDisk)}, + []string{"PLAN RAM", strconv.Itoa(d.DB.PlanRAM)}, + []string{"PLAN VCPUS", strconv.Itoa(d.DB.PlanVCPUs)}, + []string{"PLAN REPLICAS", strconv.Itoa(d.DB.PlanReplicas)}, + []string{"REGION", d.DB.Region}, + []string{"DATABASE ENGINE", d.DB.DatabaseEngine}, + []string{"DATABASE ENGINE VERSION", d.DB.DatabaseEngineVersion}, + []string{"VPC ID", d.DB.VPCID}, + []string{"STATUS", d.DB.Status}, + []string{"LABEL", d.DB.Label}, + []string{"TAG", d.DB.Tag}, + []string{"DB NAME", d.DB.DBName}, + ) + + if d.DB.DatabaseEngine == "ferretpg" { + data = append(data, + []string{" "}, + []string{"FERRETDB CREDENTIALS"}, + []string{"HOST", d.DB.FerretDBCredentials.Host}, + []string{"PORT", strconv.Itoa(d.DB.FerretDBCredentials.Port)}, + []string{"USER", d.DB.FerretDBCredentials.User}, + []string{"PASSWORD", d.DB.FerretDBCredentials.Password}, + []string{"PUBLIC IP", d.DB.FerretDBCredentials.PublicIP}, + ) + + if d.DB.FerretDBCredentials.PrivateIP != "" { + data = append(data, + []string{"PRIVATE IP", d.DB.FerretDBCredentials.PrivateIP}, + ) + } + + data = append(data, []string{" "}) + } + + data = append(data, []string{"HOST", d.DB.Host}) + + if d.DB.PublicHost != "" { + data = append(data, []string{"PUBLIC HOST", d.DB.PublicHost}) + } + + data = append(data, + []string{"USER", d.DB.User}, + []string{"PASSWORD", d.DB.Password}, + []string{"PORT", d.DB.Port}, + []string{"MAINTENANCE DOW", d.DB.MaintenanceDOW}, + []string{"MAINTENANCE TIME", d.DB.MaintenanceTime}, + []string{"LATEST BACKUP", d.DB.LatestBackup}, + []string{"TRUSTED IPS", printer.ArrayOfStringsToString(d.DB.TrustedIPs)}, + ) + + if d.DB.DatabaseEngine == "mysql" { + data = append(data, + []string{"MYSQL SQL MODES", printer.ArrayOfStringsToString(d.DB.MySQLSQLModes)}, + []string{"MYSQL REQUIRE PRIMARY KEY", strconv.FormatBool(*d.DB.MySQLRequirePrimaryKey)}, + []string{"MYSQL SLOW QUERY LOG", strconv.FormatBool(*d.DB.MySQLSlowQueryLog)}, + ) + + if *d.DB.MySQLSlowQueryLog { + data = append(data, + []string{"MYSQL LONG QUERY TIME", strconv.Itoa(d.DB.MySQLLongQueryTime)}, + ) + } + } + + if d.DB.DatabaseEngine == "pg" && len(d.DB.PGAvailableExtensions) > 0 { + data = append(data, + []string{" "}, + []string{"PG AVAILABLE EXTENSIONS"}, + []string{"NAME", "VERSIONS"}, + ) + + for i := range d.DB.PGAvailableExtensions { + if len(d.DB.PGAvailableExtensions[i].Versions) > 0 { + data = append(data, []string{ + d.DB.PGAvailableExtensions[i].Name, + printer.ArrayOfStringsToString(d.DB.PGAvailableExtensions[i].Versions)}) + } else { + data = append(data, []string{d.DB.PGAvailableExtensions[i].Name, ""}) + } + } + data = append(data, []string{" "}) + } + + if d.DB.DatabaseEngine == "redis" { + data = append(data, []string{"REDIS EVICTION POLICY", d.DB.RedisEvictionPolicy}) + } + + data = append(data, []string{"CLUSTER TIME ZONE", d.DB.ClusterTimeZone}) + + if len(d.DB.ReadReplicas) > 0 { + data = append(data, + []string{" "}, + []string{"READ REPLICAS"}, + ) + + for i := range d.DB.ReadReplicas { + data = append(data, + []string{"ID", d.DB.ReadReplicas[i].ID}, + []string{"DATE CREATED", d.DB.ReadReplicas[i].DateCreated}, + []string{"PLAN", d.DB.ReadReplicas[i].Plan}, + []string{"PLAN DISK", strconv.Itoa(d.DB.ReadReplicas[i].PlanDisk)}, + []string{"PLAN RAM", strconv.Itoa(d.DB.ReadReplicas[i].PlanRAM)}, + []string{"PLAN VCPUS", strconv.Itoa(d.DB.ReadReplicas[i].PlanVCPUs)}, + []string{"PLAN REPLICAS", strconv.Itoa(d.DB.ReadReplicas[i].PlanReplicas)}, + []string{"REGION", d.DB.ReadReplicas[i].Region}, + []string{"DATABASE ENGINE", d.DB.ReadReplicas[i].DatabaseEngine}, + []string{"DATABASE ENGINE VERSION", d.DB.ReadReplicas[i].DatabaseEngineVersion}, + []string{"VPC ID", d.DB.ReadReplicas[i].VPCID}, + []string{"STATUS", d.DB.ReadReplicas[i].Status}, + []string{"LABEL", d.DB.ReadReplicas[i].Label}, + []string{"TAG", d.DB.ReadReplicas[i].Tag}, + []string{"DB NAME", d.DB.ReadReplicas[i].DBName}, + ) + + if d.DB.ReadReplicas[i].DatabaseEngine == "ferretpg" { + data = append(data, + []string{" "}, + + []string{"FERRETDB CREDENTIALS"}, + []string{"HOST", d.DB.ReadReplicas[i].FerretDBCredentials.Host}, + []string{"PORT", strconv.Itoa(d.DB.ReadReplicas[i].FerretDBCredentials.Port)}, + []string{"USER", d.DB.ReadReplicas[i].FerretDBCredentials.User}, + []string{"PASSWORD", d.DB.ReadReplicas[i].FerretDBCredentials.Password}, + []string{"PUBLIC IP", d.DB.ReadReplicas[i].FerretDBCredentials.PublicIP}, + ) + + if d.DB.ReadReplicas[i].FerretDBCredentials.PrivateIP != "" { + data = append(data, + []string{"PRIVATE IP", d.DB.ReadReplicas[i].FerretDBCredentials.PrivateIP}, + ) + } + + data = append(data, []string{" "}) + } + + data = append(data, []string{"HOST", d.DB.ReadReplicas[i].Host}) + + if d.DB.ReadReplicas[i].PublicHost != "" { + data = append(data, []string{"PUBLIC HOST", d.DB.ReadReplicas[i].PublicHost}) + } + + data = append(data, + []string{"USER", d.DB.ReadReplicas[i].User}, + []string{"PASSWORD", d.DB.ReadReplicas[i].Password}, + []string{"PORT", d.DB.ReadReplicas[i].Port}, + []string{"MAINTENANCE DOW", d.DB.ReadReplicas[i].MaintenanceDOW}, + []string{"MAINTENANCE TIME", d.DB.ReadReplicas[i].MaintenanceTime}, + []string{"LATEST BACKUP", d.DB.ReadReplicas[i].LatestBackup}, + []string{"TRUSTED IPS", printer.ArrayOfStringsToString(d.DB.ReadReplicas[i].TrustedIPs)}, + ) + + if d.DB.ReadReplicas[i].DatabaseEngine == "mysql" { + data = append(data, + []string{"MYSQL SQL MODES", printer.ArrayOfStringsToString(d.DB.ReadReplicas[i].MySQLSQLModes)}, + []string{"MYSQL REQUIRE PRIMARY KEY", strconv.FormatBool(*d.DB.ReadReplicas[i].MySQLRequirePrimaryKey)}, + []string{"MYSQL SLOW QUERY LOG", strconv.FormatBool(*d.DB.ReadReplicas[i].MySQLSlowQueryLog)}, + ) + + if *d.DB.ReadReplicas[i].MySQLSlowQueryLog { + data = append(data, []string{"MYSQL LONG QUERY TIME", strconv.Itoa(d.DB.ReadReplicas[i].MySQLLongQueryTime)}) + } + } + + if d.DB.ReadReplicas[i].DatabaseEngine == "pg" && len(d.DB.ReadReplicas[i].PGAvailableExtensions) > 0 { + data = append(data, + []string{" "}, + []string{"PG AVAILABLE EXTENSIONS"}, + []string{"NAME", "VERSIONS"}, + ) + + for j := range d.DB.ReadReplicas[i].PGAvailableExtensions { + if len(d.DB.ReadReplicas[i].PGAvailableExtensions[j].Versions) > 0 { + data = append(data, []string{ + d.DB.ReadReplicas[i].PGAvailableExtensions[j].Name, + printer.ArrayOfStringsToString(d.DB.ReadReplicas[i].PGAvailableExtensions[j].Versions), + }) + } else { + data = append(data, []string{d.DB.ReadReplicas[i].PGAvailableExtensions[j].Name, ""}) + } + } + + data = append(data, []string{" "}) + } + + if d.DB.ReadReplicas[i].DatabaseEngine == "redis" { + data = append(data, []string{"REDIS EVICTION POLICY", d.DB.ReadReplicas[i].RedisEvictionPolicy}) + } + + data = append(data, []string{"CLUSTER TIME ZONE", d.DB.ReadReplicas[i].ClusterTimeZone}) + } + } + + return data +} + +// Paging ... +func (d *DBPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// DBsSummaryPrinter ... +type DBsSummaryPrinter struct { + DBs []govultr.Database `json:"databases"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (d *DBsSummaryPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DBsSummaryPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DBsSummaryPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "REGION", + "LABEL", + "STATUS", + "ENGINE", + "VERSION", + }} +} + +// Data ... +func (d *DBsSummaryPrinter) Data() [][]string { + if len(d.DBs) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range d.DBs { + data = append(data, []string{ + + d.DBs[i].ID, + d.DBs[i].Region, + d.DBs[i].Label, + d.DBs[i].Status, + d.DBs[i].DatabaseEngine, + d.DBs[i].DatabaseEngineVersion, + }) + } + + return data +} + +// Paging ... +func (d *DBsSummaryPrinter) Paging() [][]string { + paging := &printer.Total{Total: d.Meta.Total} + return paging.Compose() +} + +// ====================================== + +// PlansPrinter ... +type PlansPrinter struct { + Plans []govultr.DatabasePlan `json:"plans"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (p *PlansPrinter) JSON() []byte { + return printer.MarshalObject(p, "json") +} + +// YAML ... +func (p *PlansPrinter) YAML() []byte { + return printer.MarshalObject(p, "yaml") +} + +// Columns ... +func (p *PlansPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (p *PlansPrinter) Data() [][]string { + if len(p.Plans) == 0 { + return [][]string{0: {"No database plans available"}} + } + + var data [][]string + for i := range p.Plans { + data = append(data, + []string{"ID", p.Plans[i].ID}, + []string{"NUMBER OF NODES", strconv.Itoa(p.Plans[i].NumberOfNodes)}, + []string{"TYPE", p.Plans[i].Type}, + []string{"VCPU COUNT", strconv.Itoa(p.Plans[i].VCPUCount)}, + []string{"RAM", strconv.Itoa(p.Plans[i].RAM)}, + []string{"DISK", strconv.Itoa(p.Plans[i].Disk)}, + []string{"MONTHLY COST", strconv.Itoa(p.Plans[i].MonthlyCost)}, + + []string{" "}, + + []string{"SUPPORTED ENGINES"}, + []string{"MYSQL", strconv.FormatBool(*p.Plans[i].SupportedEngines.MySQL)}, + []string{"PG", strconv.FormatBool(*p.Plans[i].SupportedEngines.PG)}, + []string{"REDIS", strconv.FormatBool(*p.Plans[i].SupportedEngines.Redis)}, + ) + + if !*p.Plans[i].SupportedEngines.Redis { + data = append(data, + []string{" "}, + []string{"MAX CONNECTIONS"}, + []string{"MYSQL", strconv.Itoa(p.Plans[i].MaxConnections.MySQL)}, + []string{"PG", strconv.Itoa(p.Plans[i].MaxConnections.PG)}, + []string{" "}, + ) + } + + data = append(data, + []string{"LOCATIONS", printer.ArrayOfStringsToString(p.Plans[i].Locations)}, + []string{"---------------------------"}, + ) + } + + return data +} + +// Paging ... +func (p *PlansPrinter) Paging() [][]string { + paging := &printer.Total{Total: p.Meta.Total} + return paging.Compose() +} + +// ====================================== + +// UsagePrinter ... +type UsagePrinter struct { + Usage *govultr.DatabaseUsage `json:"usage"` +} + +// JSON ... +func (u *UsagePrinter) JSON() []byte { + return printer.MarshalObject(u, "json") +} + +// YAML ... +func (u *UsagePrinter) YAML() []byte { + return printer.MarshalObject(u, "yaml") +} + +// Columns ... +func (u *UsagePrinter) Columns() [][]string { + return nil +} + +// Data ... +func (u *UsagePrinter) Data() [][]string { + var data [][]string + data = append(data, + []string{"DISK USAGE"}, + []string{"CURRENT (GB)", strconv.FormatFloat( + float64(u.Usage.Disk.CurrentGB), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + )}, + []string{"MAXIMUM (GB)", strconv.FormatFloat( + float64(u.Usage.Disk.MaxGB), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + )}, + []string{"PERCENTAGE", strconv.FormatFloat( + float64(u.Usage.Disk.Percentage), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + )}, + []string{" "}, + []string{"MEMORY USAGE"}, + []string{"CURRENT (MB)", strconv.FormatFloat( + float64(u.Usage.Memory.CurrentMB), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + )}, + []string{"MAXIMUM (MB)", strconv.FormatFloat( + float64(u.Usage.Memory.MaxMB), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + )}, + []string{"PERCENTAGE", strconv.FormatFloat( + float64(u.Usage.Memory.Percentage), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + )}, + []string{" "}, + []string{"CPU USAGE"}, + []string{"PERCENTAGE", strconv.FormatFloat( + float64(u.Usage.CPU.Percentage), + 'f', + utils.FloatPrecision, + utils.FloatBitDepth, + )}, + ) + + return data +} + +// Paging ... +func (u *UsagePrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// UsersPrinter ... +type UsersPrinter struct { + Users []govultr.DatabaseUser `json:"users"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (u *UsersPrinter) JSON() []byte { + return printer.MarshalObject(u, "json") +} + +// YAML ... +func (u *UsersPrinter) YAML() []byte { + return printer.MarshalObject(u, "yaml") +} + +// Columns ... +func (u *UsersPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (u *UsersPrinter) Data() [][]string { + if len(u.Users) == 0 { + return [][]string{0: {"No database users"}} + } + + var data [][]string + for i := range u.Users { + data = append(data, + []string{"USERNAME", u.Users[i].Username}, + []string{"PASSWORD", u.Users[i].Password}, + ) + + if u.Users[i].Encryption != "" { + data = append(data, []string{"ENCRYPTION", u.Users[i].Encryption}) + } + + if u.Users[i].AccessControl != nil { + data = append(data, + []string{"ACCESS CONTROL"}, + []string{"REDIS ACL CATEGORIES", printer.ArrayOfStringsToString(u.Users[i].AccessControl.RedisACLCategories)}, + []string{"REDIS ACL CHANNELS", printer.ArrayOfStringsToString(u.Users[i].AccessControl.RedisACLChannels)}, + []string{"REDIS ACL COMMANDS", printer.ArrayOfStringsToString(u.Users[i].AccessControl.RedisACLCommands)}, + []string{"REDIS ACL KEYS", printer.ArrayOfStringsToString(u.Users[i].AccessControl.RedisACLKeys)}, + ) + } + + data = append(data, []string{"---------------------------"}) + } + + return data +} + +// Paging ... +func (u *UsersPrinter) Paging() [][]string { + paging := &printer.Total{Total: u.Meta.Total} + return paging.Compose() +} + +// ====================================== + +// UserPrinter ... +type UserPrinter struct { + User *govultr.DatabaseUser `json:"user"` +} + +// JSON ... +func (u *UserPrinter) JSON() []byte { + return printer.MarshalObject(u, "json") +} + +// YAML ... +func (u *UserPrinter) YAML() []byte { + return printer.MarshalObject(u, "yaml") +} + +// Columns ... +func (u *UserPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (u *UserPrinter) Data() [][]string { + var data [][]string + data = append(data, + []string{"USERNAME", u.User.Username}, + []string{"PASSWORD", u.User.Password}, + ) + + if u.User.Encryption != "" { + data = append(data, []string{"ENCRYPTION", u.User.Encryption}) + } + + if u.User.AccessControl != nil { + data = append(data, + []string{"ACCESS CONTROL"}, + []string{"REDIS ACL CATEGORIES", printer.ArrayOfStringsToString(u.User.AccessControl.RedisACLCategories)}, + []string{"REDIS ACL CHANNELS", printer.ArrayOfStringsToString(u.User.AccessControl.RedisACLChannels)}, + []string{"REDIS ACL COMMANDS", printer.ArrayOfStringsToString(u.User.AccessControl.RedisACLCommands)}, + []string{"REDIS ACL KEYS", printer.ArrayOfStringsToString(u.User.AccessControl.RedisACLKeys)}, + ) + } + + return data +} + +// Paging ... +func (u *UserPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// LogicalDBsPrinter ... +type LogicalDBsPrinter struct { + DBs []govultr.DatabaseDB `json:"dbs"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (l *LogicalDBsPrinter) JSON() []byte { + return printer.MarshalObject(l, "json") +} + +// YAML ... +func (l *LogicalDBsPrinter) YAML() []byte { + return printer.MarshalObject(l, "yaml") +} + +// Columns ... +func (l *LogicalDBsPrinter) Columns() [][]string { + return [][]string{0: { + "NAME", + }} +} + +// Data ... +func (l *LogicalDBsPrinter) Data() [][]string { + if len(l.DBs) == 0 { + return [][]string{0: {"---"}} + } + + var data [][]string + for i := range l.DBs { + data = append(data, []string{l.DBs[i].Name}) + } + + return data +} + +// Paging ... +func (l *LogicalDBsPrinter) Paging() [][]string { + paging := &printer.Total{Total: l.Meta.Total} + return paging.Compose() +} + +// ====================================== + +// LogicalDBPrinter ... +type LogicalDBPrinter struct { + DB *govultr.DatabaseDB `json:"db"` +} + +// JSON ... +func (l *LogicalDBPrinter) JSON() []byte { + return printer.MarshalObject(l, "json") +} + +// YAML ... +func (l *LogicalDBPrinter) YAML() []byte { + return printer.MarshalObject(l, "yaml") +} + +// Columns ... +func (l *LogicalDBPrinter) Columns() [][]string { + return [][]string{0: { + "NAME", + }} +} + +// Data ... +func (l *LogicalDBPrinter) Data() [][]string { + return [][]string{0: {l.DB.Name}} +} + +// Paging ... +func (l *LogicalDBPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// UpdatesPrinter ... +type UpdatesPrinter struct { + Updates []string `json:"available_updates"` +} + +// JSON ... +func (u *UpdatesPrinter) JSON() []byte { + return printer.MarshalObject(u, "json") +} + +// YAML ... +func (u *UpdatesPrinter) YAML() []byte { + return printer.MarshalObject(u, "yaml") +} + +// Columns ... +func (u *UpdatesPrinter) Columns() [][]string { + return [][]string{0: {"AVAILABLE UPDATES"}} +} + +// Data ... +func (u *UpdatesPrinter) Data() [][]string { + var data [][]string + + for i := range u.Updates { + data = append(data, []string{u.Updates[i]}) + } + + return data +} + +// Paging ... +func (u *UpdatesPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// AlertsPrinter ... +type AlertsPrinter struct { + Alerts []govultr.DatabaseAlert `json:"alerts"` +} + +// JSON ... +func (a *AlertsPrinter) JSON() []byte { + return printer.MarshalObject(a, "json") +} + +// YAML ... +func (a *AlertsPrinter) YAML() []byte { + return printer.MarshalObject(a, "yaml") +} + +// Columns ... +func (a *AlertsPrinter) Columns() [][]string { + return [][]string{0: { + "NAME", + }} +} + +// Data ... +func (a *AlertsPrinter) Data() [][]string { + if len(a.Alerts) == 0 { + return [][]string{0: {"No active database alerts"}} + } + + var data [][]string + for i := range a.Alerts { + data = append(data, + []string{"TIMESTAMP", a.Alerts[i].Timestamp}, + []string{"MESSAGE TYPE", a.Alerts[i].MessageType}, + []string{"DESCRIPTION", a.Alerts[i].Description}, + ) + + if a.Alerts[i].Recommendation != "" { + data = append(data, []string{"RECOMMENDATION", a.Alerts[i].Recommendation}) + } + + if a.Alerts[i].MaintenanceScheduled != "" { + data = append(data, []string{"MAINTENANCE SCHEDULED", a.Alerts[i].MaintenanceScheduled}) + } + + if a.Alerts[i].ResourceType != "" { + data = append(data, []string{"RESOURCE TYPE", a.Alerts[i].ResourceType}) + } + + if a.Alerts[i].TableCount != 0 { + data = append(data, []string{"TABLE COUNT", strconv.Itoa(a.Alerts[i].TableCount)}) + } + + data = append(data, []string{"---------------------------"}) + } + + return data +} + +// Paging ... +func (a *AlertsPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// MigrationPrinter ... +type MigrationPrinter struct { + Migration *govultr.DatabaseMigration `json:"migration"` +} + +// JSON ... +func (m *MigrationPrinter) JSON() []byte { + return printer.MarshalObject(m, "json") +} + +// YAML ... +func (m *MigrationPrinter) YAML() []byte { + return printer.MarshalObject(m, "yaml") +} + +// Columns ... +func (m *MigrationPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (m *MigrationPrinter) Data() [][]string { + var data [][]string + data = append(data, []string{"STATUS", m.Migration.Status}) + + if m.Migration.Method != "" { + data = append(data, []string{"METHOD", m.Migration.Method}) + } + + if m.Migration.Error != "" { + data = append(data, []string{"ERROR", m.Migration.Error}) + } + + data = append(data, + []string{" "}, + []string{"CREDENTIALS"}, + []string{"HOST", m.Migration.Credentials.Host}, + []string{"PORT", strconv.Itoa(m.Migration.Credentials.Port)}, + []string{"USERNAME", m.Migration.Credentials.Username}, + []string{"PASSWORD", m.Migration.Credentials.Password}, + ) + + if m.Migration.Credentials.Database != "" { + data = append(data, []string{"DATABASE", m.Migration.Credentials.Database}) + } + + if m.Migration.Credentials.IgnoredDatabases != "" { + data = append(data, []string{"IGNORED DATABASES", m.Migration.Credentials.IgnoredDatabases}) + } + + data = append(data, []string{"SSL", strconv.FormatBool(*m.Migration.Credentials.SSL)}) + + return data +} + +// Paging ... +func (m *MigrationPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// BackupPrinter ... +type BackupPrinter struct { + Backup *govultr.DatabaseBackups `json:"backups"` +} + +// JSON ... +func (b *BackupPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BackupPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BackupPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (b *BackupPrinter) Data() [][]string { + var data [][]string + data = append(data, + []string{"LATEST BACKUP"}, + []string{"DATE", b.Backup.LatestBackup.Date}, + []string{"TIME", b.Backup.LatestBackup.Time}, + []string{" "}, + []string{"OLDEST BACKUP"}, + []string{"DATE", b.Backup.OldestBackup.Date}, + []string{"TIME", b.Backup.OldestBackup.Time}, + ) + + return data +} + +// Paging ... +func (b *BackupPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// ConnectionsPrinter ... +type ConnectionsPrinter struct { + Connections *govultr.DatabaseConnections `json:"connections"` + ConnectionPools []govultr.DatabaseConnectionPool `json:"connection_pools"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (c *ConnectionsPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ConnectionsPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ConnectionsPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (c *ConnectionsPrinter) Data() [][]string { + var data [][]string + + data = append(data, + []string{"CONNECTIONS"}, + []string{"USED", strconv.Itoa(c.Connections.Used)}, + []string{"AVAILABLE", strconv.Itoa(c.Connections.Available)}, + []string{"MAX", strconv.Itoa(c.Connections.Max)}, + + []string{" "}, + []string{"CONNECTION POOLS"}, + ) + + for i := range c.ConnectionPools { + data = append(data, + []string{"NAME", c.ConnectionPools[i].Name}, + []string{"DATABASE", c.ConnectionPools[i].Database}, + []string{"USERNAME", c.ConnectionPools[i].Username}, + []string{"MODE", c.ConnectionPools[i].Mode}, + []string{"SIZE", strconv.Itoa(c.ConnectionPools[i].Size)}, + []string{"---------------------------"}, + ) + } + + return data +} + +// Paging ... +func (c *ConnectionsPrinter) Paging() [][]string { + paging := &printer.Total{Total: c.Meta.Total} + return paging.Compose() +} + +// ====================================== + +// ConnectionPoolPrinter ... +type ConnectionPoolPrinter struct { + ConnectionPool *govultr.DatabaseConnectionPool `json:"connection_pool"` +} + +// JSON ... +func (c *ConnectionPoolPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ConnectionPoolPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ConnectionPoolPrinter) Columns() [][]string { + return [][]string{0: { + "NAME", + "DATABASE", + "USERNAME", + "MODE", + "SIZE", + }} +} + +// Data ... +func (c *ConnectionPoolPrinter) Data() [][]string { + return [][]string{0: { + c.ConnectionPool.Name, + c.ConnectionPool.Database, + c.ConnectionPool.Username, + c.ConnectionPool.Mode, + strconv.Itoa(c.ConnectionPool.Size), + }} +} + +// Paging ... +func (c *ConnectionPoolPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// AdvancedOptionsPrinter ... +type AdvancedOptionsPrinter struct { + Configured *govultr.DatabaseAdvancedOptions `json:"configured_options"` + Available []govultr.AvailableOption `json:"available_options"` +} + +// JSON ... +func (a *AdvancedOptionsPrinter) JSON() []byte { + return printer.MarshalObject(a, "json") +} + +// YAML ... +func (a *AdvancedOptionsPrinter) YAML() []byte { + return printer.MarshalObject(a, "yaml") +} + +// Columns ... +func (a *AdvancedOptionsPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (a *AdvancedOptionsPrinter) Data() [][]string { + var data [][]string + + if a.Configured == (&govultr.DatabaseAdvancedOptions{}) { + data = append(data, []string{"CONFIGURED OPTIONS", "None"}) + } else { + data = append(data, []string{"CONFIGURED OPTIONS"}) + v := reflect.ValueOf(*a.Configured) + for i := 0; i < v.NumField(); i++ { + if !v.Field(i).IsZero() { + if v.Field(i).Kind() == reflect.Pointer { + data = append(data, []string{v.Type().Field(i).Name, v.Field(i).Elem().Interface().(string)}) + } else { + data = append(data, []string{v.Type().Field(i).Name, v.Field(i).Interface().(string)}) + } + } + } + } + + data = append(data, + []string{" "}, + []string{"AVAILABLE OPTIONS"}, + ) + + for i := range a.Available { + data = append(data, + []string{"NAME", a.Available[i].Name}, + []string{"TYPE", a.Available[i].Type}, + ) + + if a.Available[i].Type == "enum" { + data = append(data, + []string{"ENUMERALS", printer.ArrayOfStringsToString(a.Available[i].Enumerals)}, + ) + } + + if a.Available[i].Type == "int" || a.Available[i].Type == "float" { + data = append(data, + []string{"MIN VALUE", strconv.Itoa(*a.Available[i].MinValue)}, + []string{"MAX VALUE", strconv.Itoa(*a.Available[i].MaxValue)}, + ) + } + + if len(a.Available[i].AltValues) > 0 { + data = append(data, []string{"ALT VALUES", printer.ArrayOfIntsToString(a.Available[i].AltValues)}) + } + + if a.Available[i].Units != "" { + data = append(data, []string{"UNITS", a.Available[i].Units}) + } + + data = append(data, []string{"---------------------------"}) + } + + return data +} + +// Paging ... +func (a *AdvancedOptionsPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// VersionsPrinter ... +type VersionsPrinter struct { + Versions []string `json:"available_versions"` +} + +// JSON ... +func (v *VersionsPrinter) JSON() []byte { + return printer.MarshalObject(v, "json") +} + +// YAML ... +func (v *VersionsPrinter) YAML() []byte { + return printer.MarshalObject(v, "yaml") +} + +// Columns ... +func (v *VersionsPrinter) Columns() [][]string { + return [][]string{0: { + "AVAILABLE VERSIONS", + }} +} + +// Data ... +func (v *VersionsPrinter) Data() [][]string { + var data [][]string + for i := range v.Versions { + data = append(data, []string{v.Versions[i]}) + } + + return data +} + +// Paging ... +func (v *VersionsPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/dns.go b/cmd/dns.go deleted file mode 100644 index f34866b1..00000000 --- a/cmd/dns.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "github.com/spf13/cobra" -) - -// DNS represents the dns command -func DNS() *cobra.Command { - dnsCmd := &cobra.Command{ - Use: "dns", - Short: "dns is used to access dns commands", - Long: ``, - } - - dnsCmd.AddCommand(DNSDomain()) - dnsCmd.AddCommand(DNSRecord()) - return dnsCmd -} diff --git a/cmd/dns/dns.go b/cmd/dns/dns.go new file mode 100644 index 00000000..98359ca0 --- /dev/null +++ b/cmd/dns/dns.go @@ -0,0 +1,645 @@ +// Package dns provides the functionality for dns commands in the CLI +package dns + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + dnsLong = `` + dnsExample = `` + + createLong = `` + createExample = `` + + domainLong = `` + domainExample = `` +) + +// NewCmdDNS provides the CLI command functionality for DNS +func NewCmdDNS(base *cli.Base) *cobra.Command { //nolint:funlen,gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "dns", + Short: "Commands to control DNS records", + Long: dnsLong, + Example: dnsExample, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + domain := &cobra.Command{ + Use: "domain", + Short: "DNS domain commands", + Long: domainLong, + Example: domainExample, + } + + // Domain List + domainList := &cobra.Command{ + Use: "list", + Short: "Get list of domains", + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + + dms, meta, err := o.domainList() + if err != nil { + printer.Error(fmt.Errorf("error retrieving domain list : %v", err)) + os.Exit(1) + } + + data := &DNSDomainsPrinter{Domains: dms, Meta: meta} + o.Base.Printer.Display(data, nil) + }, + } + + domainList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + domainList.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Domain Get + domainGet := &cobra.Command{ + Use: "get ", + Short: "Get a domain", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + dm, err := o.domainGet() + if err != nil { + printer.Error(fmt.Errorf("error retrieving domain : %v", err)) + os.Exit(1) + } + + data := &DNSDomainPrinter{Domain: *dm} + o.Base.Printer.Display(data, nil) + }, + } + + // Domain Create + domainCreate := &cobra.Command{ + Use: "create", + Short: "Create a domain", + Long: createLong, + Example: createExample, + Run: func(cmd *cobra.Command, args []string) { + domain, errDo := cmd.Flags().GetString("domain") + if errDo != nil { + printer.Error(fmt.Errorf("error parsing 'domain' flag for domain create : %v", errDo)) + os.Exit(1) + } + + ip, errIP := cmd.Flags().GetString("ip") + if errIP != nil { + printer.Error(fmt.Errorf("error parsing 'ip' flag for domain create : %v", errIP)) + os.Exit(1) + } + + o.DomainCreateReq = &govultr.DomainReq{ + Domain: domain, + IP: ip, + } + + dm, err := o.domainCreate() + if err != nil { + printer.Error(fmt.Errorf("error creating dns domain : %v", err)) + os.Exit(1) + } + + data := &DNSDomainPrinter{Domain: *dm} + o.Base.Printer.Display(data, nil) + }, + } + + domainCreate.Flags().StringP("domain", "d", "", "name of the domain") + if err := domainCreate.MarkFlagRequired("domain"); err != nil { + printer.Error(fmt.Errorf("error marking domain create 'domain' flag required: %v", err)) + os.Exit(1) + } + domainCreate.Flags().StringP("ip", "i", "", "instance ip you want to assign this domain to") + + // Domain Delete + domainDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete a domain", + Aliases: []string{"destroy"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a domain name") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.domainDelete(); err != nil { + printer.Error(fmt.Errorf("error delete dns domain : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("dns domain has been deleted"), nil) + }, + } + + // Domain DNSSEC Update + domainDNSSEC := &cobra.Command{ + Use: "dnssec ", + Short: "enable/disable dnssec", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a domain name") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + enabled, errEn := cmd.Flags().GetBool("enabled") + if errEn != nil { + printer.Error(fmt.Errorf("error parsing 'enabled' flag for dnssec : %v", errEn)) + os.Exit(1) + } + + disabled, errDi := cmd.Flags().GetBool("disabled") + if errEn != nil { + printer.Error(fmt.Errorf("error parsing 'disabled' flag for dnssec : %v", errDi)) + os.Exit(1) + } + + if cmd.Flags().Changed("enabled") { + if enabled { + o.DomainDNSSECEnabled = "enabled" + } else { + o.DomainDNSSECEnabled = "disabled" + } + } + + if cmd.Flags().Changed("disabled") { + if disabled { + o.DomainDNSSECEnabled = "disabled" + } else { + o.DomainDNSSECEnabled = "enabled" + } + } + + if err := o.domainUpdate(); err != nil { + printer.Error(fmt.Errorf("error toggling dnssec : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("dns domain DNSSEC has been updated"), nil) + }, + } + + domainDNSSEC.Flags().BoolP("enabled", "e", true, "enable dnssec") + domainDNSSEC.Flags().BoolP("disabled", "d", true, "disable dnssec") + domainDNSSEC.MarkFlagsOneRequired("enabled", "disabled") + domainDNSSEC.MarkFlagsMutuallyExclusive("enabled", "disabled") + + // Domain DNSSEC Info + domainDNSSECInfo := &cobra.Command{ + Use: "dnssec-info ", + Short: "Get DNS SEC info", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a domain name") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + info, err := o.domainDNSSECGet() + if err != nil { + printer.Error(fmt.Errorf("error getting domain dnssec info : %v", err)) + os.Exit(1) + } + + data := &DNSSECPrinter{SEC: info} + o.Base.Printer.Display(data, nil) + }, + } + + // Domain SOA Info + domainSOAInfo := &cobra.Command{ + Use: "soa-info ", + Short: "Get DNS SOA info", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a domain name") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + info, err := o.domainSOAGet() + if err != nil { + printer.Error(fmt.Errorf("error getting domain soa info : %v", err)) + os.Exit(1) + } + + data := &DNSSOAPrinter{SOA: *info} + o.Base.Printer.Display(data, nil) + }, + } + + // Domain SOA Update + domainSOAUpdate := &cobra.Command{ + Use: "soa-update ", + Short: "Update SOA for a domain", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a domain name") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + ns, errNs := cmd.Flags().GetString("ns-primary") + if errNs != nil { + printer.Error(fmt.Errorf("error parsing 'ns-primary' flag for domain soa : %v", errNs)) + os.Exit(1) + } + + email, errEm := cmd.Flags().GetString("email") + if errEm != nil { + printer.Error(fmt.Errorf("error parsing 'email' flag for domain soa : %v", errEm)) + os.Exit(1) + } + + o.SOAUpdateReq = &govultr.Soa{ + NSPrimary: ns, + Email: email, + } + + if err := o.domainSOAUpdate(); err != nil { + printer.Error(fmt.Errorf("error updating domain soa : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("domain soa has been updated"), nil) + }, + } + + domainSOAUpdate.Flags().StringP("ns-primary", "n", "", "primary nameserver to store in the SOA record") + domainSOAUpdate.Flags().StringP("email", "e", "", "administrative email to store in the SOA record") + + domain.AddCommand( + domainList, + domainGet, + domainCreate, + domainDelete, + domainDNSSEC, + domainDNSSECInfo, + domainSOAInfo, + domainSOAUpdate, + ) + + // Record + record := &cobra.Command{ + Use: "record", + Short: "dns record", + } + + // Record List + recordList := &cobra.Command{ + Use: "list ", + Short: "List all DNS records", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a domain name") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + + recs, meta, err := o.recordList() + if err != nil { + printer.Error(fmt.Errorf("error retrieiving domain records : %v", err)) + os.Exit(1) + } + + data := &DNSRecordsPrinter{Records: recs, Meta: meta} + o.Base.Printer.Display(data, nil) + }, + } + + recordList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + recordList.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Record Get + recordGet := &cobra.Command{ + Use: "get ", + Short: "Get a DNS record", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a domain name and record ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + rec, err := o.recordGet() + if err != nil { + printer.Error(fmt.Errorf("error while getting domain record : %v", err)) + os.Exit(1) + } + + data := &DNSRecordPrinter{Record: *rec} + o.Base.Printer.Display(data, nil) + }, + } + + // Record Create + recordCreate := &cobra.Command{ + Use: "create ", + Short: "Create a dns record", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a domain name") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + rType, errTy := cmd.Flags().GetString("type") + if errTy != nil { + printer.Error(fmt.Errorf("error parsing 'type' flag for domain record create : %v", errTy)) + os.Exit(1) + } + + name, errNa := cmd.Flags().GetString("name") + if errNa != nil { + printer.Error(fmt.Errorf("error parsing 'name' flag for domain record create : %v", errNa)) + os.Exit(1) + } + + dt, errDa := cmd.Flags().GetString("data") + if errDa != nil { + printer.Error(fmt.Errorf("error parsing 'data' flag for domain record create : %v", errDa)) + os.Exit(1) + } + + ttl, errTt := cmd.Flags().GetInt("ttl") + if errTt != nil { + printer.Error(fmt.Errorf("error parsing 'ttl' flag for domain record create : %v", errTt)) + os.Exit(1) + } + + priority, errPr := cmd.Flags().GetInt("priority") + if errPr != nil { + printer.Error(fmt.Errorf("error parsing 'priority' flag for domain record create : %v", errPr)) + os.Exit(1) + } + + o.RecordReq = &govultr.DomainRecordReq{ + Name: name, + Type: rType, + Data: dt, + TTL: ttl, + Priority: &priority, + } + + rec, err := o.recordCreate() + if err != nil { + printer.Error(fmt.Errorf("error creating domain record : %v", err)) + os.Exit(1) + } + + data := &DNSRecordPrinter{Record: *rec} + o.Base.Printer.Display(data, nil) + }, + } + + recordCreate.Flags().StringP("type", "t", "", "type for the record") + if err := recordCreate.MarkFlagRequired("type"); err != nil { + printer.Error(fmt.Errorf("error marking dns record create 'type' flag required: %v", err)) + os.Exit(1) + } + + recordCreate.Flags().StringP("name", "n", "", "name of the record") + if err := recordCreate.MarkFlagRequired("name"); err != nil { + printer.Error(fmt.Errorf("error marking dns record create 'name' flag required: %v", err)) + os.Exit(1) + } + + recordCreate.Flags().StringP("data", "d", "", "data for the record") + if err := recordCreate.MarkFlagRequired("data"); err != nil { + printer.Error(fmt.Errorf("error marking dns record create 'data' flag required: %v", err)) + os.Exit(1) + } + + recordCreate.Flags().IntP("ttl", "l", 0, "ttl for the record") + if err := recordCreate.MarkFlagRequired("ttl"); err != nil { + printer.Error(fmt.Errorf("error marking dns record create 'ttl' flag required: %v", err)) + os.Exit(1) + } + + recordCreate.Flags().IntP("priority", "p", 0, "only required for MX and SRV") + if err := recordCreate.MarkFlagRequired("priority"); err != nil { + printer.Error(fmt.Errorf("error marking dns record create 'priority' flag required: %v", err)) + os.Exit(1) + } + + // Record Delete + recordDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete DNS record", + Aliases: []string{"destroy"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a domain name & record ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.recordDelete(); err != nil { + printer.Error(fmt.Errorf("error deleting domain record : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("domain record has been deleted"), nil) + }, + } + + // Record Update + recordUpdate := &cobra.Command{ + Use: "update ", + Short: "Update DNS record", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a domain name & record ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + name, errNa := cmd.Flags().GetString("name") + if errNa != nil { + printer.Error(fmt.Errorf("error parsing 'name' flag for domain record update : %v", errNa)) + os.Exit(1) + } + + dt, errDa := cmd.Flags().GetString("data") + if errDa != nil { + printer.Error(fmt.Errorf("error parsing 'data' flag for domain record update : %v", errDa)) + os.Exit(1) + } + + ttl, errTt := cmd.Flags().GetInt("ttl") + if errTt != nil { + printer.Error(fmt.Errorf("error parsing 'ttl' flag for domain record update : %v", errTt)) + os.Exit(1) + } + + priority, errPr := cmd.Flags().GetInt("priority") + if errPr != nil { + printer.Error(fmt.Errorf("error parsing 'priority' flag for domain record update : %v", errPr)) + os.Exit(1) + } + + o.RecordReq = &govultr.DomainRecordReq{} + + if cmd.Flags().Changed("name") { + o.RecordReq.Name = name + } + + if cmd.Flags().Changed("data") { + o.RecordReq.Data = dt + } + + if cmd.Flags().Changed("ttl") { + o.RecordReq.TTL = ttl + } + + if cmd.Flags().Changed("priority") { + o.RecordReq.Priority = govultr.IntToIntPtr(priority) + } + + if err := o.recordUpdate(); err != nil { + printer.Error(fmt.Errorf("error updating domain record : %v", errPr)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("domain record has been updated"), nil) + }, + } + + recordUpdate.Flags().StringP("name", "n", "", "name of record") + recordUpdate.Flags().StringP("data", "d", "", "data for the record") + recordUpdate.Flags().IntP("ttl", "", 0, "time to live for the record") + recordUpdate.Flags().IntP("priority", "p", 0, "only required for MX and SRV") + + record.AddCommand( + recordList, + recordGet, + recordCreate, + recordUpdate, + recordDelete, + ) + + cmd.AddCommand( + domain, + record, + ) + + return cmd +} + +type options struct { + Base *cli.Base + DomainCreateReq *govultr.DomainReq + DomainDNSSECEnabled string + SOAUpdateReq *govultr.Soa + RecordReq *govultr.DomainRecordReq +} + +// domainList ... +func (o *options) domainList() ([]govultr.Domain, *govultr.Meta, error) { + dms, meta, _, err := o.Base.Client.Domain.List(o.Base.Context, o.Base.Options) + return dms, meta, err +} + +// domainGet ... +func (o *options) domainGet() (*govultr.Domain, error) { + dm, _, err := o.Base.Client.Domain.Get(o.Base.Context, o.Base.Args[0]) + return dm, err +} + +// domainCreate ... +func (o *options) domainCreate() (*govultr.Domain, error) { + dm, _, err := o.Base.Client.Domain.Create(o.Base.Context, o.DomainCreateReq) + return dm, err +} + +// domainUpdate ... +func (o *options) domainUpdate() error { + return o.Base.Client.Domain.Update(o.Base.Context, o.Base.Args[0], o.DomainDNSSECEnabled) +} + +// domainDelete ... +func (o *options) domainDelete() error { + return o.Base.Client.Domain.Delete(o.Base.Context, o.Base.Args[0]) +} + +// domainDNSSECGet ... +func (o *options) domainDNSSECGet() ([]string, error) { + sec, _, err := o.Base.Client.Domain.GetDNSSec(o.Base.Context, o.Base.Args[0]) + return sec, err +} + +// domainSOAGet ... +func (o *options) domainSOAGet() (*govultr.Soa, error) { + soa, _, err := o.Base.Client.Domain.GetSoa(o.Base.Context, o.Base.Args[0]) + return soa, err +} + +// domainSOAUpdate ... +func (o *options) domainSOAUpdate() error { + return o.Base.Client.Domain.UpdateSoa(o.Base.Context, o.Base.Args[0], o.SOAUpdateReq) +} + +// recordList ... +func (o *options) recordList() ([]govultr.DomainRecord, *govultr.Meta, error) { + rec, meta, _, err := o.Base.Client.DomainRecord.List(o.Base.Context, o.Base.Args[0], o.Base.Options) + return rec, meta, err +} + +// recordGet ... +func (o *options) recordGet() (*govultr.DomainRecord, error) { + rec, _, err := o.Base.Client.DomainRecord.Get(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) + return rec, err +} + +// recordCreate ... +func (o *options) recordCreate() (*govultr.DomainRecord, error) { + rec, _, err := o.Base.Client.DomainRecord.Create(o.Base.Context, o.Base.Args[0], o.RecordReq) + return rec, err +} + +// recordUpdate ... +func (o *options) recordUpdate() error { + return o.Base.Client.DomainRecord.Update(o.Base.Context, o.Base.Args[0], o.Base.Args[1], o.RecordReq) +} + +// recordDelete ... +func (o *options) recordDelete() error { + return o.Base.Client.DomainRecord.Delete(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} diff --git a/cmd/dns/printer.go b/cmd/dns/printer.go new file mode 100644 index 00000000..4dea4dfd --- /dev/null +++ b/cmd/dns/printer.go @@ -0,0 +1,279 @@ +package dns + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// DNSRecordsPrinter ... +type DNSRecordsPrinter struct { + Records []govultr.DomainRecord `json:"records"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (d *DNSRecordsPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DNSRecordsPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DNSRecordsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "TYPE", + "NAME", + "DATA", + "PRIORITY", + "TTL", + }} +} + +// Data ... +func (d *DNSRecordsPrinter) Data() [][]string { + if len(d.Records) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range d.Records { + data = append(data, []string{ + d.Records[i].ID, + d.Records[i].Type, + d.Records[i].Name, + d.Records[i].Data, + strconv.Itoa(d.Records[i].Priority), + strconv.Itoa(d.Records[i].TTL), + }) + } + + return data +} + +// Paging ... +func (d *DNSRecordsPrinter) Paging() [][]string { + return printer.NewPaging(d.Meta.Total, &d.Meta.Links.Next, &d.Meta.Links.Prev).Compose() +} + +// ====================================== + +// DNSRecordPrinter ... +type DNSRecordPrinter struct { + Record govultr.DomainRecord `json:"records"` +} + +// JSON ... +func (d *DNSRecordPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DNSRecordPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DNSRecordPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "TYPE", + "NAME", + "DATA", + "PRIORITY", + "TTL", + }} +} + +// Data ... +func (d *DNSRecordPrinter) Data() [][]string { + return [][]string{0: { + d.Record.ID, + d.Record.Type, + d.Record.Name, + d.Record.Data, + strconv.Itoa(d.Record.Priority), + strconv.Itoa(d.Record.TTL), + }} +} + +// Paging ... +func (d *DNSRecordPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// DNSDomainsPrinter ... +type DNSDomainsPrinter struct { + Domains []govultr.Domain `json:"domains"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (d *DNSDomainsPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DNSDomainsPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DNSDomainsPrinter) Columns() [][]string { + return [][]string{0: { + "DOMAIN", + "DATE CREATED", + "DNSSEC", + }} +} + +// Data ... +func (d *DNSDomainsPrinter) Data() [][]string { + if len(d.Domains) == 0 { + return [][]string{0: {"---", "---", "---"}} + } + + var data [][]string + for i := range d.Domains { + data = append(data, []string{ + d.Domains[i].Domain, + d.Domains[i].DateCreated, + d.Domains[i].DNSSec, + }) + } + + return data +} + +// Paging ... +func (d *DNSDomainsPrinter) Paging() [][]string { + return printer.NewPaging(d.Meta.Total, &d.Meta.Links.Next, &d.Meta.Links.Prev).Compose() +} + +// ====================================== + +// DNSDomainPrinter ... +type DNSDomainPrinter struct { + Domain govultr.Domain `json:"domain"` +} + +// JSON ... +func (d *DNSDomainPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DNSDomainPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DNSDomainPrinter) Columns() [][]string { + return [][]string{0: { + "DOMAIN", + "DATE CREATED", + "DNS SEC", + }} +} + +// Data ... +func (d *DNSDomainPrinter) Data() [][]string { + return [][]string{0: { + d.Domain.Domain, + d.Domain.DateCreated, + d.Domain.DNSSec, + }} +} + +// Paging ... +func (d *DNSDomainPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// DNSSOAPrinter ... +type DNSSOAPrinter struct { + SOA govultr.Soa `json:"dns_soa"` +} + +// JSON ... +func (d *DNSSOAPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DNSSOAPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DNSSOAPrinter) Columns() [][]string { + return [][]string{0: { + "NS PRIMARY", + "EMAIL", + }} +} + +// Data ... +func (d *DNSSOAPrinter) Data() [][]string { + return [][]string{0: { + d.SOA.NSPrimary, + d.SOA.Email, + }} +} + +// Paging ... +func (d *DNSSOAPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// DNSSECPrinter ... +type DNSSECPrinter struct { + SEC []string `json:"dns_sec"` +} + +// JSON ... +func (d *DNSSECPrinter) JSON() []byte { + return printer.MarshalObject(d, "json") +} + +// YAML ... +func (d *DNSSECPrinter) YAML() []byte { + return printer.MarshalObject(d, "yaml") +} + +// Columns ... +func (d *DNSSECPrinter) Columns() [][]string { + return [][]string{0: { + "DNSSEC INFO", + }} +} + +// Data ... +func (d *DNSSECPrinter) Data() [][]string { + if len(d.SEC) == 0 { + return [][]string{0: {"---"}} + } + + var data [][]string + for i := range d.SEC { + data = append(data, []string{d.SEC[i]}) + } + + return data +} + +// Paging ... +func (d *DNSSECPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/dnsDomain.go b/cmd/dnsDomain.go deleted file mode 100644 index 09b99c61..00000000 --- a/cmd/dnsDomain.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// DNSDomain represents the domain sub command -func DNSDomain() *cobra.Command { - dnsDomainCmd := &cobra.Command{ - Use: "domain", - Short: "dns domain", - Long: ``, - } - - dnsDomainCmd.AddCommand(domainCreate, domainGet, domainDelete, secEnable, secInfo, domainList, soaInfo, soaUpdate) - - // Create - domainCreate.Flags().StringP("domain", "d", "", "name of the domain") - domainCreate.MarkFlagRequired("domain") - domainCreate.Flags().StringP("ip", "i", "", "instance ip you want to assign this domain to") - - // Dns Sec - secEnable.Flags().StringP("enabled", "e", "", "set whether dns sec is enabled or not. true or false") - secEnable.MarkFlagRequired("enabled") - - // Soa Update - soaUpdate.Flags().StringP("ns-primary", "n", "", "primary nameserver to store in the SOA record") - soaUpdate.MarkFlagRequired("ns-primary") - soaUpdate.Flags().StringP("email", "e", "", "administrative email to store in the SOA record") - - // List - domainList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - domainList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return dnsDomainCmd -} - -var domainCreate = &cobra.Command{ - Use: "create", - Short: "create a domain", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - domain, _ := cmd.Flags().GetString("domain") - ip, _ := cmd.Flags().GetString("ip") - - options := &govultr.DomainReq{ - Domain: domain, - IP: ip, - } - - dns, err := client.Domain.Create(context.Background(), options) - if err != nil { - fmt.Printf("error creating dns domain : %v\n", err) - os.Exit(1) - } - - printer.Domain(dns) - }, -} - -var domainDelete = &cobra.Command{ - Use: "delete ", - Short: "delete a domain", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a domain name") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - if err := client.Domain.Delete(context.Background(), domain); err != nil { - fmt.Printf("error delete dns domain : %v\n", err) - os.Exit(1) - } - - fmt.Println("deleted dns domain") - }, -} - -var secEnable = &cobra.Command{ - Use: "dnssec ", - Short: "enable/disable dnssec", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a domain name") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - enabled, _ := cmd.Flags().GetString("enabled") - if err := client.Domain.Update(context.Background(), domain, enabled); err != nil { - fmt.Printf("error toggling dnssec : %v\n", err) - os.Exit(1) - } - - fmt.Println("toggled dns sec") - }, -} - -var secInfo = &cobra.Command{ - Use: "dnssec-info ", - Short: "get dns sec info", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a domain name") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - info, err := client.Domain.GetDNSSec(context.Background(), domain) - if err != nil { - fmt.Printf("error getting dnssec info : %v\n", err) - os.Exit(1) - } - - printer.SecInfo(info) - }, -} - -var domainGet = &cobra.Command{ - Use: "get ", - Short: "get a domain", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - domain, err := client.Domain.Get(context.Background(), id) - if err != nil { - fmt.Printf("error getting domain : %v\n", err) - os.Exit(1) - } - - printer.Domain(domain) - }, -} - -var domainList = &cobra.Command{ - Use: "list", - Short: "get list of domains", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - list, meta, err := client.Domain.List(context.Background(), options) - if err != nil { - fmt.Printf("error getting domains : %v\n", err) - os.Exit(1) - } - - printer.DomainList(list, meta) - }, -} - -var soaInfo = &cobra.Command{ - Use: "soa-info ", - Short: "get dns soa info", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a domain name") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - info, err := client.Domain.GetSoa(context.Background(), domain) - if err != nil { - fmt.Printf("error toggling dnssec : %v\n", err) - os.Exit(1) - } - - printer.SoaInfo(info) - }, -} - -var soaUpdate = &cobra.Command{ - Use: "soa-update ", - Short: "update soa for a domain", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a domain name") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - nsPrimary, _ := cmd.Flags().GetString("ns-primary") - email, _ := cmd.Flags().GetString("email") - - soaUpdate := &govultr.Soa{ - NSPrimary: nsPrimary, - Email: email, - } - - if err := client.Domain.UpdateSoa(context.Background(), domain, soaUpdate); err != nil { - fmt.Printf("error toggling dnssec : %v\n", err) - os.Exit(1) - } - - fmt.Println("updated SOA") - }, -} diff --git a/cmd/dnsRecord.go b/cmd/dnsRecord.go deleted file mode 100644 index f06e0c1f..00000000 --- a/cmd/dnsRecord.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - "regexp" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// DNSRecord represents the dnsRecord command -func DNSRecord() *cobra.Command { - dnsRecordCmd := &cobra.Command{ - Use: "record", - Short: "dns record", - Long: ``, - } - - dnsRecordCmd.AddCommand(recordCreate, recordGet, recordList, recordDelete, recordUpdate) - - // Create - recordCreate.Flags().StringP("domain", "m", "", "name of domain you want to create this record for") - recordCreate.Flags().StringP("type", "t", "", "record type you want to create : Possible values A, AAAA, CNAME, NS, MX, SRV, TXT CAA, SSHFP") - recordCreate.Flags().StringP("name", "n", "", "name of record") - recordCreate.Flags().StringP("data", "d", "", "data for the record") - recordCreate.MarkFlagRequired("domain") - recordCreate.MarkFlagRequired("type") - recordCreate.MarkFlagRequired("name") - recordCreate.MarkFlagRequired("data") - // Create Optional - recordCreate.Flags().IntP("ttl", "", 0, "time to live for the record") - recordCreate.Flags().IntP("priority", "p", 0, "only required for MX and SRV") - - // Update - recordUpdate.Flags().StringP("name", "n", "", "name of record") - recordUpdate.Flags().StringP("data", "d", "", "data for the record") - recordUpdate.Flags().IntP("ttl", "", 0, "time to live for the record") - recordUpdate.Flags().IntP("priority", "p", -1, "only required for MX and SRV") - - // List - recordList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - recordList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return dnsRecordCmd -} - -// Temporary solution to determine if the record type is TXT, in order to -// add quotes around the value. The API does not accept TXT records without -// quotes. -var regRecordTxt = regexp.MustCompile("([A-Z]|=|_)") - -var recordCreate = &cobra.Command{ - Use: "create", - Short: "create a dns record", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - domain, _ := cmd.Flags().GetString("domain") - rType, _ := cmd.Flags().GetString("type") - name, _ := cmd.Flags().GetString("name") - data, _ := cmd.Flags().GetString("data") - // Record data for TXT must be enclosed in quotes - if data[0] != '"' && data[len(data)-1] != '"' && regRecordTxt.Match([]byte(data)) { - data = fmt.Sprintf("\"%s\"", data) - } - ttl, _ := cmd.Flags().GetInt("ttl") - priority, _ := cmd.Flags().GetInt("priority") - - options := &govultr.DomainRecordReq{ - Name: name, - Type: rType, - Data: data, - TTL: ttl, - Priority: &priority, - } - - record, err := client.DomainRecord.Create(context.Background(), domain, options) - if err != nil { - fmt.Printf("error while creating dns record : %v\n", err) - os.Exit(1) - } - - printer.DnsRecord(record) - }, -} - -var recordGet = &cobra.Command{ - Use: "get ", - Short: "get dns record", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a domain name and recordID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - id := args[1] - - record, err := client.DomainRecord.Get(context.Background(), domain, id) - if err != nil { - fmt.Printf("error while getting dns records : %v\n", err) - os.Exit(1) - } - - printer.DnsRecord(record) - }, -} - -var recordList = &cobra.Command{ - Use: "list ", - Short: "list all dns records", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a domain name") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - options := getPaging(cmd) - - records, meta, err := client.DomainRecord.List(context.Background(), domain, options) - if err != nil { - fmt.Printf("error while getting dns records : %v\n", err) - os.Exit(1) - } - - printer.DnsRecordsList(records, meta) - }, -} - -var recordDelete = &cobra.Command{ - Use: "delete ", - Short: "delete dns record", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a domainName & recordID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - id := args[1] - - if err := client.DomainRecord.Delete(context.Background(), domain, id); err != nil { - fmt.Printf("error while deleting dns record : %v\n", err) - os.Exit(1) - } - - fmt.Println("deleted dns record") - }, -} - -var recordUpdate = &cobra.Command{ - Use: "update ", - Short: "update dns record", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a domainName & recordID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - domain := args[0] - id := args[1] - name, _ := cmd.Flags().GetString("name") - data, _ := cmd.Flags().GetString("data") - ttl, _ := cmd.Flags().GetInt("ttl") - priority, _ := cmd.Flags().GetInt("priority") - - updates := &govultr.DomainRecordReq{} - - if name != "" { - updates.Name = name - } - - if data != "" { - // Record data for TXT must be enclosed in quotes - if data[0] != '"' && data[len(data)-1] != '"' && regRecordTxt.Match([]byte(data)) { - data = fmt.Sprintf("\"%s\"", data) - } - updates.Data = data - } - - if ttl != 0 { - updates.TTL = ttl - } - - if priority != -1 { - updates.Priority = &priority - } - - if err := client.DomainRecord.Update(context.Background(), domain, id, updates); err != nil { - fmt.Printf("error updating dns record : %v\n", err) - os.Exit(1) - } - - fmt.Println("updated dns record") - }, -} diff --git a/cmd/firewall.go b/cmd/firewall.go deleted file mode 100644 index 23899c32..00000000 --- a/cmd/firewall.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "github.com/spf13/cobra" -) - -// Firewall represents the firewall command -func Firewall() *cobra.Command { - firewallCmd := &cobra.Command{ - Use: "firewall", - Short: "firewall is used to access firewall commands", - Long: ``, - Aliases: []string{"fw"}, - } - - firewallCmd.AddCommand(FirewallGroup(), FirewallRule()) - - return firewallCmd -} diff --git a/cmd/firewall/firewall.go b/cmd/firewall/firewall.go new file mode 100644 index 00000000..06a3ca04 --- /dev/null +++ b/cmd/firewall/firewall.go @@ -0,0 +1,538 @@ +// Package firewall provides the functionality for firewall commands in the CLI +package firewall + +import ( + "errors" + "fmt" + "os" + "strconv" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + ruleLong = `Show commands available for firewall rules` + ruleExample = ` + # Full example + vultr-cli firewall rule + + # Shortened example with aliases + vultr-cli fw r + ` + ruleCreateLong = ` + Create a new firewall rule in the provided firewall group + + If protocol is TCP or UDP, port must be provided. + + An ip-type of v4 or v6 must be supplied for all rules. + ` + ruleCreateExample = ` + # Full examples + vultr-cli firewall rule create --id=f04ae5aa-ff6a-4078-900d-78cc17dca2d5 --ip-type=v4 --protocol=tcp --size=24 \ + --subnet=127.0.0.0 --port=30000 + + vultr-cli firewall rule create --id=f04ae5aa-ff6a-4078-900d-78cc17dca2d5 --ip-type=v4 --protocol=icmp --size=24 --subnet=127.0.0.0 + + # Shortened example with aliases + vultr-cli fw r c -i=f04ae5aa-ff6a-4078-900d-78cc17dca2d5 -t=v4 -p=tcp -z=24 -s=127.0.0.0 -r=30000 + ` + ruleDeleteLong = `Delete a firewall rule in the provided firewall group` + ruleDeleteExample = ` + # Full example + vultr-cli firewall rule delete 704ac064-4ff2-49ca-a6e6-88262cca8f8a f31ade4f-2308-4a58-82c6-2d1bae0837b3 + + # Shortened example with aliases + vultr-cli fw r d 704ac064-4ff2-49ca-a6e6-88262cca8f8a f31ade4f-2308-4a58-82c6-2d1bae0837b3 + ` + ruleGetLong = `Get a firewall rule in the provided firewall group` + ruleGetExample = ` + # Full example + vultr-cli firewall rule get 704ac064-4ff2-49ca-a6e6-88262cca8f8a f31ade4f-2308-4a58-82c6-2d1bae0837b3 + + # Shortened example with aliases + vultr-cli fw r get 704ac064-4ff2-49ca-a6e6-88262cca8f8a f31ade4f-2308-4a58-82c6-2d1bae0837b3 + ` + ruleListLong = `List all firewall rules in the provided firewall group` + ruleListExample = ` + # Full example + vultr-cli firewall rule list 704ac064-4ff2-49ca-a6e6-88262cca8f8a + + # Shortened example with aliases + vultr-cli fw r l 704ac064-4ff2-49ca-a6e6-88262cca8f8a + ` +) + +// NewCmdFirewall provides the CLI command functionality for Firewall +func NewCmdFirewall(base *cli.Base) *cobra.Command { //nolint:gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "firewall", + Short: "Access firewall commands", + Aliases: []string{"fw"}, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // Group + group := &cobra.Command{ + Use: "group", + Short: "Commands to access firewall group functions", + Aliases: []string{"g"}, + } + + // Group List + groupList := &cobra.Command{ + Use: "list", + Short: "List all firewall groups", + Aliases: []string{"l"}, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + + groups, meta, err := o.listGroups() + if err != nil { + printer.Error(fmt.Errorf("error retrieving firewall group list : %v", err)) + os.Exit(1) + } + + data := &FirewallGroupsPrinter{Groups: groups, Meta: meta} + o.Base.Printer.Display(data, nil) + }, + } + + groupList.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + groupList.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Group Get + groupGet := &cobra.Command{ + Use: "get ", + Short: "Get firewall group", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a firewall group ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + group, err := o.getGroup() + if err != nil { + printer.Error(fmt.Errorf("error getting firewall group : %v", err)) + os.Exit(1) + } + + data := &FirewallGroupPrinter{Group: *group} + o.Base.Printer.Display(data, nil) + }, + } + + // Group Create + groupCreate := &cobra.Command{ + Use: "create", + Short: "create a firewall group", + Aliases: []string{"c"}, + Run: func(cmd *cobra.Command, args []string) { + description, errDe := cmd.Flags().GetString("description") + if errDe != nil { + printer.Error(fmt.Errorf("error parsing 'description' flag for firewall group create: %v", errDe)) + os.Exit(1) + } + + o.GroupReq = &govultr.FirewallGroupReq{ + Description: description, + } + + grp, err := o.createGroup() + if err != nil { + printer.Error(fmt.Errorf("error creating firewall group : %v", err)) + os.Exit(1) + } + + data := &FirewallGroupPrinter{Group: *grp} + o.Base.Printer.Display(data, nil) + }, + } + + groupCreate.Flags().StringP("description", "d", "", "(optional) Description of firewall group.") + + // Group Update + groupUpdate := &cobra.Command{ + Use: "update ", + Short: "Update firewall group description", + Aliases: []string{"u"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a firewall group ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + description, errDe := cmd.Flags().GetString("description") + if errDe != nil { + printer.Error(fmt.Errorf("error parsing 'description' flag for firewall group update : %v", errDe)) + os.Exit(1) + } + + o.GroupReq = &govultr.FirewallGroupReq{ + Description: description, + } + + if err := o.updateGroup(); err != nil { + printer.Error(fmt.Errorf("error updating firewall group : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("firewall group has been updated"), nil) + }, + } + + groupUpdate.Flags().StringP("description", "d", "", "Description of firewall group.") + if err := groupUpdate.MarkFlagRequired("description"); err != nil { + printer.Error(fmt.Errorf("error marking firewall group 'description' flag required: %v", err)) + os.Exit(1) + } + + // Group Delete + groupDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete a firewall group", + Aliases: []string{"d", "destroy"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a firewall group ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.deleteGroup(); err != nil { + printer.Error(fmt.Errorf("error deleting firewall group : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("firewall group has been deleted"), nil) + }, + } + + group.AddCommand( + groupList, + groupGet, + groupCreate, + groupUpdate, + groupDelete, + ) + + // Rule + rule := &cobra.Command{ + Use: "rule", + Short: "Commands to access firewall rules", + Aliases: []string{"r"}, + Long: ruleLong, + Example: ruleExample, + } + + // Rule List + ruleList := &cobra.Command{ + Use: "list ", + Short: "List all firewall rules", + Aliases: []string{"l"}, + Long: ruleListLong, + Example: ruleListExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a firewall group ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + o.Base.Options = utils.GetPaging(cmd) + + rules, meta, err := o.listRules() + if err != nil { + printer.Error(fmt.Errorf("error retrieving firewall rule list : %v", err)) + os.Exit(1) + } + + data := &FirewallRulesPrinter{Rules: rules, Meta: meta} + o.Base.Printer.Display(data, nil) + }, + } + + ruleList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + ruleList.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + "(optional) Number of items requested per page. Default is 100 and Max is 500.", + ) + + // Rule Get + ruleGet := &cobra.Command{ + Use: "get ", + Short: "Get firewall rule", + Long: ruleGetLong, + Example: ruleGetExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a firewall group ID and firewall rule number") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + rule, err := o.getRule() + if err != nil { + printer.Error(fmt.Errorf("error getting firewall rule : %v", err)) + os.Exit(1) + } + + data := &FirewallRulePrinter{Rule: *rule} + o.Base.Printer.Display(data, nil) + }, + } + + // Rule Create + ruleCreate := &cobra.Command{ + Use: "create", + Short: "Create a firewall rule", + Aliases: []string{"c"}, + Long: ruleCreateLong, + Example: ruleCreateExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a firewall group ID") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + protocol, errPr := cmd.Flags().GetString("protocol") + if errPr != nil { + printer.Error(fmt.Errorf("error parsing 'protocol' flag for firewall group create : %v", errPr)) + os.Exit(1) + } + + subnet, errSu := cmd.Flags().GetString("subnet") + if errSu != nil { + printer.Error(fmt.Errorf("error parsing 'subnet' flag for firewall group create : %v", errSu)) + os.Exit(1) + } + + ipType, errIP := cmd.Flags().GetString("ip-type") + if errIP != nil { + printer.Error(fmt.Errorf("error parsing 'ip-type' flag for firewall group create : %v", errIP)) + os.Exit(1) + } + + size, errSi := cmd.Flags().GetInt("size") + if errSi != nil { + printer.Error(fmt.Errorf("error parsing 'size' flag for firewall group create : %v", errSi)) + os.Exit(1) + } + + source, errSo := cmd.Flags().GetString("source") + if errSo != nil { + printer.Error(fmt.Errorf("error parsing 'source' flag for firewall group create : %v", errSo)) + os.Exit(1) + } + + port, errPo := cmd.Flags().GetString("port") + if errPo != nil { + printer.Error(fmt.Errorf("error parsing 'port' flag for firewall group create : %v", errPo)) + os.Exit(1) + } + + notes, errNo := cmd.Flags().GetString("notes") + if errNo != nil { + printer.Error(fmt.Errorf("error parsing 'notes' flag for firewall group create : %v", errNo)) + os.Exit(1) + } + + o.RuleReq = &govultr.FirewallRuleReq{ + Protocol: protocol, + Subnet: subnet, + SubnetSize: size, + Notes: notes, + } + + if port != "" { + o.RuleReq.Port = port + } + + if source != "" { + o.RuleReq.Source = source + } + + if ipType == "" { + printer.Error(fmt.Errorf("a firewall rule requires an IP type. Pass an --ip-type value of v4 or v6")) + os.Exit(1) + } + + if ipType != "" { + o.RuleReq.IPType = ipType + } + + rule, err := o.createRule() + if err != nil { + printer.Error(fmt.Errorf("error creating firewall rule : %v", err)) + os.Exit(1) + } + + data := &FirewallRulePrinter{Rule: *rule} + o.Base.Printer.Display(data, nil) + }, + } + + ruleCreate.Flags().StringP("protocol", "p", "", "Protocol type. Possible values: 'icmp', 'tcp', 'udp', 'gre'.") + if err := ruleCreate.MarkFlagRequired("protocol"); err != nil { + printer.Error(fmt.Errorf("error marking firewall rule create 'protocol' flag required : %v", err)) + os.Exit(1) + } + + ruleCreate.Flags().StringP("subnet", "s", "", "The IPv4 network in CIDR notation.") + if err := ruleCreate.MarkFlagRequired("subnet"); err != nil { + printer.Error(fmt.Errorf("error marking firewall rule create 'subnet' flag required : %v", err)) + os.Exit(1) + } + + ruleCreate.Flags().IntP("size", "z", 0, "The number of bits for the netmask in CIDR notation.") + if err := ruleCreate.MarkFlagRequired("size"); err != nil { + printer.Error(fmt.Errorf("error marking firewall rule create 'size' flag required : %v", err)) + os.Exit(1) + } + + ruleCreate.Flags().StringP( + "source", + "", + "", + "(optional) When empty, uses value from subnet and size. If \"cloudflare\", allows all Cloudflare IP space through firewall.", + ) + + ruleCreate.Flags().StringP("ip-type", "t", "", "The type of IP rule - v4 or v6.") + if err := ruleCreate.MarkFlagRequired("ip-type"); err != nil { + printer.Error(fmt.Errorf("error marking firewall rule create 'ip-type' flag required : %v", err)) + os.Exit(1) + } + + ruleCreate.Flags().StringP( + "port", + "r", + "", + "(optional) TCP/UDP only. This field can be an integer value specifying a port or a colon separated port range.", + ) + + ruleCreate.Flags().StringP("notes", "n", "", "(optional) This field supports notes up to 255 characters.") + + // Rule Delete + ruleDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete a firewall rule", + Aliases: []string{"d", "destroy"}, + Long: ruleDeleteLong, + Example: ruleDeleteExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a firewall group ID and firewall rule number") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + if err := o.deleteRule(); err != nil { + printer.Error(fmt.Errorf("error deleting firewall rule : %v", err)) + os.Exit(1) + } + + o.Base.Printer.Display(printer.Info("firewall rule deleted"), nil) + }, + } + + rule.AddCommand( + ruleList, + ruleGet, + ruleCreate, + ruleDelete, + ) + + cmd.AddCommand( + group, + rule, + ) + + return cmd +} + +type options struct { + Base *cli.Base + GroupReq *govultr.FirewallGroupReq + RuleReq *govultr.FirewallRuleReq +} + +// listGroups ... +func (o *options) listGroups() ([]govultr.FirewallGroup, *govultr.Meta, error) { + groups, meta, _, err := o.Base.Client.FirewallGroup.List(o.Base.Context, o.Base.Options) + return groups, meta, err +} + +// getGroup ... +func (o *options) getGroup() (*govultr.FirewallGroup, error) { + group, _, err := o.Base.Client.FirewallGroup.Get(o.Base.Context, o.Base.Args[0]) + return group, err +} + +// createGroup ... +func (o *options) createGroup() (*govultr.FirewallGroup, error) { + group, _, err := o.Base.Client.FirewallGroup.Create(o.Base.Context, o.GroupReq) + return group, err +} + +// updateGroup ... +func (o *options) updateGroup() error { + return o.Base.Client.FirewallGroup.Update(o.Base.Context, o.Base.Args[0], o.GroupReq) +} + +// deleteGroup ... +func (o *options) deleteGroup() error { + return o.Base.Client.FirewallGroup.Delete(o.Base.Context, o.Base.Args[0]) +} + +// listRules ... +func (o *options) listRules() ([]govultr.FirewallRule, *govultr.Meta, error) { + rules, meta, _, err := o.Base.Client.FirewallRule.List(o.Base.Context, o.Base.Args[0], o.Base.Options) + return rules, meta, err +} + +// getRule ... +func (o *options) getRule() (*govultr.FirewallRule, error) { + id, errIn := strconv.Atoi(o.Base.Args[1]) + if errIn != nil { + return nil, fmt.Errorf("unable to convert int to string : %v", errIn) + } + + rule, _, err := o.Base.Client.FirewallRule.Get(o.Base.Context, o.Base.Args[0], id) + return rule, err +} + +// createRule ... +func (o *options) createRule() (*govultr.FirewallRule, error) { + rule, _, err := o.Base.Client.FirewallRule.Create(o.Base.Context, o.Base.Args[0], o.RuleReq) + return rule, err +} + +// deleteRule ... +func (o *options) deleteRule() error { + id, err := strconv.Atoi(o.Base.Args[1]) + if err != nil { + return fmt.Errorf("unable to convert int to string : %v", err) + } + return o.Base.Client.FirewallRule.Delete(o.Base.Context, o.Base.Args[0], id) +} diff --git a/cmd/firewall/printer.go b/cmd/firewall/printer.go new file mode 100644 index 00000000..9fc1a53b --- /dev/null +++ b/cmd/firewall/printer.go @@ -0,0 +1,223 @@ +package firewall + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" +) + +// FirewallGroupsPrinter ... +type FirewallGroupsPrinter struct { + Groups []govultr.FirewallGroup `json:"firewall_groups"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (f *FirewallGroupsPrinter) JSON() []byte { + return printer.MarshalObject(f, "json") +} + +// YAML ... +func (f *FirewallGroupsPrinter) YAML() []byte { + return printer.MarshalObject(f, "yaml") +} + +// Columns ... +func (f *FirewallGroupsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "DATE CREATED", + "DATE MODIFIED", + "INSTANCE COUNT", + "RULE COUNT", + "MAX RULE COUNT", + "DESCRIPTION", + }} +} + +// Data ... +func (f *FirewallGroupsPrinter) Data() [][]string { + if len(f.Groups) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range f.Groups { + data = append(data, []string{ + f.Groups[i].ID, + f.Groups[i].DateCreated, + f.Groups[i].DateModified, + strconv.Itoa(f.Groups[i].InstanceCount), + strconv.Itoa(f.Groups[i].RuleCount), + strconv.Itoa(f.Groups[i].MaxRuleCount), + f.Groups[i].Description, + }) + } + + return data +} + +// Paging ... +func (f *FirewallGroupsPrinter) Paging() [][]string { + return printer.NewPaging(f.Meta.Total, &f.Meta.Links.Next, &f.Meta.Links.Prev).Compose() +} + +// ====================================== + +// FirewallGroupPrinter ... +type FirewallGroupPrinter struct { + Group govultr.FirewallGroup `json:"firewall_group"` +} + +// JSON ... +func (f *FirewallGroupPrinter) JSON() []byte { + return printer.MarshalObject(f, "json") +} + +// YAML ... +func (f *FirewallGroupPrinter) YAML() []byte { + return printer.MarshalObject(f, "yaml") +} + +// Columns ... +func (f *FirewallGroupPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "DATE CREATED", + "DATE MODIFIED", + "INSTANCE COUNT", + "RULE COUNT", + "MAX RULE COUNT", + "DESCRIPTION", + }} +} + +// Data ... +func (f *FirewallGroupPrinter) Data() [][]string { + return [][]string{0: { + f.Group.ID, + f.Group.DateCreated, + f.Group.DateModified, + strconv.Itoa(f.Group.InstanceCount), + strconv.Itoa(f.Group.RuleCount), + strconv.Itoa(f.Group.MaxRuleCount), + f.Group.Description, + }} +} + +// Paging ... +func (f *FirewallGroupPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// FirewallRulesPrinter ... +type FirewallRulesPrinter struct { + Rules []govultr.FirewallRule `json:"firewall_rules"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (f *FirewallRulesPrinter) JSON() []byte { + return printer.MarshalObject(f, "json") +} + +// YAML ... +func (f *FirewallRulesPrinter) YAML() []byte { + return printer.MarshalObject(f, "yaml") +} + +// Columns ... +func (f *FirewallRulesPrinter) Columns() [][]string { + return [][]string{0: { + "RULE NUMBER", + "ACTION", + "TYPE", + "PROTOCOL", + "PORT", + "NETWORK", + "SOURCE", + "NOTES", + }} +} + +// Data ... +func (f *FirewallRulesPrinter) Data() [][]string { + if len(f.Rules) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range f.Rules { + data = append(data, []string{ + strconv.Itoa(f.Rules[i].ID), + f.Rules[i].Action, + f.Rules[i].IPType, + f.Rules[i].Protocol, + f.Rules[i].Port, + utils.FormatFirewallNetwork(f.Rules[i].Subnet, f.Rules[i].SubnetSize), + utils.GetFirewallSource(f.Rules[i].Source), + f.Rules[i].Notes, + }) + } + + return data +} + +// Paging ... +func (f *FirewallRulesPrinter) Paging() [][]string { + return printer.NewPaging(f.Meta.Total, &f.Meta.Links.Next, &f.Meta.Links.Prev).Compose() +} + +// ====================================== + +// FirewallRulePrinter ... +type FirewallRulePrinter struct { + Rule govultr.FirewallRule `json:"firewall_rule"` +} + +// JSON ... +func (f *FirewallRulePrinter) JSON() []byte { + return printer.MarshalObject(f, "json") +} + +// YAML ... +func (f *FirewallRulePrinter) YAML() []byte { + return printer.MarshalObject(f, "yaml") +} + +// Columns ... +func (f *FirewallRulePrinter) Columns() [][]string { + return [][]string{0: { + "RULE NUMBER", + "ACTION", + "TYPE", + "PROTOCOL", + "PORT", + "NETWORK", + "SOURCE", + "NOTES", + }} +} + +// Data ... +func (f *FirewallRulePrinter) Data() [][]string { + return [][]string{0: { + strconv.Itoa(f.Rule.ID), + f.Rule.Action, + f.Rule.IPType, + f.Rule.Protocol, + f.Rule.Port, + utils.FormatFirewallNetwork(f.Rule.Subnet, f.Rule.SubnetSize), + utils.GetFirewallSource(f.Rule.Source), + f.Rule.Notes, + }} +} + +// Paging ... +func (f *FirewallRulePrinter) Paging() [][]string { + return nil +} diff --git a/cmd/firewallGroup.go b/cmd/firewallGroup.go deleted file mode 100644 index a83f5798..00000000 --- a/cmd/firewallGroup.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// FirewallGroup represents the firewall group commands -func FirewallGroup() *cobra.Command { - firewallGroupCmd := &cobra.Command{ - Use: "group", - Short: "group is used to access firewall group commands", - Long: ``, - Aliases: []string{"g"}, - } - - firewallGroupCmd.AddCommand(firewallGroupCreate, firewallGroupDelete, firewallGroupGet, firewallGroupUpdate, firewallGroupList) - - firewallGroupCreate.Flags().StringP("description", "d", "", "(optional) Description of firewall group.") - - firewallGroupList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - firewallGroupList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return firewallGroupCmd -} - -var firewallGroupCreate = &cobra.Command{ - Use: "create", - Short: "create a firewall group", - Aliases: []string{"c"}, - Run: func(cmd *cobra.Command, args []string) { - description, _ := cmd.Flags().GetString("description") - options := &govultr.FirewallGroupReq{ - Description: description, - } - - fwg, err := client.FirewallGroup.Create(context.Background(), options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.FirewallGroup(fwg) - }, -} - -var firewallGroupDelete = &cobra.Command{ - Use: "delete ", - Short: "Delete a firewall group", - Aliases: []string{"d", "destroy"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a firewallGroupID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - if err := client.FirewallGroup.Delete(context.Background(), args[0]); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("Firewall group has been deleted") - }, -} - -var firewallGroupUpdate = &cobra.Command{ - Use: "update ", - Short: "Update firewall group description", - Aliases: []string{"u"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a firewallGroupID and description") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - description := args[1] - options := &govultr.FirewallGroupReq{ - Description: description, - } - - if err := client.FirewallGroup.Update(context.Background(), args[0], options); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("Firewall group has been updated") - }, -} - -var firewallGroupGet = &cobra.Command{ - Use: "get ", - Short: "Get firewall group", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a firewallGroupID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - list, meta, err := client.FirewallGroup.List(context.Background(), options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.FirewallGroups(list, meta) - }, -} - -var firewallGroupList = &cobra.Command{ - Use: "list", - Short: "List all firewall groups", - Aliases: []string{"l"}, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - list, meta, err := client.FirewallGroup.List(context.Background(), options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.FirewallGroups(list, meta) - }, -} diff --git a/cmd/firewallRule.go b/cmd/firewallRule.go deleted file mode 100644 index ca9f1d52..00000000 --- a/cmd/firewallRule.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - "strconv" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// FirewallRule represents the firewall rule commands -func FirewallRule() *cobra.Command { - firewallRuleCmd := &cobra.Command{ - Use: "rule", - Short: "rule is used to access firewall rule commands", - Aliases: []string{"r"}, - } - - firewallRuleCmd.AddCommand(firewallRuleCreate, firewallRuleDelete, firewallRuleGet, firewallRuleList) - - firewallRuleCreate.Flags().StringP("id", "i", "", "ID of the target firewall group.") - firewallRuleCreate.Flags().StringP("protocol", "p", "", "Protocol type. Possible values: 'icmp', 'tcp', 'udp', 'gre'.") - firewallRuleCreate.Flags().StringP("subnet", "s", "", "The IPv4 network in CIDR notation.") - firewallRuleCreate.Flags().IntP("size", "z", 0, "The number of bits for the netmask in CIDR notation.") - firewallRuleCreate.Flags().IntP("source", "o", 0, "(optional) When empty, uses value from subnet and size. If \"cloudflare\", allows all Cloudflare IP space through firewall.") - firewallRuleCreate.Flags().StringP("type", "t", "", "The type of IP rule - v4 or v6.") - - firewallRuleCreate.Flags().StringP("port", "r", "", "(optional) TCP/UDP only. This field can be an integer value specifying a port or a colon separated port range.") - firewallRuleCreate.Flags().StringP("notes", "n", "", "(optional) This field supports notes up to 255 characters.") - - firewallRuleCreate.MarkFlagRequired("id") - firewallRuleCreate.MarkFlagRequired("protocol") - firewallRuleCreate.MarkFlagRequired("subnet") - firewallRuleCreate.MarkFlagRequired("size") - firewallRuleCreate.MarkFlagRequired("type") - - firewallRuleList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - firewallRuleList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return firewallRuleCmd -} - -var firewallRuleCreate = &cobra.Command{ - Use: "create", - Short: "create a firewall rule", - Aliases: []string{"c"}, - Run: func(cmd *cobra.Command, args []string) { - id, _ := cmd.Flags().GetString("id") - protocol, _ := cmd.Flags().GetString("protocol") - subnet, _ := cmd.Flags().GetString("subnet") - ipType, _ := cmd.Flags().GetString("type") - size, _ := cmd.Flags().GetInt("size") - source, _ := cmd.Flags().GetString("source") - port, _ := cmd.Flags().GetString("port") - notes, _ := cmd.Flags().GetString("notes") - - options := &govultr.FirewallRuleReq{ - Protocol: protocol, - IPType: ipType, - Subnet: subnet, - SubnetSize: size, - Notes: notes, - } - - if port != "" { - options.Port = port - } - - if source != "" { - options.Source = source - } - - fwr, err := client.FirewallRule.Create(context.Background(), id, options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.FirewallRule(fwr) - }, -} - -var firewallRuleDelete = &cobra.Command{ - Use: "delete ", - Short: "Delete a firewall rule", - Aliases: []string{"d", "destroy"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a firewallGroupID and firewallRuleNumber") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - rule, _ := strconv.Atoi(args[1]) - if err := client.FirewallRule.Delete(context.Background(), args[0], rule); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - fmt.Println("Firewall rule has been deleted") - }, -} - -var firewallRuleGet = &cobra.Command{ - Use: "get ", - Short: "Get firewall rule", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a firewallGroupID and firewallRuleNumber") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - ruleNumber, _ := strconv.Atoi(args[1]) - fwRule, err := client.FirewallRule.Get(context.Background(), args[0], ruleNumber) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.FirewallRule(fwRule) - }, -} - -var firewallRuleList = &cobra.Command{ - Use: "list ", - Short: "List all firewall rules", - Aliases: []string{"l"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a firewallGroupID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - list, meta, err := client.FirewallRule.List(context.Background(), args[0], options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.FirewallRules(list, meta) - }, -} diff --git a/cmd/instance.go b/cmd/instance.go deleted file mode 100644 index b82a5494..00000000 --- a/cmd/instance.go +++ /dev/null @@ -1,1153 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// Instance represents the instance command -func Instance() *cobra.Command { - instanceCmd := &cobra.Command{ - Use: "instance", - Short: "commands to interact with instances on vultr", - Long: ``, - } - - instanceCmd.AddCommand(instanceStart, instanceStop, instanceRestart, instanceReinstall, instanceTag, instanceDelete, instanceLabel, instanceBandwidth, instanceList, instanceInfo, updateFwgGroup, instanceRestore, instanceCreate) - - instanceTag.Flags().StringP("tag", "t", "", "tag you want to set for a given instance") - instanceTag.MarkFlagRequired("tag") - - instanceLabel.Flags().StringP("label", "l", "", "label you want to set for a given instance") - instanceLabel.MarkFlagRequired("label") - - updateFwgGroup.Flags().StringP("instance-id", "i", "", "instance id of the instance you want to use") - updateFwgGroup.Flags().StringP("firewall-group-id", "f", "", "firewall group id that you want to assign. 0 Value will unset the firewall-group") - updateFwgGroup.MarkFlagRequired("instance-id") - updateFwgGroup.MarkFlagRequired("firewall-group-id") - - instanceRestore.Flags().StringP("backup", "b", "", "id of backup you wish to restore the instance with") - instanceRestore.Flags().StringP("snapshot", "s", "", "id of snapshot you wish to restore the instance with") - - instanceCreate.Flags().StringP("region", "r", "", "region id you wish to have the instance created in") - instanceCreate.Flags().StringP("plan", "p", "", "plan id you wish the instance to have") - instanceCreate.Flags().IntP("operatingSystems", "o", 0, "operatingSystems id you wish the instance to have") - instanceCreate.MarkFlagRequired("region") - instanceCreate.MarkFlagRequired("plan") - - // Optional Params - instanceCreate.Flags().StringP("ipxe", "", "", "if you've selected the 'custom' operating system, this can be set to chainload the specified URL on bootup") - instanceCreate.Flags().StringP("iso", "", "", "iso ID you want to create the instance with") - instanceCreate.Flags().StringP("snapshot", "", "", "snapshot ID you want to create the instance with") - instanceCreate.Flags().StringP("script-id", "", "", "script id of the startup script") - instanceCreate.Flags().BoolP("ipv6", "", false, "enable ipv6 | true or false") - instanceCreate.Flags().BoolP("private-network", "", false, "enable private network | true or false") - instanceCreate.Flags().StringArrayP("network", "", []string{}, "network IDs you want to assign to the instance") - instanceCreate.Flags().StringP("label", "l", "", "label you want to give this instance") - instanceCreate.Flags().StringArrayP("ssh-keys", "s", []string{}, "ssh keys you want to assign to the instance") - instanceCreate.Flags().BoolP("auto-backup", "b", false, "enable auto backups | true or false") - instanceCreate.Flags().IntP("app", "a", 0, "application ID you want this instance to have") - instanceCreate.Flags().StringP("userdata", "u", "", "plain text userdata you want to give this instance which the CLI will base64 encode") - instanceCreate.Flags().BoolP("notify", "n", true, "notify when instance has been created | true or false") - instanceCreate.Flags().BoolP("ddos", "d", false, "enable ddos protection | true or false") - instanceCreate.Flags().StringP("reserved-ipv4", "", "", "ip address of the floating IP to use as the main IP for this instance") - instanceCreate.Flags().StringP("host", "", "", "The hostname to assign to this instance") - instanceCreate.Flags().StringP("tag", "t", "", "The tag to assign to this instance") - instanceCreate.Flags().StringP("firewall-group", "", "", "The firewall group to assign to this instance") - - instanceList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - instanceList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - instanceIPV4List.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - instanceIPV4List.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - instanceIPV6List.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - instanceIPV6List.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - // Sub commands for OS - osCmd := &cobra.Command{ - Use: "operatingSystems", - Short: "update operating system for an instance", - Long: ``, - } - - osCmd.AddCommand(osUpdate, osUpdateList) - osUpdate.Flags().IntP("operatingSystems", "o", 0, "operating system ID you wish to use") - osUpdate.MarkFlagRequired("operatingSystems") - instanceCmd.AddCommand(osCmd) - - // Sub commands for App - appCMD := &cobra.Command{ - Use: "app", - Short: "update application for an instance", - Long: ``, - } - appCMD.AddCommand(appUpdate, appUpdateList) - appUpdate.Flags().IntP("app", "a", 0, "application ID you wish to use") - appUpdate.MarkFlagRequired("app") - instanceCmd.AddCommand(appCMD) - - // Sub commands for Backup - backupCMD := &cobra.Command{ - Use: "backup", - Short: "list and create backup schedules for an instance", - Long: ``, - } - backupCMD.AddCommand(backupGet, backupCreate) - backupCreate.Flags().StringP("type", "t", "", "type string Backup cron type. Can be one of 'daily', 'weekly', 'monthly', 'daily_alt_even', or 'daily_alt_odd'.") - backupCreate.MarkFlagRequired("type") - backupCreate.Flags().IntP("hour", "o", 0, "Hour value (0-23). Applicable to crons: 'daily', 'weekly', 'monthly', 'daily_alt_even', 'daily_alt_odd'") - backupCreate.Flags().IntP("dow", "w", 0, "Day-of-week value (0-6). Applicable to crons: 'weekly'") - backupCreate.Flags().IntP("dom", "m", 0, "Day-of-month value (1-28). Applicable to crons: 'monthly'") - instanceCmd.AddCommand(backupCMD) - - // IPV4 Subcommands - isoCmd := &cobra.Command{ - Use: "iso", - Short: "attach/detach ISOs to a given instance", - Long: ``, - } - isoCmd.AddCommand(isoStatus, isoAttach, isoDetach) - isoAttach.Flags().StringP("iso-id", "i", "", "id of the ISO you wish to attach") - isoAttach.MarkFlagRequired("iso-id") - instanceCmd.AddCommand(isoCmd) - - ipv4Cmd := &cobra.Command{ - Use: "ipv4", - Short: "list/create/delete ipv4 on instance", - Long: ``, - } - ipv4Cmd.AddCommand(instanceIPV4List, createIpv4, deleteIpv4) - createIpv4.Flags().Bool("reboot", false, "whether to reboot instance after adding ipv4 address") - deleteIpv4.Flags().StringP("ipv4", "i", "", "ipv4 address you wish to delete") - deleteIpv4.MarkFlagRequired("ipv4") - instanceCmd.AddCommand(ipv4Cmd) - - // IPV6 Subcommands - ipv6Cmd := &cobra.Command{ - Use: "ipv6", - Short: "commands for ipv6 on instance", - Long: ``, - } - ipv6Cmd.AddCommand(instanceIPV6List) - instanceCmd.AddCommand(ipv6Cmd) - - // Plans SubCommands - plansCmd := &cobra.Command{ - Use: "plan", - Short: "update/list plans for an instance", - Long: ``, - } - plansCmd.AddCommand(upgradePlan, upgradePlanList) - upgradePlan.Flags().StringP("plan", "p", "", "plan id that you wish to upgrade to") - upgradePlan.MarkFlagRequired("plan") - instanceCmd.AddCommand(plansCmd) - - // ReverseDNS SubCommands - reverseCmd := &cobra.Command{ - Use: "reverse-dns", - Short: "commands to handle reverse-dns on an instance", - Long: ``, - } - reverseCmd.AddCommand(defaultIpv4, listIpv6, deleteIpv6, setIpv4, setIpv6) - defaultIpv4.Flags().StringP("ip", "i", "", "iPv4 address used in the reverse DNS update") - defaultIpv4.MarkFlagRequired("ip") - deleteIpv6.Flags().StringP("ip", "i", "", "ipv6 address you wish to delete") - - defaultIpv4.MarkFlagRequired("ip") - setIpv4.Flags().StringP("ip", "i", "", "ip address you wish to set a reverse DNS on") - setIpv4.Flags().StringP("entry", "e", "", "reverse dns entry") - setIpv4.MarkFlagRequired("ip") - setIpv4.MarkFlagRequired("entry") - - setIpv6.Flags().StringP("ip", "i", "", "ip address you wish to set a reverse DNS on") - setIpv6.Flags().StringP("entry", "e", "", "reverse dns entry") - setIpv6.MarkFlagRequired("ip") - setIpv6.MarkFlagRequired("entry") - instanceCmd.AddCommand(reverseCmd) - - userdataCmd := &cobra.Command{ - Use: "user-data", - Short: "commands to handle userdata on an instance", - Long: ``, - } - userdataCmd.AddCommand(setUserData, getUserData) - setUserData.Flags().StringP("userdata", "d", "/dev/stdin", "file to read userdata from") - instanceCmd.AddCommand(userdataCmd) - - return instanceCmd -} - -var instanceStart = &cobra.Command{ - Use: "start ", - Short: "starts an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.Instance.Start(context.Background(), id); err != nil { - fmt.Printf("error starting instance : %v\n", err) - os.Exit(1) - } - - fmt.Println("Started up instance") - }, -} - -var instanceStop = &cobra.Command{ - Use: "stop ", - Short: "stops an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.Instance.Halt(context.Background(), id); err != nil { - fmt.Printf("error stopping instance : %v\n", err) - os.Exit(1) - } - - fmt.Println("Stopped the instance") - }, -} - -var instanceRestart = &cobra.Command{ - Use: "restart ", - Short: "restart an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.Instance.Reboot(context.Background(), id); err != nil { - fmt.Printf("error rebooting instance : %v\n", err) - os.Exit(1) - } - - fmt.Println("Rebooted instance") - }, -} - -var instanceReinstall = &cobra.Command{ - Use: "reinstall ", - Short: "reinstall an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.Instance.Reinstall(context.Background(), id); err != nil { - fmt.Printf("error reinstalling instance : %v\n", err) - os.Exit(1) - } - - fmt.Println("Reinstalled instance") - }, -} - -var instanceTag = &cobra.Command{ - Use: "tag ", - Short: "add/modify tag on instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - tag, _ := cmd.Flags().GetString("tag") - options := &govultr.InstanceUpdateReq{ - Tag: tag, - } - - if err := client.Instance.Update(context.Background(), id, options); err != nil { - fmt.Printf("error adding tag to instance : %v\n", err) - os.Exit(1) - } - - fmt.Printf("Tagged instance with : %s\n", tag) - }, -} - -var instanceDelete = &cobra.Command{ - Use: "delete ", - Short: "delete/destroy an instance", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.Instance.Delete(context.Background(), id); err != nil { - fmt.Printf("error deleting instance : %v\n", err) - os.Exit(1) - } - - fmt.Println("Deleted instance") - }, -} - -var instanceLabel = &cobra.Command{ - Use: "label ", - Short: "label an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - label, _ := cmd.Flags().GetString("label") - options := &govultr.InstanceUpdateReq{ - Label: label, - } - - if err := client.Instance.Update(context.Background(), id, options); err != nil { - fmt.Printf("error labeling instance : %v\n", err) - os.Exit(1) - } - - fmt.Printf("Labeled instance with : %s\n", label) - }, -} - -var instanceBandwidth = &cobra.Command{ - Use: "bandwidth ", - Short: "bandwidth for instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - bw, err := client.Instance.GetBandwidth(context.Background(), id) - if err != nil { - fmt.Printf("error getting bandwidth for instance : %v\n", err) - os.Exit(1) - } - - printer.InstanceBandwidth(bw) - }, -} - -var instanceIPV4List = &cobra.Command{ - Use: "list ", - Aliases: []string{"v4"}, - Short: "list ipv4 for an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - options := getPaging(cmd) - v4, meta, err := client.Instance.ListIPv4(context.Background(), id, options) - if err != nil { - fmt.Printf("error getting ipv4 info : %v\n", err) - os.Exit(1) - } - - printer.InstanceIPV4(v4, meta) - }, -} - -var instanceIPV6List = &cobra.Command{ - Use: "list ", - Aliases: []string{"v6"}, - Short: "list ipv6 for an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - options := getPaging(cmd) - v6, meta, err := client.Instance.ListIPv6(context.TODO(), id, options) - if err != nil { - fmt.Printf("error getting ipv6 info : %v\n", err) - os.Exit(1) - } - - printer.InstanceIPV6(v6, meta) - }, -} - -var instanceList = &cobra.Command{ - Use: "list", - Aliases: []string{"l"}, - Short: "list all available instances", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - s, meta, err := client.Instance.List(context.TODO(), options) - if err != nil { - fmt.Printf("error getting list of instances : %v\n", err) - os.Exit(1) - } - - printer.InstanceList(s, meta) - }, -} - -var instanceInfo = &cobra.Command{ - Use: "get ", - Short: "get info about a specific instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - s, err := client.Instance.Get(context.TODO(), id) - if err != nil { - fmt.Printf("error getting instance : %v\n", err) - os.Exit(1) - } - - printer.Instance(s) - }, -} - -var updateFwgGroup = &cobra.Command{ - Use: "update-firewall-group", - Short: "assign a firewall group to instance", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - id, _ := cmd.Flags().GetString("instance-id") - fwgID, _ := cmd.Flags().GetString("firewall-group-id") - - options := &govultr.InstanceUpdateReq{ - FirewallGroupID: fwgID, - } - - if err := client.Instance.Update(context.TODO(), id, options); err != nil { - fmt.Printf("error setting firewall group : %v\n", err) - os.Exit(1) - } - - fmt.Println("Updated firewall group") - }, -} - -var osUpdate = &cobra.Command{ - Use: "change ", - Short: "changes operating system", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - osID, _ := cmd.Flags().GetInt("operatingSystems") - - options := &govultr.InstanceUpdateReq{ - OsID: osID, - } - - if err := client.Instance.Update(context.TODO(), id, options); err != nil { - fmt.Printf("error updating operatingSystems : %v\n", err) - os.Exit(1) - } - - fmt.Println("Updated OS") - }, -} - -var osUpdateList = &cobra.Command{ - Use: "list ", - Short: "available operating systems an instance can change to.", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - list, err := client.Instance.GetUpgrades(context.TODO(), id) - - if err != nil { - fmt.Printf("error listing available operatingSystems : %v\n", err) - os.Exit(1) - } - - printer.OsList(list.OS) - }, -} - -var appUpdate = &cobra.Command{ - Use: "change ", - Short: "changes application", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - appID, _ := cmd.Flags().GetInt("app") - - options := &govultr.InstanceUpdateReq{ - AppID: appID, - } - - if err := client.Instance.Update(context.TODO(), id, options); err != nil { - fmt.Printf("error updating application : %v\n", err) - os.Exit(1) - } - - fmt.Println("Updated Application") - }, -} - -var appUpdateList = &cobra.Command{ - Use: "list ", - Short: "available apps an instance can change to.", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - list, err := client.Instance.GetUpgrades(context.TODO(), id) - - if err != nil { - fmt.Printf("error listing available applications : %v\n", err) - os.Exit(1) - } - - printer.AppList(list.Applications) - }, -} - -var backupGet = &cobra.Command{ - Use: "get ", - Short: "get backup schedules on a given instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - info, err := client.Instance.GetBackupSchedule(context.TODO(), id) - if err != nil { - fmt.Printf("error getting application info : %v\n", err) - os.Exit(1) - } - - printer.BackupsGet(info) - }, -} - -var backupCreate = &cobra.Command{ - Use: "create ", - Short: "create backup schedule on a given instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - - crontType, _ := cmd.Flags().GetString("type") - hour, _ := cmd.Flags().GetInt("hour") - dow, _ := cmd.Flags().GetInt("dow") - dom, _ := cmd.Flags().GetInt("dom") - - backup := &govultr.BackupScheduleReq{ - Type: crontType, - Hour: hour, - Dow: dow, - Dom: dom, - } - - if err := client.Instance.SetBackupSchedule(context.TODO(), id, backup); err != nil { - fmt.Printf("error creating backup schedule : %v\n", err) - os.Exit(1) - } - - fmt.Println("Created backup schedule") - }, -} - -var isoStatus = &cobra.Command{ - Use: "status ", - Short: "current ISO state", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - info, err := client.Instance.ISOStatus(context.TODO(), id) - if err != nil { - fmt.Printf("error getting iso state info : %v\n", err) - os.Exit(1) - } - - printer.IsoStatus(info) - }, -} - -var isoAttach = &cobra.Command{ - Use: "attach ", - Short: "attach ISO to instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - iso, _ := cmd.Flags().GetString("iso-id") - if err := client.Instance.AttachISO(context.TODO(), id, iso); err != nil { - fmt.Printf("error attaching iso : %v\n", err) - os.Exit(1) - } - - fmt.Println("ISO has been attached") - }, -} - -var isoDetach = &cobra.Command{ - Use: "detach ", - Short: "detach ISO from instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.Instance.DetachISO(context.TODO(), id); err != nil { - fmt.Printf("error detaching iso : %v\n", err) - os.Exit(1) - } - - fmt.Println("ISO has been detached") - }, -} - -var instanceRestore = &cobra.Command{ - Use: "restore ", - Short: "restore instance from backup/snapshot", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - - backup, _ := cmd.Flags().GetString("backup") - snapshot, _ := cmd.Flags().GetString("snapshot") - options := &govultr.RestoreReq{} - - if backup == "" && snapshot == "" { - fmt.Println("at least one flag must be provided (snapshot or backup)") - os.Exit(1) - } else if backup != "" && snapshot != "" { - fmt.Println("one flag must be provided not both (snapshot or backup)") - os.Exit(1) - } - - if snapshot != "" { - options.SnapshotID = snapshot - } else { - options.BackupID = backup - } - - if err := client.Instance.Restore(context.TODO(), id, options); err != nil { - fmt.Printf("error restoring instance : %v\n", err) - os.Exit(1) - } - - fmt.Println("Instance has been restored") - }, -} - -var createIpv4 = &cobra.Command{ - Use: "create ", - Short: "create ipv4 for instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - reboot, _ := cmd.Flags().GetBool("reboot") - - _, err := client.Instance.CreateIPv4(context.TODO(), id, govultr.BoolToBoolPtr(reboot)) - if err != nil { - fmt.Printf("error creating ipv4 : %v\n", err) - os.Exit(1) - } - - fmt.Println("IPV4 has been created") - }, -} - -var deleteIpv4 = &cobra.Command{ - Use: "delete ", - Short: "delete ipv4 for instance", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - ip, _ := cmd.Flags().GetString("ipv4") - - if err := client.Instance.DeleteIPv4(context.TODO(), id, ip); err != nil { - fmt.Printf("error deleting ipv4 : %v\n", err) - os.Exit(1) - } - - fmt.Println("IPV4 has been deleted") - }, -} - -var upgradePlan = &cobra.Command{ - Use: "upgrade ", - Short: "upgrade plan for instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - plan, _ := cmd.Flags().GetString("plan") - - options := &govultr.InstanceUpdateReq{ - Plan: plan, - } - - if err := client.Instance.Update(context.TODO(), id, options); err != nil { - fmt.Printf("error upgrading plans : %v\n", err) - os.Exit(1) - } - - fmt.Println("Upgraded plan") - }, -} - -var upgradePlanList = &cobra.Command{ - Use: "list ", - Short: "available plans an instance can upgrade to.", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - list, err := client.Instance.GetUpgrades(context.TODO(), id) - - if err != nil { - fmt.Printf("error listing available plans : %v\n", err) - os.Exit(1) - } - - printer.PlansList(list.Plans) - }, -} - -var defaultIpv4 = &cobra.Command{ - Use: "default-ipv4 ", - Short: "Set a reverse DNS entry for an IPv4 address of an instance to the original setting", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - ip, _ := cmd.Flags().GetString("ip") - - if err := client.Instance.DefaultReverseIPv4(context.TODO(), id, ip); err != nil { - fmt.Printf("error setting default reverse dns : %v\n", err) - os.Exit(1) - } - - fmt.Println("Set default reserve dns") - }, -} - -var listIpv6 = &cobra.Command{ - Use: "list-ipv6 ", - Short: "List the IPv6 reverse DNS entries for an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - rip, err := client.Instance.ListReverseIPv6(context.TODO(), id) - if err != nil { - fmt.Printf("error getting the reverse ipv6 list: %v\n", err) - os.Exit(1) - } - printer.ReverseIpv6(rip) - }, -} - -var deleteIpv6 = &cobra.Command{ - Use: "delete-ipv6 ", - Short: "Remove a reverse DNS entry for an IPv6 address for an instance", - Aliases: []string{"destroy-ipv6"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - ip, _ := cmd.Flags().GetString("ip") - if err := client.Instance.DeleteReverseIPv6(context.TODO(), id, ip); err != nil { - fmt.Printf("error deleting reverse ipv6 entry : %v\n", err) - os.Exit(1) - } - - fmt.Println("Deleted reverse DNS IPV6 entry") - }, -} - -var setIpv4 = &cobra.Command{ - Use: "set-ipv4 ", - Short: "Set a reverse DNS entry for an IPv4 address for an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - ip, _ := cmd.Flags().GetString("ip") - entry, _ := cmd.Flags().GetString("entry") - - options := &govultr.ReverseIP{ - IP: ip, - Reverse: entry, - } - - if err := client.Instance.CreateReverseIPv4(context.TODO(), id, options); err != nil { - fmt.Printf("error setting reverse dns ipv4 entry : %v\n", err) - os.Exit(1) - } - - fmt.Println("Set reverse DNS entry for ipv4 address") - }, -} - -var setIpv6 = &cobra.Command{ - Use: "set-ipv6 ", - Short: "Set a reverse DNS entry for an IPv6 address for an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - ip, _ := cmd.Flags().GetString("ip") - entry, _ := cmd.Flags().GetString("entry") - - options := &govultr.ReverseIP{ - IP: ip, - Reverse: entry, - } - - if err := client.Instance.CreateReverseIPv6(context.TODO(), id, options); err != nil { - fmt.Printf("error setting reverse dns ipv6 entry : %v\n", err) - os.Exit(1) - } - fmt.Println("Set reverse DNS entry for ipv6 address") - }, -} - -var instanceCreate = &cobra.Command{ - Use: "create", - Short: "Create an instance", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - region, _ := cmd.Flags().GetString("region") - plan, _ := cmd.Flags().GetString("plan") - osID, _ := cmd.Flags().GetInt("operatingSystems") - - // Optional - ipxe, _ := cmd.Flags().GetString("ipxe") - iso, _ := cmd.Flags().GetString("iso") - snapshot, _ := cmd.Flags().GetString("snapshot") - script, _ := cmd.Flags().GetString("script-id") - ipv6, _ := cmd.Flags().GetBool("ipv6") - privateNetwork, _ := cmd.Flags().GetBool("private-network") - networks, _ := cmd.Flags().GetStringArray("network") - label, _ := cmd.Flags().GetString("label") - ssh, _ := cmd.Flags().GetStringArray("ssh-keys") - backup, _ := cmd.Flags().GetBool("auto-backup") - app, _ := cmd.Flags().GetInt("app") - userData, _ := cmd.Flags().GetString("userdata") - notify, _ := cmd.Flags().GetBool("notify") - ddos, _ := cmd.Flags().GetBool("ddos") - ipv4, _ := cmd.Flags().GetString("reserved-ipv4") - host, _ := cmd.Flags().GetString("host") - tag, _ := cmd.Flags().GetString("tag") - fwg, _ := cmd.Flags().GetString("firewall-group") - - osOptions := map[string]interface{}{"iso_id": iso, "os_id": osID, "app_id": app, "snapshot_id": snapshot} - - if iso != "" { - osOptions["iso_id"] = iso - } - - osOption, err := optionCheck(osOptions) - if err != nil { - fmt.Printf("error creating instance : %v\n", err) - os.Exit(1) - } - - opt := &govultr.InstanceCreateReq{ - Plan: plan, - Region: region, - IPXEChainURL: ipxe, - ISOID: iso, - SnapshotID: snapshot, - ScriptID: script, - AttachPrivateNetwork: networks, - Label: label, - SSHKeys: ssh, - AppID: app, - UserData: userData, - ReservedIPv4: ipv4, - Hostname: host, - Tag: tag, - FirewallGroupID: fwg, - EnableIPv6: govultr.BoolToBoolPtr(false), - DDOSProtection: govultr.BoolToBoolPtr(false), - ActivationEmail: govultr.BoolToBoolPtr(false), - Backups: "disabled", - EnablePrivateNetwork: govultr.BoolToBoolPtr(false), - } - - // If no osOptions were selected and osID has a real value then set the osOptions to os_id - if osOption == "os_id" && osID != 0 { - opt.OsID = osID - } else if osOption == "" && osID == 0 { - fmt.Printf("error creating instance: an os_id, snapshot_id, iso_id, or app_id must be provided\n") - os.Exit(1) - } - - if ipv6 { - opt.EnableIPv6 = govultr.BoolToBoolPtr(true) - } - if ddos { - opt.DDOSProtection = govultr.BoolToBoolPtr(true) - } - if notify { - opt.ActivationEmail = govultr.BoolToBoolPtr(true) - } - if backup { - opt.Backups = "enabled" - } - if privateNetwork { - opt.EnablePrivateNetwork = govultr.BoolToBoolPtr(true) - } - - if userData != "" { - opt.UserData = base64.StdEncoding.EncodeToString([]byte(userData)) - } - - //region, plan, osOpt, opt - instance, err := client.Instance.Create(context.TODO(), opt) - if err != nil { - fmt.Printf("error creating instance : %v\n", err) - os.Exit(1) - } - - printer.Instance(instance) - }, -} - -var setUserData = &cobra.Command{ - Use: "set ", - Short: "Set the plain text user-data of an instance", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - userData, _ := cmd.Flags().GetString("userdata") - - rawData, err := ioutil.ReadFile(userData) - if err != nil { - fmt.Printf("error reading user-data : %v\n", err) - os.Exit(1) - } - - options := &govultr.InstanceUpdateReq{ - UserData: base64.StdEncoding.EncodeToString(rawData), - } - - if err = client.Instance.Update(context.TODO(), args[0], options); err != nil { - fmt.Printf("error setting user-data : %v\n", err) - os.Exit(1) - } - - fmt.Println("Set user-data for instance") - }, -} - -var getUserData = &cobra.Command{ - Use: "get ", - Short: "Get the user-data of an instance", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an instanceID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - userData, err := client.Instance.GetUserData(context.TODO(), args[0]) - if err != nil { - fmt.Printf("error getting user-data : %v\n", err) - os.Exit(1) - } - - printer.UserData(userData) - }, -} - -func optionCheck(options map[string]interface{}) (string, error) { - var result []string - for k, v := range options { - switch v.(type) { - case int: - if v != 0 { - result = append(result, k) - } - case string: - if v != "" { - result = append(result, k) - } - } - } - - if len(result) > 1 { - return "", fmt.Errorf("too many options have been selected : %v : please select one", result) - } - - // Return back an empty slice so we can possibly add in osID - if len(result) == 0 { - return "", nil - } - - return result[0], nil -} diff --git a/cmd/instance/instance.go b/cmd/instance/instance.go new file mode 100644 index 00000000..e06fff9f --- /dev/null +++ b/cmd/instance/instance.go @@ -0,0 +1,1777 @@ +// Package instance provides the command for the CLI to control instances +package instance + +import ( + "encoding/base64" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/ip" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/userdata" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Get commands available to instance` + example = ` + # Full example + vultr-cli instance + ` + listLong = `` + listExample = `` + getLong = `` + getExample = `` + createLong = `Create a new instance with specified plan, region and os (from image, snapshot, app or ISO)` + createExample = ` + # Full example + vultr-cli instance create --region="ewr" --plan="vc2-2c-4gb" --os=1743 + + You must pass one of these in addition to the required --region and --plan flags: + --os + --snapshot + --iso + --app + --image + + # Shortened example with aliases + vultr-cli instance c -r="ewr" -p="vc2-2c-4gb" -o=1743 + + # Full example with attached VPCs + vultr-cli instance create --region="ewr" --plan="vc2-2c-4gb" --os=1743 \ + --vpc-ids="08422775-5be0-4371-afba-64b03f9ad22d,13a45caa-9c06-4b5d-8f76-f5281ab172b7" + + # Full example with assigned ssh keys + vultr-cli instance create --region="ewr" --plan="vc2-2c-4gb" --os=1743 \ + --ssh-keys="a14b6539-5583-41e8-a035-c07a76897f2b,be624232-56c7-4d5c-bf87-9bdaae7a1fbd" + ` + deleteLong = `` + deleteExample = `` + tagsLong = `Modify the tags of the specified instance` + tagsExample = ` + # Full example + vultr-cli instance tags --tags="example-tag-1,example-tag-2" + + # Shortened example with aliases + vultr-cli instance tags -t="example-tag-1,example-tag-2" + ` + + userDataSetLong = `` + userDataSetExample = `` + userDataGetLong = `` + userDataGetExample = `` + vpcAttachLong = `Attaches an existing VPC to the specified instance` + vpcAttachExample = ` + # Full example + vultr-cli instance vpc attach --vpc-id="2126b7d9-5e2a-491e-8840-838aa6b5f294" + ` + vpcDetachLong = `Detaches an existing VPC from the specified instance` + vpcDetachExample = ` + # Full example + vultr-cli instance vpc detach --vpc-id="2126b7d9-5e2a-491e-8840-838aa6b5f294" + ` + + vpc2AttachLong = `Attaches an existing VPC 2.0 network to the specified instance` + vpc2AttachExample = ` + # Full example + vultr-cli instance vpc2 attach --vpc-id="2126b7d9-5e2a-491e-8840-838aa6b5f294" + ` + vpc2DetachLong = `Detaches an existing VPC 2.0 network from the specified instance` + vpc2DetachExample = ` + # Full example + vultr-cli instance vpc2 detach --vpc-id="2126b7d9-5e2a-491e-8840-838aa6b5f294" + ` +) + +// NewCmdInstance ... +func NewCmdInstance(base *cli.Base) *cobra.Command { //nolint:funlen,gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "instance", + Short: "commands to interact with instances on vultr", + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Aliases: []string{"l"}, + Short: "List all instances", + Long: listLong, + Example: listExample, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + instances, meta, err := o.list() + if err != nil { + return fmt.Errorf("error getting instance list : %v", err) + } + + data := &InstancesPrinter{Instances: instances, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "Get info on an instance", + Long: getLong, + Example: getExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + instance, err := o.get() + if err != nil { + return fmt.Errorf("error getting instance : %v", err) + } + + data := &InstancePrinter{Instance: instance} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "Create an instance", + Aliases: []string{"c"}, + Long: createLong, + Example: createExample, + RunE: func(cmd *cobra.Command, args []string) error { + region, errRe := cmd.Flags().GetString("region") + if errRe != nil { + return fmt.Errorf("error parsing flag 'region' for instance create : %v", errRe) + } + + plan, errPl := cmd.Flags().GetString("plan") + if errPl != nil { + return fmt.Errorf("error parsing flag 'plan' for instance create : %v", errPl) + } + + osID, errOs := cmd.Flags().GetInt("os") + if errOs != nil { + return fmt.Errorf("error parsing flag 'os' for instance create : %v", errOs) + } + + ipxe, errIP := cmd.Flags().GetString("ipxe") + if errIP != nil { + return fmt.Errorf("error parsing flag 'ipxe' for instance create : %v", errIP) + } + + iso, errIs := cmd.Flags().GetString("iso") + if errIs != nil { + return fmt.Errorf("error parsing flag 'iso' for instance create : %v", errIs) + } + + snapshot, errSn := cmd.Flags().GetString("snapshot") + if errSn != nil { + return fmt.Errorf("error parsing flag 'snapshot' for instance create : %v", errSn) + } + + script, errSc := cmd.Flags().GetString("script-id") + if errSc != nil { + return fmt.Errorf("error parsing flag 'script' for instance create : %v", errSc) + } + + ipv6, errIv := cmd.Flags().GetBool("ipv6") + if errIv != nil { + return fmt.Errorf("error parsing flag 'ipv6' for instance create : %v", errIv) + } + + vpcEnable, errVp := cmd.Flags().GetBool("vpc-enable") + if errVp != nil { + return fmt.Errorf("error parsing flag 'vpc-enable' for instance create : %v", errVp) + } + + vpcAttach, errVp := cmd.Flags().GetStringSlice("vpc-ids") + if errVp != nil { + return fmt.Errorf("error parsing flag 'vpc-ids' for instance create : %v", errVp) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for instance create : %v", errLa) + } + + ssh, errSs := cmd.Flags().GetStringSlice("ssh-keys") + if errSs != nil { + return fmt.Errorf("error parsing flag 'ssh-keys' for instance create : %v", errSs) + } + + backup, errBa := cmd.Flags().GetBool("auto-backup") + if errBa != nil { + return fmt.Errorf("error parsing flag 'auto-backup' for instance create : %v", errBa) + } + + app, errAp := cmd.Flags().GetInt("app") + if errAp != nil { + return fmt.Errorf("error parsing flag 'app' for instance create : %v", errAp) + } + + image, errIm := cmd.Flags().GetString("image") + if errIm != nil { + return fmt.Errorf("error parsing flag 'image' for instance create : %v", errIm) + } + + userData, errUs := cmd.Flags().GetString("userdata") + if errUs != nil { + return fmt.Errorf("error parsing flag 'userData' for instance create : %v", errUs) + } + + notify, errNo := cmd.Flags().GetBool("notify") + if errNo != nil { + return fmt.Errorf("error parsing flag 'notify' for instance create : %v", errNo) + } + + ddos, errDd := cmd.Flags().GetBool("ddos") + if errDd != nil { + return fmt.Errorf("error parsing flag 'ddos' for instance create : %v", errDd) + } + + ipv4, errIi := cmd.Flags().GetString("reserved-ipv4") + if errIi != nil { + return fmt.Errorf("error parsing flag 'reserved-ipv4' for instance create : %v", errIi) + } + + host, errHo := cmd.Flags().GetString("host") + if errHo != nil { + return fmt.Errorf("error parsing flag 'host' for instance create : %v", errHo) + } + + tags, errTa := cmd.Flags().GetStringSlice("tags") + if errTa != nil { + return fmt.Errorf("error parsing flag 'tags' for instance create : %v", errTa) + } + + fwg, errFw := cmd.Flags().GetString("firewall-group") + if errFw != nil { + return fmt.Errorf("error parsing flag 'firewall-group' for instance create : %v", errFw) + } + + o.CreateReq = &govultr.InstanceCreateReq{ + Plan: plan, + Region: region, + OsID: osID, + ISOID: iso, + SnapshotID: snapshot, + AppID: app, + ImageID: image, + IPXEChainURL: ipxe, + ScriptID: script, + Label: label, + SSHKeys: ssh, + UserData: userData, + ReservedIPv4: ipv4, + Hostname: host, + Tags: tags, + FirewallGroupID: fwg, + EnableIPv6: govultr.BoolToBoolPtr(ipv6), + DDOSProtection: govultr.BoolToBoolPtr(ddos), + ActivationEmail: govultr.BoolToBoolPtr(notify), + Backups: "disabled", + EnableVPC: govultr.BoolToBoolPtr(vpcEnable), + AttachVPC: vpcAttach, + } + + if backup { + o.CreateReq.Backups = "enabled" + } + + if userData != "" { + o.CreateReq.UserData = base64.StdEncoding.EncodeToString([]byte(userData)) + } + + instance, err := o.create() + if err != nil { + return fmt.Errorf("error creating instance : %v", err) + } + + data := &InstancePrinter{Instance: instance} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + create.Flags().StringP("region", "r", "", "The ID of the region in which to create the instance") + if err := create.MarkFlagRequired("region"); err != nil { + fmt.Printf("error marking instance create 'region' flag required: %v", err) + os.Exit(1) + } + + create.Flags().StringP("plan", "p", "", "The plan ID with which to create the instance") + if err := create.MarkFlagRequired("plan"); err != nil { + fmt.Printf("error marking instance create 'plan' flag required: %v", err) + os.Exit(1) + } + + create.Flags().IntP("os", "", 0, "os id you wish the instance to have") + create.Flags().StringP("iso", "", "", "iso ID you want to create the instance with") + create.Flags().StringP("snapshot", "", "", "snapshot ID you want to create the instance with") + create.Flags().IntP("app", "a", 0, "application ID you want this instance to have") + create.Flags().StringP("image", "", "", "image ID of the application that will be installed on the server.") + create.MarkFlagsMutuallyExclusive("os", "iso", "snapshot", "app", "image") + create.MarkFlagsOneRequired("os", "iso", "snapshot", "app", "image") + + create.Flags().StringP( + "ipxe", + "", + "", + "if you've selected the 'custom' operating system, this can be set to chainload the specified URL on bootup", + ) + create.Flags().StringP("script-id", "", "", "script id of the startup script") + create.Flags().BoolP("ipv6", "", false, "enable ipv6 | true or false") + create.Flags().BoolP("vpc-enable", "", false, "enable VPC | true or false") + create.Flags().StringSliceP("vpc-ids", "", []string{}, "VPC IDs you want to assign to the instance") + create.Flags().StringP("label", "l", "", "label you want to give this instance") + create.Flags().StringSliceP("ssh-keys", "s", []string{}, "ssh keys you want to assign to the instance") + create.Flags().BoolP("auto-backup", "b", false, "enable auto backups | true or false") + create.Flags().StringP("userdata", "u", "", "plain text userdata you want to give this instance which the CLI will base64 encode") + create.Flags().BoolP("notify", "n", false, "notify when instance has been created | true or false") + create.Flags().BoolP("ddos", "d", false, "enable ddos protection | true or false") + create.Flags().StringP("reserved-ipv4", "", "", "ID of the floating IP to use as the main IP for this instance") + create.Flags().StringP("host", "", "", "The hostname to assign to this instance") + create.Flags().StringSliceP("tags", "", []string{}, "A comma-separated list of tags to assign to this instance") + create.Flags().StringP("firewall-group", "", "", "The firewall group to assign to this instance") + + // Update + // update := &cobra.Command{} + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "Delete an instance", + Aliases: []string{"destroy"}, + Long: deleteLong, + Example: deleteExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.del(); err != nil { + return fmt.Errorf("error deleting instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("Instance has been deleted"), nil) + + return nil + }, + } + + // Label + label := &cobra.Command{ + Use: "label ", + Short: "Label an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for instance update : %v", errLa) + } + + o.UpdateReq = &govultr.InstanceUpdateReq{ + Label: label, + } + + instance, err := o.update() + if err != nil { + return fmt.Errorf("error updating instance label : %v", err) + } + + data := &InstancePrinter{Instance: instance} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + label.Flags().StringP("label", "l", "", "The label you want to set on an instance") + if err := label.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking instance label 'label' flag required: %v", err) + os.Exit(1) + } + + // Tags + tags := &cobra.Command{ + Use: "tags ", + Short: "Update tags on an instance", + Long: tagsLong, + Example: tagsExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + tags, errTa := cmd.Flags().GetStringSlice("tags") + if errTa != nil { + return fmt.Errorf("error parsing flag 'tags' for instance update : %v", errTa) + } + + o.UpdateReq = &govultr.InstanceUpdateReq{ + Tags: tags, + } + + instance, err := o.update() + if err != nil { + return fmt.Errorf("error updating instance tags : %v", err) + } + + data := &InstancePrinter{Instance: instance} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + tags.Flags().StringSliceP("tags", "t", []string{}, "A comma separated list of tags to apply to the instance") + if err := tags.MarkFlagRequired("tags"); err != nil { + fmt.Printf("error marking instance tags 'tags' flag required: %v", err) + os.Exit(1) + } + + // User Data + userData := &cobra.Command{ + Use: "user-data", + Short: "Commands to handle user data on an instance", + } + + // User Data Get + userDataGet := &cobra.Command{ + Use: "get ", + Short: "Get the user data on an instance", + Long: userDataGetLong, + Example: userDataGetExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + ud, err := o.userData() + if err != nil { + return fmt.Errorf("error getting instance user data : %v", err) + } + + data := &userdata.UserDataPrinter{UserData: *ud} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // User Data Set + userDataSet := &cobra.Command{ + Use: "set ", + Short: "Update user-data on an instance", + Long: userDataSetLong, + Example: userDataSetExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + userDataPath, errPa := cmd.Flags().GetString("userdata") + if errPa != nil { + return fmt.Errorf("error parsing flag 'userdata' for instance update : %v", errPa) + } + + userDataPath = filepath.Clean(userDataPath) + + rawData, errRe := os.ReadFile(userDataPath) + if errRe != nil { + return fmt.Errorf("error reading user-data : %v", errRe) + } + + o.UpdateReq = &govultr.InstanceUpdateReq{ + UserData: base64.StdEncoding.EncodeToString(rawData), + } + + _, err := o.update() + if err != nil { + return fmt.Errorf("error updating instance user data : %v", err) + } + + o.Base.Printer.Display(printer.Info("Instance user data has been updated"), nil) + + return nil + }, + } + + userDataSet.Flags().StringP("userdata", "d", "/dev/stdin", "The file to read userdata from") + + userData.AddCommand( + userDataGet, + userDataSet, + ) + + // Start + start := &cobra.Command{ + Use: "start ", + Short: "Start an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.start(); err != nil { + return fmt.Errorf("error starting instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("Instance started"), nil) + + return nil + }, + } + + // Stop + stop := &cobra.Command{ + Use: "stop ", + Short: "Stop an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.stop(); err != nil { + return fmt.Errorf("error stopping instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("Instance stopped"), nil) + + return nil + }, + } + + // Restart + restart := &cobra.Command{ + Use: "restart ", + Short: "Restart an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.restart(); err != nil { + return fmt.Errorf("error restarting instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("Instance restarted"), nil) + + return nil + }, + } + + // ISO + iso := &cobra.Command{ + Use: "iso", + Short: "Manage ISOs on an instance", + } + + // ISO Status + isoStatus := &cobra.Command{ + Use: "status ", + Short: "Get ISO status", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + iso, err := o.isoStatus() + if err != nil { + return fmt.Errorf("error getting instance iso status : %v", err) + } + + data := &ISOPrinter{ISO: *iso} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // ISO Attach + isoAttach := &cobra.Command{ + Use: "attach ", + Short: "Attach ISO to an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + iso, errIs := cmd.Flags().GetString("iso-id") + if errIs != nil { + return fmt.Errorf("error parsing flag 'iso' for instance iso attach: %v", errIs) + } + + o.ISOAttachID = iso + + if err := o.isoAttach(); err != nil { + return fmt.Errorf("error attaching iso to instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("ISO attached to instance"), nil) + + return nil + }, + } + + isoAttach.Flags().StringP("iso-id", "i", "", "id of the ISO you wish to attach") + if err := isoAttach.MarkFlagRequired("iso-id"); err != nil { + fmt.Printf("error marking instance iso attach 'iso-id' flag required: %v", err) + os.Exit(1) + } + + iso.AddCommand( + isoStatus, + isoAttach, + ) + + // Backup + backup := &cobra.Command{ + Use: "backup", + Short: "List and create backup schedules for an instance", + } + + // Backup Get + backupGet := &cobra.Command{ + Use: "get ", + Short: "Get the backup schedule for an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + bk, err := o.backups() + if err != nil { + return fmt.Errorf("error getting instance backups : %v", err) + } + + data := &BackupPrinter{Backup: *bk} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Backup Create + backupCreate := &cobra.Command{ + Use: "create ", + Short: "Create a backup schedule for an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + crontType, errCr := cmd.Flags().GetString("type") + if errCr != nil { + return fmt.Errorf("error parsing flag 'crontType' for instance backup create : %v", errCr) + } + + hour, errHo := cmd.Flags().GetInt("hour") + if errHo != nil { + return fmt.Errorf("error parsing flag 'hour' for instance backup create : %v", errHo) + } + + dow, errDo := cmd.Flags().GetInt("dow") + if errDo != nil { + return fmt.Errorf("error parsing flag 'dow' for instance backup create : %v", errDo) + } + + dom, errDo := cmd.Flags().GetInt("dom") + if errDo != nil { + return fmt.Errorf("error parsing flag 'dom' for instance backup create : %v", errDo) + } + + o.BackupCreateReq = &govultr.BackupScheduleReq{ + Type: crontType, + Hour: govultr.IntToIntPtr(hour), + Dow: govultr.IntToIntPtr(dow), + Dom: dom, + } + + if err := o.backupCreate(); err != nil { + return fmt.Errorf("error getting instance backups : %v", err) + } + + o.Base.Printer.Display(printer.Info("Instance backup created"), nil) + + return nil + }, + } + + backupCreate.Flags().StringP( + "type", + "t", + "", + "type string Backup cron type. Can be one of 'daily', 'weekly', 'monthly', 'daily_alt_even', or 'daily_alt_odd'.", + ) + if err := backupCreate.MarkFlagRequired("type"); err != nil { + fmt.Printf("error marking instance backup create 'type' flag required: %v", err) + os.Exit(1) + } + backupCreate.Flags().IntP( + "hour", + "", + 0, + "Hour value (0-23). Applicable to crons: 'daily', 'weekly', 'monthly', 'daily_alt_even', 'daily_alt_odd'", + ) + backupCreate.Flags().IntP("dow", "w", 0, "Day-of-week value (0-6). Applicable to crons: 'weekly'") + backupCreate.Flags().IntP("dom", "m", 0, "Day-of-month value (1-28). Applicable to crons: 'monthly'") + + backup.AddCommand( + backupGet, + backupCreate, + ) + + // Restore + restore := &cobra.Command{ + Use: "restore ", + Short: "Restore instance from backup or snapshot", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + backup, errBa := cmd.Flags().GetString("backup") + if errBa != nil { + return fmt.Errorf("error parsing flag 'backup' for instance restore : %v", errBa) + } + + snapshot, errSn := cmd.Flags().GetString("snapshot") + if errSn != nil { + return fmt.Errorf("error parsing flag 'snapshot' for instance restore : %v", errSn) + } + + o.RestoreReq = &govultr.RestoreReq{ + SnapshotID: snapshot, + BackupID: backup, + } + + if err := o.restore(); err != nil { + return fmt.Errorf("error restoring instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("Instance restored"), nil) + + return nil + }, + } + + restore.Flags().StringP("backup", "b", "", "id of backup you wish to restore the instance with") + restore.Flags().StringP("snapshot", "s", "", "id of snapshot you wish to restore the instance with") + restore.MarkFlagsOneRequired("backup", "snapshot") + restore.MarkFlagsMutuallyExclusive("backup", "snapshot") + + // Reinstall + reinstall := &cobra.Command{ + Use: "reinstall ", + Short: "reinstall an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + hostname, errHo := cmd.Flags().GetString("host") + if errHo != nil { + return fmt.Errorf("error parsing flag 'host' for instance reinstall : %v", errHo) + } + + o.ReinstallReq = &govultr.ReinstallReq{} + if cmd.Flags().Changed("host") { + o.ReinstallReq.Hostname = hostname + } + + if err := o.reinstall(); err != nil { + return fmt.Errorf("error reinstalling instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("Instance reinstalled"), nil) + + return nil + }, + } + + reinstall.Flags().StringP("host", "", "", "The hostname to assign to this instance") + + // Operating System + operatingSystem := &cobra.Command{ + Use: "os", + Short: "Operating system commands for an instance", + } + + // OS List + osList := &cobra.Command{ + Use: "list ", + Short: "List available operating systems", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + upgrades, err := o.upgrades() + if err != nil { + return fmt.Errorf("error getting instance os list : %v", err) + } + + data := &OSsPrinter{OSs: upgrades.OS} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // OS Change + osChange := &cobra.Command{ + Use: "change ", + Short: "Change operating system", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + osID, errOs := cmd.Flags().GetInt("os") + if errOs != nil { + return fmt.Errorf("error parsing flag 'osID' for instance os change : %v", errOs) + } + + o.UpdateReq = &govultr.InstanceUpdateReq{ + OsID: osID, + } + + _, err := o.update() + if err != nil { + return fmt.Errorf("error updating instance os : %v", err) + } + + o.Base.Printer.Display(printer.Info("OS change complete"), nil) + + return nil + }, + } + + osChange.Flags().IntP("os", "", 0, "operating system ID you wish to use") + if err := osChange.MarkFlagRequired("os"); err != nil { + fmt.Printf("error marking instance os update 'os' flag required: %v", err) + os.Exit(1) + } + + operatingSystem.AddCommand( + osList, + osChange, + ) + + // Application + app := &cobra.Command{ + Use: "app", + Aliases: []string{"image"}, + Short: "Application commands for an instance", + } + + // App List + appList := &cobra.Command{ + Use: "list ", + Short: "List available applications", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + upgrades, err := o.upgrades() + if err != nil { + return fmt.Errorf("error getting instance applications list : %v", err) + } + + data := &AppsPrinter{Apps: upgrades.Applications} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // App Change + appChange := &cobra.Command{ + Use: "change ", + Short: "Change application", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + appID, errAp := cmd.Flags().GetInt("app") + if errAp != nil { + return fmt.Errorf("error parsing flag 'app' for instance application change : %v", errAp) + } + + o.UpdateReq = &govultr.InstanceUpdateReq{ + AppID: appID, + } + + _, err := o.update() + if err != nil { + return fmt.Errorf("error updating instance application : %v", err) + } + + o.Base.Printer.Display(printer.Info("Application change complete"), nil) + + return nil + }, + } + + appChange.Flags().IntP("app", "", 0, "Application ID you wish to use") + if err := appChange.MarkFlagRequired("app"); err != nil { + fmt.Printf("error marking instance app update 'app' flag required: %v", err) + os.Exit(1) + } + + app.AddCommand( + appList, + appChange, + ) + + // Plan + plan := &cobra.Command{ + Use: "plan", + Short: "Plan commands for an instance", + } + + // Plan List + planList := &cobra.Command{ + Use: "list ", + Short: "List available plans", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + upgrades, err := o.upgrades() + if err != nil { + return fmt.Errorf("error getting instance applications list : %v", err) + } + + data := &PlansPrinter{Plans: upgrades.Plans} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Plan Upgrade + planUpgrade := &cobra.Command{ + Use: "upgrade ", + Short: "Upgrade instance plan", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + plan, errPl := cmd.Flags().GetString("plan") + if errPl != nil { + return fmt.Errorf("error parsing flag 'plan' for instance plan upgrade : %v", errPl) + } + + o.UpdateReq = &govultr.InstanceUpdateReq{ + Plan: plan, + } + + _, err := o.update() + if err != nil { + return fmt.Errorf("error upgrading plan on instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("Plan upgrade complete"), nil) + + return nil + }, + } + + planUpgrade.Flags().String("plan", "", "The plan ID you wish to use") + if err := planUpgrade.MarkFlagRequired("plan"); err != nil { + fmt.Printf("error marking instance plan upgrade 'plan' flag required: %v", err) + os.Exit(1) + } + + plan.AddCommand( + planList, + planUpgrade, + ) + + // IPv4 + ipv4 := &cobra.Command{ + Use: "ipv4", + Short: "IPv4 instance commands", + } + + // IPv4 List + ipv4List := &cobra.Command{ + Use: "list ", + Aliases: []string{"v4"}, + Short: "List IPv4 for an instance", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + v4s, meta, err := o.ipv4s() + if err != nil { + return fmt.Errorf("error getting ipv4 list for instance : %v", err) + } + + data := &ip.IPv4sPrinter{IPv4s: v4s, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + ipv4List.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + ipv4List.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // IPv4 Create + ipv4Create := &cobra.Command{ + Use: "create ", + Short: "Create IPv4 for instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + reboot, errRe := cmd.Flags().GetBool("reboot") + if errRe != nil { + return fmt.Errorf("error parsing flag 'reboot' for instance ipv4 create: %v", errRe) + } + + if cmd.Flags().Changed("reboot") { + o.Reboot = &reboot + } + + if err := o.ipv4Create(); err != nil { + return fmt.Errorf("error creating instance ipv4 : %v", err) + } + + o.Base.Printer.Display(printer.Info("IPv4 has been created"), nil) + + return nil + }, + } + + ipv4Create.Flags().Bool("reboot", false, "whether to reboot instance after adding ipv4 address") + + // IPv4 Delete + ipv4Delete := &cobra.Command{ + Use: "delete ", + Short: "Delete IPv4 on an instance", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide an instance ID and the IP address") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.ipv4Delete(); err != nil { + return fmt.Errorf("error deleting ipv4 : %v", err) + } + + o.Base.Printer.Display(printer.Info("IPv4 has been deleted"), nil) + + return nil + }, + } + + ipv4.AddCommand( + ipv4List, + ipv4Create, + ipv4Delete, + ) + + // IPv6 + ipv6 := &cobra.Command{ + Use: "ipv6", + Short: "IPv6 instance commands", + } + + // IPv6 List + ipv6List := &cobra.Command{ + Use: "list ", + Aliases: []string{"v6"}, + Short: "List IPv6 for an instance", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + v6s, meta, err := o.ipv6s() + if err != nil { + return fmt.Errorf("error getting ipv6 list for instance : %v", err) + } + + data := &ip.IPv6sPrinter{IPv6s: v6s, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + ipv6List.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + ipv6List.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + ipv6.AddCommand( + ipv6List, + ) + + // Reverse DNS + reverseDNS := &cobra.Command{ + Use: "reverse-dns", + Short: "Commands to handle reverse DNS on an instance", + } + + rDNSIPv4Default := &cobra.Command{ + Use: "default-ipv4 ", + Short: "Set a reverse DNS entry for an IPv4 address of an instance to the original setting", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide an instance ID and IP address") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.reverseDNSDefault(); err != nil { + return fmt.Errorf("error setting default reverse dns ipv4 : %v", err) + } + + o.Base.Printer.Display(printer.Info("Reverse DNS defaulse IPv4 has been set"), nil) + + return nil + }, + } + + // Reverse DNS IPv4 Set + rDNSIPv4Set := &cobra.Command{ + Use: "set-ipv4 ", + Short: "Set a reverse DNS IPv4 address entry for an instance", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide an instance ID and an IPv4 address") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + entry, errEn := cmd.Flags().GetString("entry") + if errEn != nil { + return fmt.Errorf("error parsing flag 'entry' for instance reverse dns ipv4 set : %v", errEn) + } + + o.ReverseDNSReq = &govultr.ReverseIP{ + IP: o.Base.Args[1], + Reverse: entry, + } + + if err := o.reverseDNSIPv4Create(); err != nil { + return fmt.Errorf("error creating reverse dns ipv4 : %v", err) + } + + o.Base.Printer.Display(printer.Info("Reverse DNS IPv4 has been set"), nil) + + return nil + }, + } + + rDNSIPv4Set.Flags().StringP("entry", "e", "", "reverse dns entry") + if err := rDNSIPv4Set.MarkFlagRequired("entry"); err != nil { + fmt.Printf("error marking instance reverse-dns set-ipv4 'entry' flag required: %v", err) + os.Exit(1) + } + + // Reverse DNS IPv6 List + rDNSIPv6List := &cobra.Command{ + Use: "list-ipv6 ", + Short: "List the IPv6 reverse DNS entries for an instance", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + ips, err := o.reverseDNSIPv6List() + if err != nil { + return fmt.Errorf("error retrieving list of reverse dns ipv6 : %v", err) + } + + data := &ReverseIPsPrinter{ReverseIPs: ips} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Reverse DNS IPv6 Set + rDNSIPv6Set := &cobra.Command{ + Use: "set-ipv6 ", + Short: "Set a reverse DNS IPv6 address entry for an instance", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide an instance ID and an IPv6 address") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + entry, errEn := cmd.Flags().GetString("entry") + if errEn != nil { + return fmt.Errorf("error parsing flag 'entry' for instance reverse dns ipv6 set : %v", errEn) + } + + o.ReverseDNSReq = &govultr.ReverseIP{ + IP: o.Base.Args[1], + Reverse: entry, + } + + if err := o.reverseDNSIPv6Create(); err != nil { + return fmt.Errorf("error creating reverse dns ipv6 : %v", err) + } + + o.Base.Printer.Display(printer.Info("Reverse DNS IPv6 has been set"), nil) + + return nil + }, + } + + rDNSIPv6Set.Flags().StringP("entry", "e", "", "reverse dns entry") + if err := rDNSIPv6Set.MarkFlagRequired("entry"); err != nil { + fmt.Printf("error marking instance reverse-dns set-ipv6 'entry' flag required: %v", err) + os.Exit(1) + } + + // Reverse DNS IPv6 Delete + rDNSIPv6Delete := &cobra.Command{ + Use: "delete-ipv6 , ", + Short: "Remove a reverse DNS IPv6 address entry for an instance", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID and an IPv6 address") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.reverseDNSIPv6Delete(); err != nil { + return fmt.Errorf("error deleting reverse dns ipv6 : %v", err) + } + + o.Base.Printer.Display(printer.Info("Reverse DNS IPv6 has been deleted"), nil) + + return nil + }, + } + + reverseDNS.AddCommand( + rDNSIPv4Default, + rDNSIPv4Set, + rDNSIPv6List, + rDNSIPv6Delete, + rDNSIPv6Set, + ) + + // Firewall Group + firewallGroup := &cobra.Command{ + Use: "update-firewall-group", + Short: "Assign a firewall group to instance", + RunE: func(cmd *cobra.Command, args []string) error { + fwgID, errID := cmd.Flags().GetString("firewall-group-id") + if errID != nil { + return fmt.Errorf("error parsing flag 'firewall-group-id' for instance firewall group assignment : %v", errID) + } + + o.UpdateReq = &govultr.InstanceUpdateReq{ + FirewallGroupID: fwgID, + } + + if _, err := o.update(); err != nil { + return fmt.Errorf("error updating fire wall group on instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("Firewall group assigned to instance"), nil) + + return nil + }, + } + + firewallGroup.Flags().StringP( + "firewall-group-id", + "f", + "", + "firewall group id that you want to assign. 0 Value will unset the firewall-group", + ) + if err := firewallGroup.MarkFlagRequired("firewall-group-id"); err != nil { + fmt.Printf("error marking instance firewall group 'firewall-group-id' flag required: %v", err) + os.Exit(1) + } + + // VPC + vpc := &cobra.Command{ + Use: "vpc", + Short: "Commands to handle vpcs on an instance", + } + + // VPC Attach + vpcAttach := &cobra.Command{ + Use: "attach ", + Short: "Attach a VPC to an instance", + Long: vpcAttachLong, + Example: vpcAttachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide an instance ID and a VPC ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.vpcAttach(); err != nil { + return fmt.Errorf("error attaching vpc to instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("VPC attached to instance"), nil) + + return nil + }, + } + + // VPC Detach + vpcDetach := &cobra.Command{ + Use: "detach ", + Short: "Detach a VPC from an instance", + Long: vpcDetachLong, + Example: vpcDetachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide an instance ID and a VPC ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.vpcDetach(); err != nil { + return fmt.Errorf("error detaching vpc from instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("VPC detached from instance"), nil) + + return nil + }, + } + + vpc.AddCommand( + vpcAttach, + vpcDetach, + ) + + // VPC2 + vpc2 := &cobra.Command{ + Use: "vpc2", + Short: "Commands to handle vpc2s on an instance", + } + + // VPC List + vpc2List := &cobra.Command{ + Use: "list ", + Aliases: []string{"l"}, + Short: "List all VPC2 networks attached to an instance", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + vpc2s, meta, err := o.vpc2s() + if err != nil { + return fmt.Errorf("error getting vpc2 list for instance : %v", err) + } + + data := &VPC2sPrinter{VPC2s: vpc2s, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // VPC2 Attach + vpc2Attach := &cobra.Command{ + Use: "attach , ", + Short: "Attach a VPC2 to an instance", + Long: vpc2AttachLong, + Example: vpc2AttachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide an instance ID and a VPC ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + ip, errIP := cmd.Flags().GetString("ip-address") + if errIP != nil { + return fmt.Errorf("error parsing flag 'ip-address' for vpc2 instance attach : %v", errIP) + } + + o.VPC2Req = &govultr.AttachVPC2Req{ + VPCID: o.Base.Args[1], + IPAddress: &ip, + } + + if err := o.vpc2Attach(); err != nil { + return fmt.Errorf("error attaching vpc2 to instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("VPC2 attached to instance"), nil) + + return nil + }, + } + + vpc2Attach.Flags().StringP( + "ip-address", + "i", + "", + "the IP address to use for this instance on the attached VPC 2.0 network", + ) + + // VPC2 Detach + vpc2Detach := &cobra.Command{ + Use: "detach ", + Short: "Detach a VPC2 from an instance", + Long: vpc2DetachLong, + Example: vpc2DetachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide an instance ID and a VPC2 ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.vpc2Detach(); err != nil { + return fmt.Errorf("error detaching vpc2 from instance : %v", err) + } + + o.Base.Printer.Display(printer.Info("VPC2 detached from instance"), nil) + + return nil + }, + } + + vpc2.AddCommand( + vpc2List, + vpc2Attach, + vpc2Detach, + ) + + // Bandwidth + bandwidth := &cobra.Command{ + Use: "bandwidth ", + Short: "Get bandwidth usage ", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an instance ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + bw, err := o.bandwidth() + if err != nil { + return fmt.Errorf("error getting bandwidth details : %v", err) + } + + data := &BandwidthPrinter{Bandwidth: *bw} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + cmd.AddCommand( + list, + get, + create, + del, + label, + tags, + userData, + start, + stop, + restart, + iso, + backup, + restore, + reinstall, + operatingSystem, + app, + plan, + ipv4, + ipv6, + reverseDNS, + firewallGroup, + vpc, + vpc2, + bandwidth, + ) + + return cmd +} + +type options struct { + Base *cli.Base + CreateReq *govultr.InstanceCreateReq + UpdateReq *govultr.InstanceUpdateReq + BackupCreateReq *govultr.BackupScheduleReq + RestoreReq *govultr.RestoreReq + ReinstallReq *govultr.ReinstallReq + ISOAttachID string + Reboot *bool + ReverseDNSReq *govultr.ReverseIP + VPC2Req *govultr.AttachVPC2Req +} + +func (o *options) list() ([]govultr.Instance, *govultr.Meta, error) { + insts, meta, _, err := o.Base.Client.Instance.List(o.Base.Context, o.Base.Options) + return insts, meta, err +} + +func (o *options) get() (*govultr.Instance, error) { + inst, _, err := o.Base.Client.Instance.Get(o.Base.Context, o.Base.Args[0]) + return inst, err +} + +func (o *options) create() (*govultr.Instance, error) { + inst, _, err := o.Base.Client.Instance.Create(o.Base.Context, o.CreateReq) + return inst, err +} + +func (o *options) update() (*govultr.Instance, error) { + inst, _, err := o.Base.Client.Instance.Update(o.Base.Context, o.Base.Args[0], o.UpdateReq) + return inst, err +} + +func (o *options) del() error { + return o.Base.Client.Instance.Delete(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) userData() (*govultr.UserData, error) { + ud, _, err := o.Base.Client.Instance.GetUserData(o.Base.Context, o.Base.Args[0]) + return ud, err +} + +func (o *options) start() error { + return o.Base.Client.Instance.Start(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) stop() error { + return o.Base.Client.Instance.Halt(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) restart() error { + return o.Base.Client.Instance.Reboot(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) backups() (*govultr.BackupSchedule, error) { + bk, _, err := o.Base.Client.Instance.GetBackupSchedule(o.Base.Context, o.Base.Args[0]) + return bk, err +} + +func (o *options) backupCreate() error { + _, err := o.Base.Client.Instance.SetBackupSchedule(o.Base.Context, o.Base.Args[0], o.BackupCreateReq) + return err +} + +func (o *options) restore() error { + _, err := o.Base.Client.Instance.Restore(o.Base.Context, o.Base.Args[0], o.RestoreReq) + return err +} + +func (o *options) reinstall() error { + _, _, err := o.Base.Client.Instance.Reinstall(o.Base.Context, o.Base.Args[0], o.ReinstallReq) + return err +} + +func (o *options) isoStatus() (*govultr.Iso, error) { + iso, _, err := o.Base.Client.Instance.ISOStatus(o.Base.Context, o.Base.Args[0]) + return iso, err +} + +func (o *options) isoAttach() error { + _, err := o.Base.Client.Instance.AttachISO(o.Base.Context, o.Base.Args[0], o.ISOAttachID) + return err +} + +func (o *options) upgrades() (*govultr.Upgrades, error) { + oss, _, err := o.Base.Client.Instance.GetUpgrades(o.Base.Context, o.Base.Args[0]) + return oss, err +} + +func (o *options) ipv4s() ([]govultr.IPv4, *govultr.Meta, error) { + ips, meta, _, err := o.Base.Client.Instance.ListIPv4(o.Base.Context, o.Base.Args[0], o.Base.Options) + return ips, meta, err +} + +func (o *options) ipv4Create() error { + _, _, err := o.Base.Client.Instance.CreateIPv4(o.Base.Context, o.Base.Args[0], o.Reboot) + return err +} + +func (o *options) ipv4Delete() error { + return o.Base.Client.Instance.DeleteIPv4(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) ipv6s() ([]govultr.IPv6, *govultr.Meta, error) { + ips, meta, _, err := o.Base.Client.Instance.ListIPv6(o.Base.Context, o.Base.Args[0], o.Base.Options) + return ips, meta, err +} + +func (o *options) reverseDNSDefault() error { + return o.Base.Client.Instance.DefaultReverseIPv4(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) reverseDNSIPv4Create() error { + return o.Base.Client.Instance.CreateReverseIPv4(o.Base.Context, o.Base.Args[0], o.ReverseDNSReq) +} + +func (o *options) reverseDNSIPv6List() ([]govultr.ReverseIP, error) { + ips, _, err := o.Base.Client.Instance.ListReverseIPv6(o.Base.Context, o.Base.Args[0]) + return ips, err +} + +func (o *options) reverseDNSIPv6Create() error { + return o.Base.Client.Instance.CreateReverseIPv6(o.Base.Context, o.Base.Args[0], o.ReverseDNSReq) +} + +func (o *options) reverseDNSIPv6Delete() error { + return o.Base.Client.Instance.DeleteReverseIPv6(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) vpcAttach() error { + return o.Base.Client.Instance.AttachVPC(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) vpcDetach() error { + return o.Base.Client.Instance.DetachVPC(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) vpc2s() ([]govultr.VPC2Info, *govultr.Meta, error) { + vpc2s, meta, _, err := o.Base.Client.Instance.ListVPC2Info(o.Base.Context, o.Base.Args[0], o.Base.Options) + return vpc2s, meta, err +} + +func (o *options) vpc2Attach() error { + return o.Base.Client.Instance.AttachVPC2(o.Base.Context, o.Base.Args[0], o.VPC2Req) +} + +func (o *options) vpc2Detach() error { + return o.Base.Client.Instance.DetachVPC2(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) bandwidth() (*govultr.Bandwidth, error) { + bw, _, err := o.Base.Client.Instance.GetBandwidth(o.Base.Context, o.Base.Args[0]) + return bw, err +} diff --git a/cmd/instance/printer.go b/cmd/instance/printer.go new file mode 100644 index 00000000..b232694d --- /dev/null +++ b/cmd/instance/printer.go @@ -0,0 +1,513 @@ +package instance + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// InstancesPrinter ... +type InstancesPrinter struct { + Instances []govultr.Instance `json:"instances"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (i *InstancesPrinter) JSON() []byte { + return printer.MarshalObject(i, "json") +} + +// YAML ... +func (i *InstancesPrinter) YAML() []byte { + return printer.MarshalObject(i, "yaml") +} + +// Columns ... +func (i *InstancesPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "IP", + "LABEL", + "OS", + "STATUS", + "REGION", + "CPU", + "RAM", + "DISK", + "BANDWIDTH", + "TAGS", + }} +} + +// Data ... +func (i *InstancesPrinter) Data() [][]string { + if len(i.Instances) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for j := range i.Instances { + data = append(data, []string{ + i.Instances[j].ID, + i.Instances[j].MainIP, + i.Instances[j].Label, + i.Instances[j].Os, + i.Instances[j].Status, + i.Instances[j].Region, + strconv.Itoa(i.Instances[j].VCPUCount), + strconv.Itoa(i.Instances[j].RAM), + strconv.Itoa(i.Instances[j].Disk), + strconv.Itoa(i.Instances[j].AllowedBandwidth), + printer.ArrayOfStringsToString(i.Instances[j].Tags), + }) + } + return data +} + +// Paging ... +func (i *InstancesPrinter) Paging() [][]string { + return printer.NewPaging(i.Meta.Total, &i.Meta.Links.Next, &i.Meta.Links.Prev).Compose() +} + +// ====================================== + +// InstancePrinter ... +type InstancePrinter struct { + Instance *govultr.Instance `json:"instance"` +} + +// JSON ... +func (i *InstancePrinter) JSON() []byte { + return printer.MarshalObject(i, "json") +} + +// YAML ... +func (i *InstancePrinter) YAML() []byte { + return printer.MarshalObject(i, "yaml") +} + +// Columns ... +func (i *InstancePrinter) Columns() [][]string { + return [][]string{0: {"INSTANCE INFO"}} +} + +// Data ... +func (i *InstancePrinter) Data() [][]string { + var data [][]string + data = append(data, + []string{"ID", i.Instance.ID}, + []string{"OS", i.Instance.Os}, + []string{"OS ID", strconv.Itoa(i.Instance.OsID)}, + []string{"APP ID", strconv.Itoa(i.Instance.AppID)}, + []string{"RAM", strconv.Itoa(i.Instance.RAM)}, + []string{"DISK", strconv.Itoa(i.Instance.Disk)}, + []string{"MAIN IP", i.Instance.MainIP}, + []string{"VCPU COUNT", strconv.Itoa(i.Instance.VCPUCount)}, + []string{"REGION", i.Instance.Region}, + []string{"DATE CREATED", i.Instance.DateCreated}, + []string{"STATUS", i.Instance.Status}, + []string{"ALLOWED BANDWIDTH", strconv.Itoa(i.Instance.AllowedBandwidth)}, + []string{"NETMASK V4", i.Instance.NetmaskV4}, + []string{"GATEWAY V4", i.Instance.GatewayV4}, + []string{"POWER STATUS", i.Instance.PowerStatus}, + []string{"SERVER STATE", i.Instance.ServerStatus}, + []string{"PLAN", i.Instance.Plan}, + []string{"LABEL", i.Instance.Label}, + []string{"INTERNAL IP", i.Instance.InternalIP}, + []string{"KVM URL", i.Instance.KVM}, + []string{"FIREWALL GROUP ID", i.Instance.FirewallGroupID}, + []string{"V6 MAIN IP", i.Instance.V6MainIP}, + []string{"V6 NETWORK", i.Instance.V6Network}, + []string{"V6 NETWORK SIZE", strconv.Itoa(i.Instance.V6NetworkSize)}, + []string{"FEATURES", printer.ArrayOfStringsToString(i.Instance.Features)}, + []string{"TAGS", printer.ArrayOfStringsToString(i.Instance.Tags)}, + ) + + return data +} + +// Paging ... +func (i *InstancePrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// BandwidthPrinter ... +type BandwidthPrinter struct { + Bandwidth govultr.Bandwidth `json:"bandwidth"` +} + +// JSON ... +func (b *BandwidthPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BandwidthPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BandwidthPrinter) Columns() [][]string { + return [][]string{0: { + "DATE", + "INCOMING BYTES", + "OUTGOING BYTES", + }} +} + +// Data ... +func (b *BandwidthPrinter) Data() [][]string { + var data [][]string + for i := range b.Bandwidth.Bandwidth { + data = append(data, []string{ + i, + strconv.Itoa(b.Bandwidth.Bandwidth[i].IncomingBytes), + strconv.Itoa(b.Bandwidth.Bandwidth[i].OutgoingBytes), + }) + } + + return data +} + +// Paging ... +func (b *BandwidthPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// BackupPrinter ... +type BackupPrinter struct { + Backup govultr.BackupSchedule `json:"backup_schedule"` +} + +// JSON ... +func (b *BackupPrinter) JSON() []byte { + return printer.MarshalObject(b, "json") +} + +// YAML ... +func (b *BackupPrinter) YAML() []byte { + return printer.MarshalObject(b, "yaml") +} + +// Columns ... +func (b *BackupPrinter) Columns() [][]string { + return [][]string{0: { + "ENABLED", + "CRON TYPE", + "NEXT RUN", + "HOUR", + "DOW", + "DOM", + }} +} + +// Data ... +func (b *BackupPrinter) Data() [][]string { + return [][]string{0: { + strconv.FormatBool(*b.Backup.Enabled), + b.Backup.Type, + b.Backup.NextScheduleTimeUTC, + strconv.Itoa(b.Backup.Hour), + strconv.Itoa(b.Backup.Dow), + strconv.Itoa(b.Backup.Dom), + }} +} + +// Paging ... +func (b *BackupPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// ISOPrinter ... +type ISOPrinter struct { + ISO govultr.Iso `json:"iso_status"` +} + +// JSON ... +func (i *ISOPrinter) JSON() []byte { + return printer.MarshalObject(i, "json") +} + +// YAML ... +func (i *ISOPrinter) YAML() []byte { + return printer.MarshalObject(i, "yaml") +} + +// Columns ... +func (i *ISOPrinter) Columns() [][]string { + return [][]string{0: { + "ISO ID", + "STATE", + }} +} + +// Data ... +func (i *ISOPrinter) Data() [][]string { + return [][]string{0: { + i.ISO.IsoID, + i.ISO.State, + }} +} + +// Paging ... +func (i *ISOPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// OSsPrinter ... +type OSsPrinter struct { + OSs []govultr.OS `json:"operating_systems"` +} + +// JSON ... +func (o *OSsPrinter) JSON() []byte { + return printer.MarshalObject(o, "json") +} + +// YAML ... +func (o *OSsPrinter) YAML() []byte { + return printer.MarshalObject(o, "yaml") +} + +// Columns ... +func (o *OSsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "NAME", + "ARCH", + "FAMILY", + }} +} + +// Data ... +func (o *OSsPrinter) Data() [][]string { + if len(o.OSs) == 0 { + return [][]string{0: {"---", "---", "---", "---"}} + } + + var data [][]string + for i := range o.OSs { + data = append(data, []string{ + strconv.Itoa(o.OSs[i].ID), + o.OSs[i].Name, + o.OSs[i].Arch, + o.OSs[i].Family, + }) + } + + return data +} + +// Paging ... +func (o *OSsPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// AppsPrinter ... +type AppsPrinter struct { + Apps []govultr.Application `json:"applications"` +} + +// JSON ... +func (a *AppsPrinter) JSON() []byte { + return printer.MarshalObject(a, "json") +} + +// YAML ... +func (a *AppsPrinter) YAML() []byte { + return printer.MarshalObject(a, "yaml") +} + +// Columns ... +func (a *AppsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "NAME", + "SHORT NAME", + "DEPLOY NAME", + "TYPE", + "VENDOR", + "IMAGE ID", + }} +} + +// Data ... +func (a *AppsPrinter) Data() [][]string { + if len(a.Apps) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range a.Apps { + data = append(data, []string{ + strconv.Itoa(a.Apps[i].ID), + a.Apps[i].Name, + a.Apps[i].ShortName, + a.Apps[i].DeployName, + a.Apps[i].Type, + a.Apps[i].Vendor, + a.Apps[i].ImageID, + }) + } + + return data +} + +// Paging ... +func (a *AppsPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// PlansPrinter ... +type PlansPrinter struct { + Plans []string `json:"plans"` +} + +// JSON ... +func (p *PlansPrinter) JSON() []byte { + return printer.MarshalObject(p, "json") +} + +// YAML ... +func (p *PlansPrinter) YAML() []byte { + return printer.MarshalObject(p, "yaml") +} + +// Columns ... +func (p *PlansPrinter) Columns() [][]string { + return [][]string{0: { + "PLAN NAME", + }} +} + +// Data ... +func (p *PlansPrinter) Data() [][]string { + if len(p.Plans) == 0 { + return [][]string{0: {"---"}} + } + + var data [][]string + for i := range p.Plans { + data = append(data, []string{ + p.Plans[i], + }) + } + + return data +} + +// Paging ... +func (p *PlansPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// ReverseIPsPrinter ... +type ReverseIPsPrinter struct { + ReverseIPs []govultr.ReverseIP `json:"reverse_ips"` +} + +// JSON ... +func (r *ReverseIPsPrinter) JSON() []byte { + return printer.MarshalObject(r, "json") +} + +// YAML ... +func (r *ReverseIPsPrinter) YAML() []byte { + return printer.MarshalObject(r, "yaml") +} + +// Columns ... +func (r *ReverseIPsPrinter) Columns() [][]string { + return [][]string{0: { + "IP", + "REVERSE", + }} +} + +// Data ... +func (r *ReverseIPsPrinter) Data() [][]string { + if len(r.ReverseIPs) == 0 { + return [][]string{0: {"---", "---"}} + } + + var data [][]string + for j := range r.ReverseIPs { + data = append(data, []string{ + r.ReverseIPs[j].IP, + r.ReverseIPs[j].Reverse, + }) + } + + return data +} + +// Paging ... +func (r *ReverseIPsPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// VPC2sPrinter ... +type VPC2sPrinter struct { + VPC2s []govultr.VPC2Info `json:"vpcs"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (v *VPC2sPrinter) JSON() []byte { + return printer.MarshalObject(v, "json") +} + +// YAML ... +func (v *VPC2sPrinter) YAML() []byte { + return printer.MarshalObject(v, "yaml") +} + +// Columns ... +func (v *VPC2sPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "MAC ADDRESS", + "IP ADDRESS", + }} +} + +// Data ... +func (v *VPC2sPrinter) Data() [][]string { + var data [][]string + + if len(v.VPC2s) == 0 { + return [][]string{0: {"---", "---", "---"}} + } + + for i := range v.VPC2s { + data = append(data, []string{ + v.VPC2s[i].ID, + v.VPC2s[i].MacAddress, + v.VPC2s[i].IPAddress, + }) + } + + return data +} + +// Paging ... +func (v *VPC2sPrinter) Paging() [][]string { + return printer.NewPaging(v.Meta.Total, &v.Meta.Links.Next, &v.Meta.Links.Prev).Compose() +} diff --git a/cmd/ip/printer.go b/cmd/ip/printer.go new file mode 100644 index 00000000..f05a5272 --- /dev/null +++ b/cmd/ip/printer.go @@ -0,0 +1,103 @@ +// Package ip provides printers for server network addresses +package ip + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// IPv4sPrinter ... +type IPv4sPrinter struct { + IPv4s []govultr.IPv4 `json:"ipv4s"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (i *IPv4sPrinter) JSON() []byte { + return printer.MarshalObject(i, "json") +} + +// YAML ... +func (i *IPv4sPrinter) YAML() []byte { + return printer.MarshalObject(i, "yaml") +} + +// Columns ... +func (i *IPv4sPrinter) Columns() [][]string { + return [][]string{0: { + "IP", + "NETMASK", + "GATEWAY", + "TYPE", + }} +} + +// Data ... +func (i *IPv4sPrinter) Data() [][]string { + var data [][]string + for j := range i.IPv4s { + data = append(data, []string{ + i.IPv4s[j].IP, + i.IPv4s[j].Netmask, + i.IPv4s[j].Gateway, + i.IPv4s[j].Type, + }) + } + + return data +} + +// Paging ... +func (i *IPv4sPrinter) Paging() [][]string { + return printer.NewPaging(i.Meta.Total, &i.Meta.Links.Next, &i.Meta.Links.Prev).Compose() +} + +// ====================================== + +// IPv6sPrinter ... +type IPv6sPrinter struct { + IPv6s []govultr.IPv6 `json:"ipv6s"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (i *IPv6sPrinter) JSON() []byte { + return printer.MarshalObject(i, "json") +} + +// YAML ... +func (i *IPv6sPrinter) YAML() []byte { + return printer.MarshalObject(i, "yaml") +} + +// Columns ... +func (i *IPv6sPrinter) Columns() [][]string { + return [][]string{0: { + "IP", + "NETWORK", + "NETWORK SIZE", + "TYPE", + }} +} + +// Data ... +func (i *IPv6sPrinter) Data() [][]string { + var data [][]string + for j := range i.IPv6s { + data = append(data, []string{ + i.IPv6s[j].IP, + i.IPv6s[j].Network, + strconv.Itoa(i.IPv6s[j].NetworkSize), + i.IPv6s[j].Type, + }) + } + + return data +} + +// Paging ... +func (i *IPv6sPrinter) Paging() [][]string { + return printer.NewPaging(i.Meta.Total, &i.Meta.Links.Next, &i.Meta.Links.Prev).Compose() +} diff --git a/cmd/iso.go b/cmd/iso.go deleted file mode 100644 index d472a851..00000000 --- a/cmd/iso.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" - - "github.com/spf13/cobra" -) - -// ISO represents the iso command -func ISO() *cobra.Command { - isoCmd := &cobra.Command{ - Use: "iso", - Short: "iso is used to access iso commands", - Long: ``, - } - - isoCmd.AddCommand(isoCreate, isoDelete, isoPrivateGet, isoPrivateList, isoPublic) - isoCreate.Flags().StringP("url", "u", "", "url from where the ISO will be downloaded") - isoCreate.MarkFlagRequired("url") - - isoPrivateList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - isoPrivateList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - isoPublic.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - isoPublic.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return isoCmd -} - -var isoPrivateGet = &cobra.Command{ - Use: "get ", - Short: "get private ISO ", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an ISO id") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - iso, err := client.ISO.Get(context.Background(), id) - if err != nil { - fmt.Printf("error getting ISO : %v\n", err) - os.Exit(1) - } - - printer.IsoPrivate(iso) - }, -} - -var isoPrivateList = &cobra.Command{ - Use: "list", - Short: "list all private ISOs available", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - isos, meta, err := client.ISO.List(context.Background(), options) - if err != nil { - fmt.Printf("error getting private ISOs : %v\n", err) - os.Exit(1) - } - - printer.IsoPrivates(isos, meta) - }, -} - -var isoPublic = &cobra.Command{ - Use: "public", - Short: "list all public ISOs available", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - isos, meta, err := client.ISO.ListPublic(context.Background(), options) - if err != nil { - fmt.Printf("error getting public ISOs : %v\n", err) - os.Exit(1) - } - - printer.IsoPublic(isos, meta) - }, -} - -var isoCreate = &cobra.Command{ - Use: "create", - Short: "create iso from url", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - url, _ := cmd.Flags().GetString("url") - options := &govultr.ISOReq{ - URL: url, - } - - iso, err := client.ISO.Create(context.Background(), options) - if err != nil { - fmt.Printf("error creating ISOs : %v\n", err) - os.Exit(1) - } - - printer.IsoPrivate(iso) - }, -} - -var isoDelete = &cobra.Command{ - Use: "delete ", - Short: "delete a private iso", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an isoID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.ISO.Delete(context.Background(), id); err != nil { - fmt.Printf("error deleting ISOs : %v\n", err) - os.Exit(1) - } - - fmt.Println("ISO has been deleted") - }, -} diff --git a/cmd/iso/iso.go b/cmd/iso/iso.go new file mode 100644 index 00000000..cf7d4936 --- /dev/null +++ b/cmd/iso/iso.go @@ -0,0 +1,190 @@ +// Package iso provides the ISO related commands to the CLI +package iso + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +// NewCmdISO provides the CLI command for ISO functions +func NewCmdISO(base *cli.Base) *cobra.Command { + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "iso", + Short: "iso is used to access iso commands", + Long: ``, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "list all private ISOs available", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + isos, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving private ISO list : %v", err) + } + + data := &ISOsPrinter{ISOs: isos, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + "(optional) Number of items requested per page. Default is 100 and Max is 500.", + ) + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "get private ISO by ID", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an ISO ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + iso, err := o.get() + if err != nil { + return fmt.Errorf("error getting ISO : %v", err) + } + + data := &ISOPrinter{ISO: *iso} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "create ISO from url", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + url, errUR := cmd.Flags().GetString("url") + if errUR != nil { + return fmt.Errorf("error parsing flag 'url' for ISO create : %v", errUR) + } + + o.CreateReq = &govultr.ISOReq{URL: url} + + iso, err := o.create() + if err != nil { + return fmt.Errorf("error creating ISO : %v", err) + } + + data := &ISOPrinter{ISO: *iso} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + create.Flags().StringP("url", "u", "", "url from where the ISO will be downloaded") + if err := create.MarkFlagRequired("url"); err != nil { + printer.Error(fmt.Errorf("error marking iso create 'url' flag required : %v", err)) + os.Exit(1) + } + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "delete a private ISO", + Aliases: []string{"destroy"}, + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an ISO ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.del(); err != nil { + return fmt.Errorf("error deleting ISO : %v", err) + } + + o.Base.Printer.Display(printer.Info("ISO has been deleted"), nil) + return nil + }, + } + + // Public ISOs + public := &cobra.Command{ + Use: "public", + Short: "list all public ISOs available", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + isos, meta, err := o.listPublic() + if err != nil { + return fmt.Errorf("error retrieving public ISO list : %v", err) + } + + data := &PublicISOsPrinter{ISOs: isos, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + cmd.AddCommand(list, get, create, del, public) + + return cmd +} + +type options struct { + Base *cli.Base + CreateReq *govultr.ISOReq +} + +func (o *options) list() ([]govultr.ISO, *govultr.Meta, error) { + isos, meta, _, err := o.Base.Client.ISO.List(o.Base.Context, o.Base.Options) + return isos, meta, err +} + +func (o *options) get() (*govultr.ISO, error) { + iso, _, err := o.Base.Client.ISO.Get(o.Base.Context, o.Base.Args[0]) + return iso, err +} + +func (o *options) create() (*govultr.ISO, error) { + iso, _, err := o.Base.Client.ISO.Create(o.Base.Context, o.CreateReq) + return iso, err +} + +func (o *options) del() error { + return o.Base.Client.ISO.Delete(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) listPublic() ([]govultr.PublicISO, *govultr.Meta, error) { + isos, meta, _, err := o.Base.Client.ISO.ListPublic(o.Base.Context, o.Base.Options) + return isos, meta, err +} diff --git a/cmd/iso/printer.go b/cmd/iso/printer.go new file mode 100644 index 00000000..ccb36b3a --- /dev/null +++ b/cmd/iso/printer.go @@ -0,0 +1,158 @@ +package iso + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// ISOsPrinter ... +type ISOsPrinter struct { + ISOs []govultr.ISO `json:"isos"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (i *ISOsPrinter) JSON() []byte { + return printer.MarshalObject(i, "json") +} + +// YAML ... +func (i *ISOsPrinter) YAML() []byte { + return printer.MarshalObject(i, "yaml") +} + +// Columns ... +func (i *ISOsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "FILE NAME", + "SIZE", + "STATUS", + "MD5SUM", + "SHA512SUM", + "DATE CREATED", + }} +} + +// Data ... +func (i *ISOsPrinter) Data() [][]string { + if len(i.ISOs) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for n := range i.ISOs { + data = append(data, []string{ + i.ISOs[n].ID, + i.ISOs[n].FileName, + strconv.Itoa(i.ISOs[n].Size), + i.ISOs[n].Status, + i.ISOs[n].MD5Sum, + i.ISOs[n].SHA512Sum, + i.ISOs[n].DateCreated, + }) + } + + return data +} + +// Paging ... +func (i *ISOsPrinter) Paging() [][]string { + return printer.NewPaging(i.Meta.Total, &i.Meta.Links.Next, &i.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ISOPrinter ... +type ISOPrinter struct { + ISO govultr.ISO `json:"iso"` +} + +// JSON ... +func (i *ISOPrinter) JSON() []byte { + return printer.MarshalObject(i, "json") +} + +// YAML ... +func (i *ISOPrinter) YAML() []byte { + return printer.MarshalObject(i, "yaml") +} + +// Columns ... +func (i *ISOPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "FILE NAME", + "SIZE", + "STATUS", + "MD5SUM", + "SHA512SUM", + "DATE CREATED", + }} +} + +// Data ... +func (i *ISOPrinter) Data() [][]string { + return [][]string{0: { + i.ISO.ID, + i.ISO.FileName, + strconv.Itoa(i.ISO.Size), + i.ISO.Status, + i.ISO.MD5Sum, + i.ISO.SHA512Sum, + i.ISO.DateCreated, + }} +} + +// Paging ... +func (i *ISOPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// PublicISOsPrinter ... +type PublicISOsPrinter struct { + ISOs []govultr.PublicISO `json:"public_isos"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (i *PublicISOsPrinter) JSON() []byte { + return printer.MarshalObject(i, "json") +} + +// YAML ... +func (i *PublicISOsPrinter) YAML() []byte { + return printer.MarshalObject(i, "yaml") +} + +// Columns ... +func (i *PublicISOsPrinter) Columns() [][]string { + return [][]string{0: {"ID", "NAME", "DESCRIPTION"}} +} + +// Data ... +func (i *PublicISOsPrinter) Data() [][]string { + if len(i.ISOs) == 0 { + return [][]string{0: {"---", "---", "---"}} + } + + var data [][]string + for n := range i.ISOs { + data = append(data, []string{ + i.ISOs[n].ID, + i.ISOs[n].Name, + i.ISOs[n].Description, + }) + } + + return data +} + +// Paging ... +func (i *PublicISOsPrinter) Paging() [][]string { + return printer.NewPaging(i.Meta.Total, &i.Meta.Links.Next, &i.Meta.Links.Prev).Compose() +} diff --git a/cmd/kubernetes/kubernetes.go b/cmd/kubernetes/kubernetes.go new file mode 100644 index 00000000..8b06a038 --- /dev/null +++ b/cmd/kubernetes/kubernetes.go @@ -0,0 +1,1143 @@ +// Package kubernetes provides functionality for the CLI to control VKE clusters +package kubernetes + +import ( + "encoding/base64" + "errors" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Get all available commands for Kubernetes` + example = ` + # Full example + vultr-cli kubernetes + ` + + createLong = `Create kubernetes cluster on your Vultr account` + createExample = ` + # Full Example + vultr-cli kubernetes create --label="my-cluster" --region="ewr" --version="v1.29.1+1" \ + --node-pools="quantity:3,plan:vc2-2c-4gb,label:my-nodepool,tag:my-tag" + + # Shortened with alias commands + vultr-cli k c -l="my-cluster" -r="ewr" -v="v1.29.1+1" -n="quantity:3,plan:vc2-2c-4gb,label:my-nodepool,tag:my-tag" + ` + + getLong = `Get a single kubernetes cluster from your account` + getExample = ` + # Full example + vultr-cli kubernetes get ffd31f18-5f77-454c-9064-212f942c3c34 + + # Shortened with alias commands + vultr-cli k g ffd31f18-5f77-454c-9064-212f942c3c34 + ` + + listLong = `Get all kubernetes clusters available on your Vultr account` + listExample = ` + # Full example + vultr-cli kubernetes list + + # Full example with paging + vultr-cli kubernetes list --per-page=1 --cursor="bmV4dF9fQU1T" + + # Shortened with alias commands + vultr-cli k l + + # Summarized view + vultr-cli kubernetes list --summarize + ` + + updateLong = `Update a specific kubernetes cluster on your Vultr Account` + updateExample = ` + # Full example + vultr-cli kubernetes update ffd31f18-5f77-454c-9065-212f942c3c35 --label="updated-label" + + # Shortened with alias commands + vultr-cli k u ffd31f18-5f77-454c-9065-212f942c3c35 -l="updated-label" + ` + + deleteLong = `Delete a specific kubernetes cluster off your Vultr Account` + deleteExample = ` + # Full example + vultr-cli kubernetes delete ffd31f18-5f77-454c-9065-212f942c3c35 + + # Shortened with alias commands + vultr-cli k d ffd31f18-5f77-454c-9065-212f942c3c35' + + # Delete a specific kubernetes cluster and all linked load balancers and block storages off your Vultr Account + vultr-cli kubernetes delete-with-resources ffd31f18-5f77-454c-9065-212f942c3c35 + ` + getConfigLong = `Returns a base64 encoded config of a specified kubernetes cluster on your Vultr Account` + getConfigExample = ` + + # Full example + vultr-cli kubernetes config ffd31f18-5f77-454c-9065-212f942c3c35 + vultr-cli kubernetes config ffd31f18-5f77-454c-9065-212f942c3c35 --output-file /your/path/ + + # Shortened with alias commands + vultr-cli k config ffd31f18-5f77-454c-9065-212f942c3c35 + vultr-cli k config ffd31f18-5f77-454c-9065-212f942c3c35 -o /your/path/ + ` + + getVersionsLong = `Returns a list of supported kubernetes versions you can deploy` + getVersionsExample = ` + # Full example + vultr-cli kubernetes versions + + # Shortened with alias commands + vultr-cli k v + ` + + upgradesLong = `Display available kubernetes upgrade commands` + upgradesExample = ` + # Full example + vultr-cli kubernetes upgrades + + # Shortened example with aliases + vultr-cli k e + ` + + getUpgradesLong = `Returns a list of available kubernetes version the cluster can be upgraded to` + getUpgradesExample = ` + # Full example + vultr-cli kubernetes upgrades list d4908765-b82a-4e7d-83d9-c0bc4c6a36d0 + + # Shortened with alias commands + vultr-cli k e l d4908765-b82a-4e7d-83d9-c0bc4c6a36d0 + ` + + upgradeLong = `Initiate an upgrade of the kubernetes version on a given cluster` + upgradeExample = ` + # Full example + vultr-cli kubernetes upgrades start d4908765-b82a-4e7d-83d9-c0bc4c6a36d0 --version="v1.23.5+3" + + # Shortened with alias commands + vultr-cli k e s d4908765-b82a-4e7d-83d9-c0bc4c6a36d0 -v="v1.23.5+3" + ` + + nodepoolLong = `Get all available commands for Kubernetes node pools` + nodepoolExample = ` + # Full example + vultr-cli kubernetes node-pool + + # Shortened with alias commands + vultr-cli k n + ` + + createNPLong = `Create node pool for your kubernetes cluster on your Vultr account` + createNPExample = ` + # Full Example + vultr-cli kubernetes node-pool create ffd31f18-5f77-454c-9064-212f942c3c34 --label="nodepool" --quantity=3 --plan="vc2-1c-2gb" + + # Shortened with alias commands + vultr-cli k n c ffd31f18-5f77-454c-9064-212f942c3c34 -l="nodepool" -q=3 -p="vc2-1c-2gb" + ` + + getNPLong = `Get a node pool in a single kubernetes cluster from your account` + getNPExample = ` + # Full example + vultr-cli kubernetes node-pool get ffd31f18-5f77-454c-9064-212f942c3c34 abd31f18-3f77-454c-9064-212f942c3c34 + # Shortened with alias commands + vultr-cli k n g ffd31f18-5f77-454c-9064-212f942c3c34 abd31f18-3f77-454c-9064-212f942c3c34 + ` + + listNPLong = `Get all nodepools from a kubernetes cluster on your Vultr account` + listNPExample = ` + # Full example + vultr-cli kubernetes node-pool list ffd31f18-5f77-454c-9064-212f942c3c34 + + # Full example with paging + vultr-cli kubernetes node-pool list ffd31f18-5f77-454c-9064-212f942c3c34 --per-page=1 --cursor="bmV4dF9fQU1T" + + # Shortened with alias commands + vultr-cli k n l ffd31f18-5f77-454c-9064-212f942c3c34 + ` + + updateNPLong = `Update a specific node pool in a kubernetes cluster on your Vultr Account` + updateNPExample = ` + # Full example + vultr-cli kubernetes node-pool update ffd31f18-5f77-454c-9064-212f942c3c34 abd31f18-3f77-454c-9064-212f942c3c34 --quantity=4 + + # Shortened with alias commands + vultr-cli k n u ffd31f18-5f77-454c-9065-212f942c3c35 abd31f18-3f77-454c-9064-212f942c3c34 --q=4 + ` + + deleteNPLong = `Delete a specific node pool in a kubernetes cluster off your Vultr Account` + deleteNPExample = ` + # Full example + vultr-cli kubernetes node-pool delete ffd31f18-5f77-454c-9065-212f942c3c35 abd31f18-3f77-454c-9064-212f942c3c34 + + # Shortened with alias commands + vultr-cli k n d ffd31f18-5f77-454c-9065-212f942c3c35 abd31f18-3f77-454c-9064-212f942c3c34' + ` + + nodeLong = `Get all available commands for Kubernetes node pool nodes` + nodeExample = ` + # Full example + vultr-cli kubernetes node-pool node + + # Shortened with alias commands + vultr-cli k n node + ` + + nodeDeleteLong = `Delete a specific node pool node in a kubernetes cluster` + nodeDeleteExample = ` + # Full example + vultr-cli kubernetes node-pool node delete ffd31f18-5f77-454c-9065-212f942c3c35 + + # Shortened with alias commands + vultr-cli k n node d ffd31f18-5f77-454c-9065-212f942c3c35 + ` + + nodeRecycleLong = `Recycles a specific node pool node in a kubernetes cluster` + nodeRecycleExample = ` + # Full example + vultr-cli kubernetes node-pool node recycle ffd31f18-5f77-454c-9065-212f942c3c35 + + # Shortened with alias commands + vultr-cli k n node r ffd31f18-5f77-454c-9065-212f942c3c35 + ` +) + +const ( + kubeconfigFilePermission = 0600 + kubeconfigDirPermission = 0755 +) + +// NewCmdKubernetes provides the CLI command for VKE functions +func NewCmdKubernetes(base *cli.Base) *cobra.Command { //nolint:funlen,gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "kubernetes", + Aliases: []string{"k"}, + Short: "Access kubernetes cluster commands", + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "List kubernetes clusters", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + summarize, errSu := cmd.Flags().GetBool("summarize") + if errSu != nil { + return fmt.Errorf("error parsing flag 'summarize' for kubernetes list : %v", errSu) + } + + k8s, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving kubernetes clusters list : %v", err) + } + + var data printer.ResourceOutput + if summarize { + data = &ClustersSummaryPrinter{Clusters: k8s, Meta: meta} + } else { + data = &ClustersPrinter{Clusters: k8s, Meta: meta} + } + + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + list.Flags().BoolP("summarize", "", false, "(optional) Summarize the list output. One line per cluster.") + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "Retrieves a kubernetes cluster", + Long: getLong, + Example: getExample, + Aliases: []string{"g"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a cluster ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + k8, err := o.get() + if err != nil { + return fmt.Errorf("error retrieving kubernetes cluster : %v", err) + } + + data := &ClusterPrinter{Cluster: k8} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "Create kubernetes cluster", + Long: createLong, + Example: createExample, + Aliases: []string{"c"}, + RunE: func(cmd *cobra.Command, args []string) error { + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for kubernetes cluster create : %v", errLa) + } + + region, errRe := cmd.Flags().GetString("region") + if errRe != nil { + return fmt.Errorf("error parsing flag 'region' for kubernetes cluster create : %v", errRe) + } + + nodepools, errNP := cmd.Flags().GetStringArray("node-pools") + if errNP != nil { + return fmt.Errorf("error parsing flag 'node-pools' for kubernetes cluster create : %v", errNP) + } + + version, errVe := cmd.Flags().GetString("version") + if errVe != nil { + return fmt.Errorf("error parsing flag 'version' for kubernetes cluster create : %v", errVe) + } + + ha, errHi := cmd.Flags().GetBool("high-avail") + if errHi != nil { + return fmt.Errorf("error parsing flag 'high-avail' for kubernetes cluster create : %v", errHi) + } + + nps, errFm := formatNodePools(nodepools) + if errFm != nil { + return fmt.Errorf("error in node pool formating : %v", errFm) + } + + o.CreateReq = &govultr.ClusterReq{ + Label: label, + Region: region, + NodePools: nps, + Version: version, + HAControlPlanes: ha, + } + + k8, err := o.create() + if err != nil { + return fmt.Errorf("error creating kubernetes cluster : %v", err) + } + + data := &ClusterPrinter{Cluster: k8} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + create.Flags().StringP("label", "l", "", "label for your kubernetes cluster") + if err := create.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking kubernetes create 'label' flag required: %v", err) + os.Exit(1) + } + + create.Flags().StringP("region", "r", "", "region you want your kubernetes cluster to be located in") + if err := create.MarkFlagRequired("region"); err != nil { + fmt.Printf("error marking kubernetes create 'region' flag required: %v", err) + os.Exit(1) + } + + create.Flags().StringP("version", "v", "", "the kubernetes version you want for your cluster") + if err := create.MarkFlagRequired("version"); err != nil { + fmt.Printf("error marking kubernetes create 'version' flag required: %v", err) + os.Exit(1) + } + + create.Flags().Bool( + "high-avail", + false, + `(optional, default false) whether or not the cluster should be deployed with multiple, +highly available, control planes`, + ) + + create.Flags().StringArrayP( + "node-pools", + "n", + []string{}, + `a comma-separated, key-value pair list of node pools. At least one node pool is required. At least one node is +required in node pool. Use / between each new node pool. E.g: +'plan:vhf-8c-32gb,label:mynodepool,tag:my-tag,quantity:3/plan:vhf-8c-32gb,label:mynodepool2,quantity:3`, + ) + if err := create.MarkFlagRequired("node-pools"); err != nil { + fmt.Printf("error marking kubernetes create 'ns-primary' flag required: %v", err) + os.Exit(1) + } + + // Update + update := &cobra.Command{ + Use: "update ", + Short: "Updates a kubernetes cluster", + Aliases: []string{"u"}, + Long: updateLong, + Example: updateExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a cluster ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for kubernetes cluster update : %v", errLa) + } + + o.UpdateReq = &govultr.ClusterReqUpdate{ + Label: label, + } + + if err := o.update(); err != nil { + return fmt.Errorf("error updating kubernetes cluster : %v", err) + } + + o.Base.Printer.Display(printer.Info("Kubernetes cluster has been updated"), nil) + + return nil + }, + } + + update.Flags().StringP("label", "l", "", "label for your kubernetes cluster") + if err := update.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking kubernetes update 'label' flag required: %v", err) + os.Exit(1) + } + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "Delete a kubernetes cluster", + Aliases: []string{"destroy", "d"}, + Long: deleteLong, + Example: deleteExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a cluster ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + withRes, errRe := cmd.Flags().GetBool("delete-resources") + if errRe != nil { + return fmt.Errorf("error parsing flag 'delete-resource' for kubernetes cluster delete: %v", errRe) + } + + if withRes { + if err := o.delWithRes(); err != nil { + return fmt.Errorf("error deleting kubernetes cluster and resources : %v", err) + } + } else { + if err := o.del(); err != nil { + return fmt.Errorf("error deleting kubernetes cluster : %v", err) + } + } + + o.Base.Printer.Display(printer.Info("Kubernetes cluster has been deleted"), nil) + + return nil + }, + } + + del.Flags().BoolP("delete-resources", "r", false, "delete a kubernetes cluster and related resources") + + // Config + config := &cobra.Command{ + Use: "config ", + Short: "Get a kubernetes cluster's config", + Long: getConfigLong, + Example: getConfigExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a clusterID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + path, errPa := cmd.Flags().GetString("output-file") + if errPa != nil { + return fmt.Errorf("error parsing flag 'output-file' for kubernetes cluster config : %v", errPa) + } + + config, err := o.config() + if err != nil { + return fmt.Errorf("error retrieving kubernetes cluster config : %v", err) + } + + if path != "" { + dir := filepath.Dir(path) + if errDi := os.MkdirAll(dir, kubeconfigDirPermission); errDi != nil { + return fmt.Errorf("error creating directory for kubeconfig : %v", errDi) + } + + kubeConfigData, errDe := base64.StdEncoding.DecodeString(config.KubeConfig) + if errDe != nil { + return fmt.Errorf("error decoding kubeconfig : %v", errDe) + } + + if errWr := os.WriteFile(path, kubeConfigData, kubeconfigFilePermission); errWr != nil { + return fmt.Errorf("error writing kubeconfig to %s : %v", path, errWr) + } + } else { + data := &ConfigPrinter{Config: config} + o.Base.Printer.Display(data, nil) + } + + return nil + }, + } + + config.Flags().StringP("output-file", "o", "", "(optional) the file path to write kubeconfig to") + + // Versions + versions := &cobra.Command{ + Use: "versions", + Short: "List supported kubernetes versions", + Long: getVersionsLong, + Example: getVersionsExample, + Aliases: []string{"v"}, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + // override parent pre-run auth check + utils.SetOptions(o.Base, cmd, args) + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + versions, err := o.versions() + if err != nil { + return fmt.Errorf("error retrieving list of kubernetes versions : %v", err) + } + + data := &VersionsPrinter{Versions: versions.Versions} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Upgrades + upgrades := &cobra.Command{ + Use: "upgrades", + Aliases: []string{"upgrade", "e"}, + Short: `Commands for kubernetes version upgrades`, + Long: upgradesLong, + Example: upgradesExample, + } + + // Upgrades List + upgradesList := &cobra.Command{ + Use: "list ", + Short: "Get available upgrades for a cluster", + Long: getUpgradesLong, + Example: getUpgradesExample, + Aliases: []string{"l"}, + RunE: func(cmd *cobra.Command, args []string) error { + upgrades, err := o.upgrades() + if err != nil { + return fmt.Errorf("error retrieving the available kubernetes upgrades : %v", err) + } + + data := &UpgradesPrinter{Upgrades: upgrades} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Upgrade start + upgradeStart := &cobra.Command{ + Use: "start ", + Short: "Perform upgrade on a cluster", + Long: upgradeLong, + Example: upgradeExample, + Aliases: []string{"s"}, + RunE: func(cmd *cobra.Command, args []string) error { + version, errVe := cmd.Flags().GetString("version") + if errVe != nil { + return fmt.Errorf("error parsing flag 'version' for kubernetes upgrade start : %v", errVe) + } + + o.UpgradeReq = &govultr.ClusterUpgradeReq{ + UpgradeVersion: version, + } + + if err := o.upgrade(); err != nil { + return fmt.Errorf("error starting the kubernetes upgrade : %v", err) + } + + o.Base.Printer.Display(printer.Info("Kubernetes upgrade has been intiated"), nil) + + return nil + }, + } + + upgradeStart.Flags().StringP("version", "v", "", "the version to upgrade the cluster to") + if err := upgradeStart.MarkFlagRequired("version"); err != nil { + fmt.Printf("error marking kubernetes upgrade 'version' flag required: %v", err) + os.Exit(1) + } + + upgrades.AddCommand( + upgradesList, + upgradeStart, + ) + + // Node Pools + nodepool := &cobra.Command{ + Use: "node-pool", + Aliases: []string{"n"}, + Short: "Commands for kubernetes cluster node pools", + Long: nodepoolLong, + Example: nodepoolExample, + } + + // Node Pools List + npList := &cobra.Command{ + Use: "list ", + Short: "List node pools", + Aliases: []string{"l"}, + Long: listNPLong, + Example: listNPExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a cluster ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + nps, meta, err := o.nodePools() + if err != nil { + return fmt.Errorf("error getting node pool list : %v", err) + } + + data := &NodePoolsPrinter{NodePools: nps, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + npList.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + npList.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + "(optional) Number of items requested per page. Default is 100 and Max is 500.", + ) + + // Node Pool Get + npGet := &cobra.Command{ + Use: "get ", + Short: "Get a node pool in kubernetes cluster", + Aliases: []string{"g"}, + Long: getNPLong, + Example: getNPExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a cluster ID and node pool ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + np, err := o.nodePool() + if err != nil { + return fmt.Errorf("error getting node pool : %v", err) + } + + data := &NodePoolPrinter{NodePool: np} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Node Pool Create + npCreate := &cobra.Command{ + Use: "create ", + Short: "Create a node pool in a kubernetes cluster", + Aliases: []string{"c"}, + Long: createNPLong, + Example: createNPExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a cluster ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + quantity, errQu := cmd.Flags().GetInt("quantity") + if errQu != nil { + return fmt.Errorf("error parsing flag 'quantity' for kubernetes cluster node pool create : %v", errQu) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for kubernetes cluster node pool create : %v", errLa) + } + + tag, errTa := cmd.Flags().GetString("tag") + if errTa != nil { + return fmt.Errorf("error parsing flag 'tag' for kubernetes cluster node pool create : %v", errTa) + } + + plan, errPl := cmd.Flags().GetString("plan") + if errPl != nil { + return fmt.Errorf("error parsing flag 'plan' for kubernetes cluster node pool create : %v", errPl) + } + + autoscaler, errAu := cmd.Flags().GetBool("auto-scaler") + if errAu != nil { + return fmt.Errorf("error parsing flag 'auto-scaler' for kubernetes cluster node pool create : %v", errAu) + } + + minNodes, errMi := cmd.Flags().GetInt("min-nodes") + if errMi != nil { + return fmt.Errorf("error parsing flag 'min-nodes' for kubernetes cluster node pool create : %v", errMi) + } + + maxNodes, errMa := cmd.Flags().GetInt("max-nodes") + if errMa != nil { + return fmt.Errorf("error parsing flag 'max-nodes' for kubernetes cluster node pool create : %v", errMa) + } + + o.npCreateReq = &govultr.NodePoolReq{ + NodeQuantity: quantity, + Label: label, + Plan: plan, + Tag: tag, + AutoScaler: govultr.BoolToBoolPtr(false), + MinNodes: minNodes, + MaxNodes: maxNodes, + } + + if autoscaler { + o.npCreateReq.AutoScaler = govultr.BoolToBoolPtr(true) + } + + np, err := o.nodePoolCreate() + if err != nil { + return fmt.Errorf("error creating kubernetes cluster node pool : %v", err) + } + + data := &NodePoolPrinter{NodePool: np} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + npCreate.Flags().StringP("label", "l", "", "label you want for your node pool.") + if err := npCreate.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking kubernetes node-pool create 'label' flag required: %v\n", err) + os.Exit(1) + } + + npCreate.Flags().StringP("tag", "t", "", "tag you want for your node pool.") + + npCreate.Flags().StringP("plan", "p", "", "the plan you want for your node pool.") + if err := npCreate.MarkFlagRequired("plan"); err != nil { + fmt.Printf("error marking kubernetes node-pool create 'plan' flag required: %v\n", err) + os.Exit(1) + } + + npCreate.Flags().IntP("quantity", "q", 1, "Number of nodes in your node pool. Note that at least one node is required for a node pool.") + if err := npCreate.MarkFlagRequired("quantity"); err != nil { + fmt.Printf("error marking kubernetes node-pool create 'quantity' flag required: %v\n", err) + os.Exit(1) + } + + npCreate.Flags().BoolP("auto-scaler", "", false, "Enable the auto scaler with your cluster") + npCreate.Flags().IntP("min-nodes", "", 1, "Minimum nodes for auto scaler") + npCreate.Flags().IntP("max-nodes", "", 1, "Maximum nodes for auto scaler") + + // Node Pool Update + npUpdate := &cobra.Command{ + Use: "update ", + Short: "Update a cluster's node pool", + Aliases: []string{"u"}, + Long: updateNPLong, + Example: updateNPExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a cluster ID and node pool ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + quantity, errQu := cmd.Flags().GetInt("quantity") + if errQu != nil { + return fmt.Errorf("error parsing flag 'quantity' for kubernetes cluster node pool update : %v", errQu) + } + + tag, errTa := cmd.Flags().GetString("tag") + if errTa != nil { + return fmt.Errorf("error parsing flag 'tag' for kubernetes cluster node pool update : %v", errTa) + } + + autoscaler, errAu := cmd.Flags().GetBool("auto-scaler") + if errAu != nil { + return fmt.Errorf("error parsing flag 'auto-scaler' for kubernetes cluster node pool update : %v", errAu) + } + + minNodes, errMi := cmd.Flags().GetInt("min-nodes") + if errMi != nil { + return fmt.Errorf("error parsing flag 'min-nodes' for kubernetes cluster node pool update : %v", errMi) + } + + maxNodes, errMa := cmd.Flags().GetInt("max-nodes") + if errMa != nil { + return fmt.Errorf("error parsing flag 'max-nodes' for kubernetes cluster node pool update : %v", errMa) + } + + o.npUpdateReq = &govultr.NodePoolReqUpdate{} + + if cmd.Flags().Changed("quantity") { + o.npUpdateReq.NodeQuantity = quantity + } + + if cmd.Flags().Changed("auto-scaler") { + o.npUpdateReq.AutoScaler = govultr.BoolToBoolPtr(autoscaler) + } + + if cmd.Flags().Changed("tag") { + o.npUpdateReq.Tag = govultr.StringToStringPtr(tag) + } + + if cmd.Flags().Changed("min-nodes") { + o.npUpdateReq.MinNodes = minNodes + } + + if cmd.Flags().Changed("max-nodes") { + o.npUpdateReq.MaxNodes = maxNodes + } + + np, err := o.nodePoolUpdate() + if err != nil { + return fmt.Errorf("error updating kubernetes cluster node pool : %v", err) + } + + data := &NodePoolPrinter{NodePool: np} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + npUpdate.Flags().IntP( + "quantity", + "q", + 1, + "Number of nodes in your node pool. Note that at least one node is required for a node pool.", + ) + npUpdate.Flags().StringP("tag", "t", "", "The tag the node pool") + npUpdate.Flags().BoolP("auto-scaler", "", false, "Enable the auto scaler with your cluster") + npUpdate.Flags().IntP("min-nodes", "", 1, "Minimum nodes for auto scaler") + npUpdate.Flags().IntP("max-nodes", "", 1, "Maximum nodes for auto scaler") + + npUpdate.MarkFlagsOneRequired("quantity", "tag", "auto-scaler", "min-nodes", "max-nodes") + + // Node Pool Delete + npDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete a cluster node pool", + Aliases: []string{"destroy", "d"}, + Long: deleteNPLong, + Example: deleteNPExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a cluster ID and node pool ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.nodePoolDelete(); err != nil { + return fmt.Errorf("error deleting kubernetes cluster node pool : %v", err) + } + + o.Base.Printer.Display(printer.Info("Kubernetes node pool has been deleted"), nil) + + return nil + }, + } + + // Node + node := &cobra.Command{ + Use: "node", + Short: "delete/recycle instances in a cluster's node pool", + Long: nodeLong, + Example: nodeExample, + } + + // Node Pool Node Delete + nodeDelete := &cobra.Command{ + Use: "delete ", + Short: "Delete a node in a cluster node pool", + Aliases: []string{"destroy", "d"}, + Long: nodeDeleteLong, + Example: nodeDeleteExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 3 { + return errors.New("please provide a cluster ID, a node pool ID and a node ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.nodePoolNodeDelete(); err != nil { + return fmt.Errorf("error deleting kubernetes cluster node pool node : %v", err) + } + + o.Base.Printer.Display(printer.Info("Kubernetes node pool node has been deleted"), nil) + + return nil + }, + } + + // Node Pool Node Recycle + nodeRecycle := &cobra.Command{ + Use: "recycle ", + Short: "Recycle a node in a cluster node pool", + Aliases: []string{"r"}, + Long: nodeRecycleLong, + Example: nodeRecycleExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 3 { + return errors.New("please provide a cluster ID, a node pool ID and a node ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.nodePoolNodeRecycle(); err != nil { + return fmt.Errorf("error recycling kubernetes cluster node pool node : %v", err) + } + + o.Base.Printer.Display(printer.Info("Kubernetes node pool node has been recycled"), nil) + + return nil + }, + } + + node.AddCommand( + nodeDelete, + nodeRecycle, + ) + + nodepool.AddCommand( + npList, + npGet, + npCreate, + npUpdate, + npDelete, + node, + ) + + cmd.AddCommand( + list, + get, + create, + update, + del, + config, + nodepool, + versions, + upgrades, + ) + + return cmd +} + +// formatNodePools parses node pools into proper format +func formatNodePools(nodePools []string) ([]govultr.NodePoolReq, error) { + var formattedList []govultr.NodePoolReq + npList := strings.Split(nodePools[0], "/") + + for _, r := range npList { + nodeData := strings.Split(r, ",") + + if len(nodeData) < 3 || len(nodeData) > 7 { + return nil, fmt.Errorf( + `unable to format node pool. each node pool must include label, quantity, and plan. + Optionally you can include tag, auto-scaler, min-nodes and max-nodes`, + ) + } + + formattedNodeData, errFormat := formatNodeData(nodeData) + if errFormat != nil { + return nil, errFormat + } + + formattedList = append(formattedList, *formattedNodeData) + } + + return formattedList, nil +} + +// formatNodeData loops over the parse strings for a node and returns the formatted struct +func formatNodeData(node []string) (*govultr.NodePoolReq, error) { + nodeData := &govultr.NodePoolReq{} + for _, f := range node { + nodeDataKeyVal := strings.Split(f, ":") + + if len(nodeDataKeyVal) != 2 && len(nodeDataKeyVal) != 3 { + return nil, fmt.Errorf("invalid node pool format") + } + + field := nodeDataKeyVal[0] + val := nodeDataKeyVal[1] + + switch { + case field == "plan": + nodeData.Plan = val + case field == "quantity": + port, err := strconv.Atoi(val) + if err != nil { + return nil, fmt.Errorf("invalid value for node pool quantity: %v", err) + } + nodeData.NodeQuantity = port + case field == "label": + nodeData.Label = val + case field == "tag": + nodeData.Tag = val + case field == "auto-scaler": + v, err := strconv.ParseBool(val) + if err != nil { + return nil, fmt.Errorf("invalid value for node pool auto-scaler: %v", err) + } + nodeData.AutoScaler = govultr.BoolToBoolPtr(v) + case field == "min-nodes": + v, err := strconv.Atoi(val) + if err != nil { + return nil, fmt.Errorf("invalid value for node pool min-nodes: %v", err) + } + nodeData.MinNodes = v + case field == "max-nodes": + v, err := strconv.Atoi(val) + if err != nil { + return nil, fmt.Errorf("invalid value for max-nodes: %v", err) + } + nodeData.MaxNodes = v + } + } + + return nodeData, nil +} + +type options struct { + Base *cli.Base + CreateReq *govultr.ClusterReq + UpdateReq *govultr.ClusterReqUpdate + UpgradeReq *govultr.ClusterUpgradeReq + npCreateReq *govultr.NodePoolReq + npUpdateReq *govultr.NodePoolReqUpdate +} + +func (o *options) list() ([]govultr.Cluster, *govultr.Meta, error) { + k8s, meta, _, err := o.Base.Client.Kubernetes.ListClusters(o.Base.Context, o.Base.Options) + return k8s, meta, err +} + +func (o *options) get() (*govultr.Cluster, error) { + k8, _, err := o.Base.Client.Kubernetes.GetCluster(o.Base.Context, o.Base.Args[0]) + return k8, err +} + +func (o *options) create() (*govultr.Cluster, error) { + k8, _, err := o.Base.Client.Kubernetes.CreateCluster(o.Base.Context, o.CreateReq) + return k8, err +} + +func (o *options) update() error { + return o.Base.Client.Kubernetes.UpdateCluster(o.Base.Context, o.Base.Args[0], o.UpdateReq) +} + +func (o *options) del() error { + return o.Base.Client.Kubernetes.DeleteCluster(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) delWithRes() error { + return o.Base.Client.Kubernetes.DeleteClusterWithResources(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) config() (*govultr.KubeConfig, error) { + kc, _, err := o.Base.Client.Kubernetes.GetKubeConfig(o.Base.Context, o.Base.Args[0]) + return kc, err +} + +func (o *options) versions() (*govultr.Versions, error) { + versions, _, err := o.Base.Client.Kubernetes.GetVersions(o.Base.Context) + return versions, err +} + +func (o *options) upgrades() ([]string, error) { + ups, _, err := o.Base.Client.Kubernetes.GetUpgrades(o.Base.Context, o.Base.Args[0]) + return ups, err +} + +func (o *options) upgrade() error { + return o.Base.Client.Kubernetes.Upgrade(o.Base.Context, o.Base.Args[0], o.UpgradeReq) +} + +func (o *options) nodePools() ([]govultr.NodePool, *govultr.Meta, error) { + nps, meta, _, err := o.Base.Client.Kubernetes.ListNodePools(o.Base.Context, o.Base.Args[0], o.Base.Options) + return nps, meta, err +} + +func (o *options) nodePool() (*govultr.NodePool, error) { + np, _, err := o.Base.Client.Kubernetes.GetNodePool(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) + return np, err +} + +func (o *options) nodePoolCreate() (*govultr.NodePool, error) { + np, _, err := o.Base.Client.Kubernetes.CreateNodePool(o.Base.Context, o.Base.Args[0], o.npCreateReq) + return np, err +} + +func (o *options) nodePoolUpdate() (*govultr.NodePool, error) { + np, _, err := o.Base.Client.Kubernetes.UpdateNodePool(o.Base.Context, o.Base.Args[0], o.Base.Args[1], o.npUpdateReq) + return np, err +} + +func (o *options) nodePoolDelete() error { + return o.Base.Client.Kubernetes.DeleteNodePool(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) nodePoolNodeDelete() error { + return o.Base.Client.Kubernetes.DeleteNodePoolInstance(o.Base.Context, o.Base.Args[0], o.Base.Args[1], o.Base.Args[2]) +} + +func (o *options) nodePoolNodeRecycle() error { + return o.Base.Client.Kubernetes.RecycleNodePoolInstance(o.Base.Context, o.Base.Args[0], o.Base.Args[1], o.Base.Args[2]) +} diff --git a/cmd/kubernetes/printer.go b/cmd/kubernetes/printer.go new file mode 100644 index 00000000..e567e5bc --- /dev/null +++ b/cmd/kubernetes/printer.go @@ -0,0 +1,504 @@ +package kubernetes + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// ClustersSummaryPrinter ... +type ClustersSummaryPrinter struct { + Clusters []govultr.Cluster `json:"vke_clusters"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (c *ClustersSummaryPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ClustersSummaryPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ClustersSummaryPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "LABEL", + "STATUS", + "REGION", + "VERSION", + "NODEPOOL#", + "NODE#", + }} +} + +// Data ... +func (c *ClustersSummaryPrinter) Data() [][]string { + if len(c.Clusters) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range c.Clusters { + nodePoolCount := len(c.Clusters[i].NodePools) + var nodeCount int = 0 + + for j := range c.Clusters[i].NodePools { + nodeCount += len(c.Clusters[i].NodePools[j].Nodes) + } + + data = append(data, []string{ + c.Clusters[i].ID, + c.Clusters[i].Label, + c.Clusters[i].Status, + c.Clusters[i].Region, + c.Clusters[i].Version, + strconv.Itoa(nodePoolCount), + strconv.Itoa(nodeCount), + }) + } + + return data +} + +// Paging ... +func (c *ClustersSummaryPrinter) Paging() [][]string { + return printer.NewPaging(c.Meta.Total, &c.Meta.Links.Next, &c.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ClustersPrinter ... +type ClustersPrinter struct { + Clusters []govultr.Cluster `json:"vke_clusters"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (c *ClustersPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ClustersPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ClustersPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (c *ClustersPrinter) Data() [][]string { + if len(c.Clusters) == 0 { + return [][]string{0: {"No active kubernetes clusters"}} + } + + var data [][]string + for i := range c.Clusters { + data = append(data, + []string{"---------------------------"}, + []string{"ID", c.Clusters[i].ID}, + []string{"LABEL", c.Clusters[i].Label}, + []string{"DATE CREATED", c.Clusters[i].DateCreated}, + []string{"CLUSTER SUBNET", c.Clusters[i].ClusterSubnet}, + []string{"SERVICE SUBNET", c.Clusters[i].ServiceSubnet}, + []string{"IP", c.Clusters[i].IP}, + []string{"ENDPOINT", c.Clusters[i].Endpoint}, + []string{"HIGH AVAIL", strconv.FormatBool(c.Clusters[i].HAControlPlanes)}, + []string{"VERSION", c.Clusters[i].Version}, + []string{"REGION", c.Clusters[i].Region}, + []string{"STATUS", c.Clusters[i].Status}, + []string{" "}, + []string{"NODE POOLS"}, + ) + + for j := range c.Clusters[i].NodePools { + data = append(data, + []string{"ID", c.Clusters[i].NodePools[j].ID}, + []string{"DATE CREATED", c.Clusters[i].NodePools[j].DateCreated}, + []string{"DATE UPDATED", c.Clusters[i].NodePools[j].DateUpdated}, + []string{"LABEL", c.Clusters[i].NodePools[j].Label}, + []string{"TAG", c.Clusters[i].NodePools[j].Tag}, + []string{"PLAN", c.Clusters[i].NodePools[j].Plan}, + []string{"STATUS", c.Clusters[i].NodePools[j].Status}, + []string{"NODE QUANTITY", strconv.Itoa(c.Clusters[i].NodePools[j].NodeQuantity)}, + []string{"AUTO SCALER", strconv.FormatBool(c.Clusters[i].NodePools[j].AutoScaler)}, + []string{"MIN NODES", strconv.Itoa(c.Clusters[i].NodePools[j].MinNodes)}, + []string{"MAX NODES", strconv.Itoa(c.Clusters[i].NodePools[j].MaxNodes)}, + []string{" "}, + []string{"NODES"}, + ) + + if len(c.Clusters[i].NodePools[j].Nodes) != 0 { + // Shouldn't ever be zero + data = append(data, []string{"ID", "DATE CREATED", "LABEL", "STATUS"}) + } + + for k := range c.Clusters[i].NodePools[j].Nodes { + data = append(data, + []string{ + c.Clusters[i].NodePools[j].Nodes[k].ID, + c.Clusters[i].NodePools[j].Nodes[k].DateCreated, + c.Clusters[i].NodePools[j].Nodes[k].Label, + c.Clusters[i].NodePools[j].Nodes[k].Status, + }, + ) + } + + data = append(data, []string{" "}) + } + } + + return data +} + +// Paging ... +func (c *ClustersPrinter) Paging() [][]string { + return printer.NewPaging(c.Meta.Total, &c.Meta.Links.Next, &c.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ClusterPrinter ... +type ClusterPrinter struct { + Cluster *govultr.Cluster `json:"vke_cluster"` +} + +// JSON ... +func (c *ClusterPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ClusterPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ClusterPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (c *ClusterPrinter) Data() [][]string { + var data [][]string + data = append(data, + []string{"ID", c.Cluster.ID}, + []string{"LABEL", c.Cluster.Label}, + []string{"DATE CREATED", c.Cluster.DateCreated}, + []string{"CLUSTER SUBNET", c.Cluster.ClusterSubnet}, + []string{"SERVICE SUBNET", c.Cluster.ServiceSubnet}, + []string{"IP", c.Cluster.IP}, + []string{"ENDPOINT", c.Cluster.Endpoint}, + []string{"HIGH AVAIL", strconv.FormatBool(c.Cluster.HAControlPlanes)}, + []string{"VERSION", c.Cluster.Version}, + []string{"REGION", c.Cluster.Region}, + []string{"STATUS", c.Cluster.Status}, + []string{" "}, + []string{"NODE POOLS"}, + ) + + for i := range c.Cluster.NodePools { + data = append(data, + []string{"ID", c.Cluster.NodePools[i].ID}, + []string{"DATE CREATED", c.Cluster.NodePools[i].DateCreated}, + []string{"DATE UPDATED", c.Cluster.NodePools[i].DateUpdated}, + []string{"LABEL", c.Cluster.NodePools[i].Label}, + []string{"TAG", c.Cluster.NodePools[i].Tag}, + []string{"PLAN", c.Cluster.NodePools[i].Plan}, + []string{"STATUS", c.Cluster.NodePools[i].Status}, + []string{"NODE QUANTITY", strconv.Itoa(c.Cluster.NodePools[i].NodeQuantity)}, + []string{"AUTO SCALER", strconv.FormatBool(c.Cluster.NodePools[i].AutoScaler)}, + []string{"MIN NODES", strconv.Itoa(c.Cluster.NodePools[i].MinNodes)}, + []string{"MAX NODES", strconv.Itoa(c.Cluster.NodePools[i].MaxNodes)}, + []string{" "}, + []string{"NODES"}, + ) + + if len(c.Cluster.NodePools[i].Nodes) != 0 { + // Shouldn't ever be zero + data = append(data, []string{"ID", "DATE CREATED", "LABEL", "STATUS"}) + } + + for j := range c.Cluster.NodePools[i].Nodes { + data = append(data, + []string{ + c.Cluster.NodePools[i].Nodes[j].ID, + c.Cluster.NodePools[i].Nodes[j].DateCreated, + c.Cluster.NodePools[i].Nodes[j].Label, + c.Cluster.NodePools[i].Nodes[j].Status, + }, + ) + } + + data = append(data, []string{" "}) + } + + return data +} + +// Paging ... +func (c *ClusterPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// NodePoolsPrinter ... +type NodePoolsPrinter struct { + NodePools []govultr.NodePool `json:"node_pools"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (n *NodePoolsPrinter) JSON() []byte { + return printer.MarshalObject(n, "json") +} + +// YAML ... +func (n *NodePoolsPrinter) YAML() []byte { + return printer.MarshalObject(n, "yaml") +} + +// Columns ... +func (n *NodePoolsPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (n *NodePoolsPrinter) Data() [][]string { + if len(n.NodePools) == 0 { + // this shouldn't be possible since at least one nodepool is required + return [][]string{0: {"No active nodepools on cluster"}} + } + + var data [][]string + for i := range n.NodePools { + data = append(data, + []string{"---------------------------"}, + []string{"ID", n.NodePools[i].ID}, + []string{"DATE CREATED", n.NodePools[i].DateCreated}, + []string{"DATE UPDATED", n.NodePools[i].DateUpdated}, + []string{"LABEL", n.NodePools[i].Label}, + []string{"TAG", n.NodePools[i].Tag}, + []string{"PLAN", n.NodePools[i].Plan}, + []string{"STATUS", n.NodePools[i].Status}, + []string{"NODE QUANTITY", strconv.Itoa(n.NodePools[i].NodeQuantity)}, + []string{"AUTO SCALER", strconv.FormatBool(n.NodePools[i].AutoScaler)}, + []string{"MIN NODES", strconv.Itoa(n.NodePools[i].MinNodes)}, + []string{"MAX NODES", strconv.Itoa(n.NodePools[i].MaxNodes)}, + []string{" "}, + []string{"NODES"}, + ) + + if len(n.NodePools[i].Nodes) != 0 { + // Shouldn't ever be zero + data = append(data, []string{"ID", "DATE CREATED", "LABEL", "STATUS"}) + } + + for j := range n.NodePools[i].Nodes { + data = append(data, + []string{ + n.NodePools[i].Nodes[j].ID, + n.NodePools[i].Nodes[j].DateCreated, + n.NodePools[i].Nodes[j].Label, + n.NodePools[i].Nodes[j].Status, + }, + ) + } + } + + return data +} + +// Paging ... +func (n *NodePoolsPrinter) Paging() [][]string { + return printer.NewPaging(n.Meta.Total, &n.Meta.Links.Next, &n.Meta.Links.Prev).Compose() +} + +// ====================================== + +// NodePoolPrinter ... +type NodePoolPrinter struct { + NodePool *govultr.NodePool `json:"node_pool"` +} + +// JSON ... +func (n *NodePoolPrinter) JSON() []byte { + return printer.MarshalObject(n, "json") +} + +// YAML ... +func (n *NodePoolPrinter) YAML() []byte { + return printer.MarshalObject(n, "yaml") +} + +// Columns ... +func (n *NodePoolPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (n *NodePoolPrinter) Data() [][]string { + var data [][]string + data = append(data, + []string{"ID", n.NodePool.ID}, + []string{"DATE CREATED", n.NodePool.DateCreated}, + []string{"DATE UPDATED", n.NodePool.DateUpdated}, + []string{"LABEL", n.NodePool.Label}, + []string{"TAG", n.NodePool.Tag}, + []string{"PLAN", n.NodePool.Plan}, + []string{"STATUS", n.NodePool.Status}, + []string{"NODE QUANTITY", strconv.Itoa(n.NodePool.NodeQuantity)}, + []string{"AUTO SCALER", strconv.FormatBool(n.NodePool.AutoScaler)}, + []string{"MIN NODES", strconv.Itoa(n.NodePool.MinNodes)}, + []string{"MAX NODES", strconv.Itoa(n.NodePool.MaxNodes)}, + []string{" "}, + []string{"NODES"}, + ) + + if len(n.NodePool.Nodes) != 0 { + // Shouldn't ever be zero + data = append(data, []string{"ID", "DATE CREATED", "LABEL", "STATUS"}) + } + + for i := range n.NodePool.Nodes { + data = append(data, + []string{ + n.NodePool.Nodes[i].ID, + n.NodePool.Nodes[i].DateCreated, + n.NodePool.Nodes[i].Label, + n.NodePool.Nodes[i].Status, + }, + ) + } + + return data +} + +// Paging ... +func (n *NodePoolPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// VersionsPrinter ... +type VersionsPrinter struct { + Versions []string `json:"versions"` +} + +// JSON ... +func (v *VersionsPrinter) JSON() []byte { + return printer.MarshalObject(v, "json") +} + +// YAML ... +func (v *VersionsPrinter) YAML() []byte { + return printer.MarshalObject(v, "yaml") +} + +// Columns ... +func (v *VersionsPrinter) Columns() [][]string { + return [][]string{0: {"VERSIONS"}} +} + +// Data ... +func (v *VersionsPrinter) Data() [][]string { + if len(v.Versions) == 0 { + return [][]string{0: {"---"}} + } + + var data [][]string + + for i := range v.Versions { + data = append(data, []string{v.Versions[i]}) + } + + return data +} + +// Paging ... +func (v *VersionsPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// UpgradesPrinter ... +type UpgradesPrinter struct { + Upgrades []string `json:"available_upgrades"` +} + +// JSON ... +func (u *UpgradesPrinter) JSON() []byte { + return printer.MarshalObject(u, "json") +} + +// YAML ... +func (u *UpgradesPrinter) YAML() []byte { + return printer.MarshalObject(u, "yaml") +} + +// Columns ... +func (u *UpgradesPrinter) Columns() [][]string { + return [][]string{0: {"UPGRADES"}} +} + +// Data ... +func (u *UpgradesPrinter) Data() [][]string { + if len(u.Upgrades) == 0 { + return [][]string{0: {"---"}} + } + + var data [][]string + + for i := range u.Upgrades { + data = append(data, []string{u.Upgrades[i]}) + } + + return data +} + +// Paging ... +func (u *UpgradesPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// ConfigPrinter ... +type ConfigPrinter struct { + Config *govultr.KubeConfig +} + +// JSON ... +func (c *ConfigPrinter) JSON() []byte { + return printer.MarshalObject(c, "json") +} + +// YAML ... +func (c *ConfigPrinter) YAML() []byte { + return printer.MarshalObject(c, "yaml") +} + +// Columns ... +func (c *ConfigPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (c *ConfigPrinter) Data() [][]string { + return [][]string{0: {c.Config.KubeConfig}} +} + +// Paging ... +func (c *ConfigPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/loadBalancer.go b/cmd/loadBalancer.go deleted file mode 100644 index 019d9220..00000000 --- a/cmd/loadBalancer.go +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright © 2020 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - "strconv" - "strings" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// LoadBalancer represents the load-balancer command -func LoadBalancer() *cobra.Command { - - lbCmd := &cobra.Command{ - Use: "load-balancer", - Aliases: []string{"lb"}, - Short: "load balancer commands", - Long: `load-balancer is used to interact with the load-balancer api`, - } - - lbCmd.AddCommand(lbCreate, lbDelete, lbGet, lbList, lbUpdate) - - // Create - lbCreate.Flags().StringP("region", "r", "", "region id you wish to have the load balancer created in") - lbCreate.MarkFlagRequired("region") - - lbCreate.Flags().StringP("balancing-algorithm", "b", "roundrobin", "(optional) balancing algorithm that determines server selection | roundrobin or leastconn") - lbCreate.Flags().StringP("ssl-redirect", "s", "", "(optional) if true, this will redirect HTTP traffic to HTTPS. You must have an HTTPS rule and SSL certificate installed on the load balancer to enable this option.") - lbCreate.Flags().StringP("proxy-protocol", "p", "", "(optional) if true, you must configure backend nodes to accept Proxy protocol.") - lbCreate.Flags().StringArrayP("forwarding-rules", "f", []string{}, "(optional) a comma-separated, key-value pair list of forwarding rules. Use - between each new rule. E.g: `frontend_port:80,frontend_protocol:http,backend_port:80,backend_protocol:http-frontend_port:81,frontend_protocol:http,backend_port:81,backend_protocol:http`") - - lbCreate.Flags().String("protocol", "http", "(optional) the protocol to use for health checks. | https, http, tcp") - lbCreate.Flags().Int("port", 80, "(optional) the port to use for health checks.") - lbCreate.Flags().String("path", "/", "(optional) HTTP Path to check. only applies if protocol is HTTP or HTTPS.") - lbCreate.Flags().IntP("check-interval", "c", 15, "(optional) interval between health checks.") - lbCreate.Flags().IntP("response-timeout", "t", 15, "(optional) timeout before health check fails.") - lbCreate.Flags().IntP("unhealthy-threshold", "u", 15, "(optional) number times a check must fail before becoming unhealthy.") - lbCreate.Flags().Int("healthy-threshold", 15, "(optional) number times a check must succeed before returning to healthy status.") - - lbCreate.Flags().String("cookie-name", "", "(optional) the cookie name to make sticky.") - - lbCreate.Flags().String("private-key", "", "(optional) the private key component for a ssl certificate.") - lbCreate.Flags().String("certificate", "", "(optional) the SSL certificate.") - lbCreate.Flags().String("certificate-chain", "", "(optional) the certificate chain for a ssl certificate.") - - lbCreate.Flags().StringP("label", "l", "", "(optional) the label for your load balancer.") - lbCreate.Flags().StringSliceP("instances", "i", []string{}, "(optional) an array of instances IDs that you want attached to the load balancer.") - - // List - lbList.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") - lbList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - // Update - lbUpdate.Flags().StringP("balancing-algorithm", "b", "roundrobin", "(optional) balancing algorithm that determines server selection | roundrobin or leastconn") - lbUpdate.Flags().StringP("ssl-redirect", "s", "", "(optional) if true, this will redirect HTTP traffic to HTTPS. You must have an HTTPS rule and SSL certificate installed on the load balancer to enable this option.") - lbUpdate.Flags().StringP("proxy-protocol", "p", "", "(optional) if true, you must configure backend nodes to accept Proxy protocol.") - lbUpdate.Flags().StringArrayP("forwarding-rules", "f", []string{}, "(optional) a comma-separated, key-value pair list of forwarding rules. Use - between each new rule. E.g: `frontend_port:80,frontend_protocol:http,backend_port:80,backend_protocol:http-frontend_port:81,frontend_protocol:http,backend_port:81,backend_protocol:http`") - - lbUpdate.Flags().String("protocol", "", "(optional) the protocol to use for health checks. | https, http, tcp") - lbUpdate.Flags().Int("port", 0, "(optional) the port to use for health checks.") - lbUpdate.Flags().String("path", "", "(optional) HTTP Path to check. only applies if protocol is HTTP or HTTPS.") - lbUpdate.Flags().IntP("check-interval", "c", 0, "(optional) interval between health checks.") - lbUpdate.Flags().IntP("response-timeout", "t", 0, "(optional) timeout before health check fails.") - lbUpdate.Flags().IntP("unhealthy-threshold", "u", 0, "(optional) number times a check must fail before becoming unhealthy.") - lbUpdate.Flags().Int("healthy-threshold", 0, "(optional) number times a check must succeed before returning to healthy status.") - - lbUpdate.Flags().String("cookie-name", "", "(optional) the cookie name to make sticky.") - - lbUpdate.Flags().String("private-key", "", "(optional) the private key component for a ssl certificate.") - lbUpdate.Flags().String("certificate", "", "(optional) the SSL certificate.") - lbUpdate.Flags().String("certificate-chain", "", "(optional) the certificate chain for a ssl certificate.") - - lbUpdate.Flags().StringP("label", "l", "", "(optional) the label for your load balancer.") - lbUpdate.Flags().StringSliceP("instances", "i", []string{}, "(optional) an array of instances IDs that you want attached to the load balancer.") - - // Rules SubCommands - rulesCmd := &cobra.Command{ - Use: "rule", - Short: "create/delete/list rules for an load balancer", - Long: ``, - } - - // rule list - ruleList.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") - ruleList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - // rule create - ruleCreate.Flags().String("frontend-protocol", "http", "the protocol on the Load Balancer to forward to the backend. | HTTP, HTTPS, TCP") - ruleCreate.Flags().String("backend-protocol", "http", "the protocol destination on the backend server. | HTTP, HTTPS, TCP") - ruleCreate.Flags().Int("frontend-port", 80, "the port number on the Load Balancer to forward to the backend.") - ruleCreate.Flags().Int("backend-port", 80, "the port number destination on the backend server.") - - ruleCreate.MarkFlagRequired("frontend-protocol") - ruleCreate.MarkFlagRequired("backend-protocol") - ruleCreate.MarkFlagRequired("frontend-port") - ruleCreate.MarkFlagRequired("backend-port") - - rulesCmd.AddCommand(ruleCreate, ruleDelete, ruleGet, ruleList) - lbCmd.AddCommand(rulesCmd) - - return lbCmd -} - -var lbCreate = &cobra.Command{ - Use: "create", - Short: "create a load balancer", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - region, _ := cmd.Flags().GetString("region") - label, _ := cmd.Flags().GetString("label") - - algorithm, _ := cmd.Flags().GetString("balancing-algorithm") - sslRedirect, _ := cmd.Flags().GetString("ssl-redirect") - proxyProtocol, _ := cmd.Flags().GetString("proxy-protocol") - - fwRules, _ := cmd.Flags().GetStringArray("forwarding-rules") - - protocol, _ := cmd.Flags().GetString("protocol") - port, _ := cmd.Flags().GetInt("port") - path, _ := cmd.Flags().GetString("path") - checkInterval, _ := cmd.Flags().GetInt("check-interval") - responseTimeout, _ := cmd.Flags().GetInt("response-timeout") - unhealthyThreshold, _ := cmd.Flags().GetInt("unhealthy-threshold") - healthyThreshold, _ := cmd.Flags().GetInt("healthy-threshold") - - privateKey, _ := cmd.Flags().GetString("private-key") - certificate, _ := cmd.Flags().GetString("certificate") - certificateChain, _ := cmd.Flags().GetString("certificate-chain") - - cookieName, _ := cmd.Flags().GetString("cookie-name") - instances, _ := cmd.Flags().GetStringSlice("instances") - - healthCheck := &govultr.HealthCheck{ - Protocol: protocol, - Path: path, - Port: port, - CheckInterval: checkInterval, - ResponseTimeout: responseTimeout, - UnhealthyThreshold: unhealthyThreshold, - HealthyThreshold: healthyThreshold, - } - - ssl := &govultr.SSL{ - PrivateKey: privateKey, - Certificate: certificate, - Chain: certificateChain, - } - - options := &govultr.LoadBalancerReq{ - Region: region, - Label: label, - BalancingAlgorithm: algorithm, - HealthCheck: healthCheck, - SSL: ssl, - } - - if cookieName != "" { - options.StickySessions = &govultr.StickySessions{ - CookieName: cookieName, - } - } - - rules, err := formatFWRules(fwRules) - if err != nil { - fmt.Printf("error creating load balancer : %v\n", err) - os.Exit(1) - } - - if len(rules) > 0 { - options.ForwardingRules = rules - } - - if sslRedirect == "yes" { - options.SSLRedirect = govultr.BoolToBoolPtr(true) - } - - if proxyProtocol == "yes" { - options.ProxyProtocol = govultr.BoolToBoolPtr(true) - } - - if len(instances) > 0 { - options.Instances = instances - } - - lb, err := client.LoadBalancer.Create(context.Background(), options) - if err != nil { - fmt.Printf("error creating load balancer : %v\n", err) - os.Exit(1) - } - - printer.LoadBalancer(lb) - }, -} - -var lbDelete = &cobra.Command{ - Use: "delete ", - Short: "deletes a load balancer", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a loadBalancerID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.LoadBalancer.Delete(context.Background(), id); err != nil { - fmt.Printf("error deleting load balancer : %v\n", err) - os.Exit(1) - } - - fmt.Println("Deleted load balancer") - }, -} - -var lbGet = &cobra.Command{ - Use: "get ", - Short: "retrieves a load balancer", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a loadBalancerID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - lb, err := client.LoadBalancer.Get(context.Background(), id) - if err != nil { - fmt.Printf("error getting load balancer : %v\n", err) - os.Exit(1) - } - - printer.LoadBalancer(lb) - }, -} - -var lbList = &cobra.Command{ - Use: "list", - Short: "retrieves a list of active load balancers", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - list, meta, err := client.LoadBalancer.List(context.Background(), options) - if err != nil { - fmt.Printf("error listing load balancers : %v\n", err) - os.Exit(1) - } - - printer.LoadBalancerList(list, meta) - }, -} - -var lbUpdate = &cobra.Command{ - Use: "update ", - Short: "updates a load balancer", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a loadBalancerID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - label, _ := cmd.Flags().GetString("label") - - algorithm, _ := cmd.Flags().GetString("balancing-algorithm") - sslRedirect, _ := cmd.Flags().GetString("ssl-redirect") - proxyProtocol, _ := cmd.Flags().GetString("proxy-protocol") - cookieName, _ := cmd.Flags().GetString("cookie-name") - - fwRules, _ := cmd.Flags().GetStringArray("forwarding-rules") - - protocol, _ := cmd.Flags().GetString("protocol") - port, _ := cmd.Flags().GetInt("port") - path, _ := cmd.Flags().GetString("path") - checkInterval, _ := cmd.Flags().GetInt("check-interval") - responseTimeout, _ := cmd.Flags().GetInt("response-timeout") - unhealthyThreshold, _ := cmd.Flags().GetInt("unhealthy-threshold") - healthyThreshold, _ := cmd.Flags().GetInt("healthy-threshold") - - privateKey, _ := cmd.Flags().GetString("private-key") - certificate, _ := cmd.Flags().GetString("certificate") - certificateChain, _ := cmd.Flags().GetString("certificate-chain") - - instances, _ := cmd.Flags().GetStringSlice("instances") - - options := &govultr.LoadBalancerReq{} - - if len(fwRules) > 0 { - rules, err := formatFWRules(fwRules) - if err != nil { - fmt.Printf("error updating load balancer : %v\n", err) - os.Exit(1) - } - - if len(rules) > 0 { - options.ForwardingRules = rules - } - } - - // Health - if port != 0 || protocol != "" || path != "" || checkInterval != 0 || responseTimeout != 0 || unhealthyThreshold != 0 || healthyThreshold != 0 { - options.HealthCheck = &govultr.HealthCheck{} - } - - if port != 0 { - options.HealthCheck.Port = port - } - - if protocol != "" { - options.HealthCheck.Protocol = protocol - } - - if path != "" { - options.HealthCheck.Path = path - } - - if checkInterval != 0 { - options.HealthCheck.CheckInterval = checkInterval - } - - if responseTimeout != 0 { - options.HealthCheck.ResponseTimeout = responseTimeout - } - - if unhealthyThreshold != 0 { - options.HealthCheck.UnhealthyThreshold = unhealthyThreshold - } - - if healthyThreshold != 0 { - options.HealthCheck.HealthyThreshold = healthyThreshold - } - - // SSL - if privateKey != "" && certificate != "" { - options.SSL = &govultr.SSL{ - PrivateKey: privateKey, - Certificate: certificate, - Chain: certificateChain, - } - } - - // Generic Info - if label != "" { - options.Label = label - } - - if proxyProtocol == "yes" { - options.ProxyProtocol = govultr.BoolToBoolPtr(true) - } else if proxyProtocol == "no" { - options.ProxyProtocol = govultr.BoolToBoolPtr(false) - } - - if sslRedirect == "yes" { - options.SSLRedirect = govultr.BoolToBoolPtr(true) - } else if sslRedirect == "no" { - options.SSLRedirect = govultr.BoolToBoolPtr(false) - } - - if cookieName != "" { - options.StickySessions = &govultr.StickySessions{ - CookieName: cookieName, - } - } - - if algorithm != "" { - options.BalancingAlgorithm = algorithm - } - - if len(instances) > 0 { - options.Instances = instances - } - - if err := client.LoadBalancer.Update(context.Background(), id, options); err != nil { - fmt.Printf("error updating load balancer : %v\n", err) - os.Exit(1) - } - - fmt.Println("Updated load balancer") - }, -} - -var ruleList = &cobra.Command{ - Use: "list rule ", - Short: "lists a load balancers forwarding rules", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a loadBalancerID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - options := getPaging(cmd) - rules, meta, err := client.LoadBalancer.ListForwardingRules(context.Background(), id, options) - if err != nil { - fmt.Printf("error listing load balancer rules : %v\n", err) - os.Exit(1) - } - - printer.LoadBalancerRuleList(rules, meta) - }, -} - -var ruleCreate = &cobra.Command{ - Use: "create rule ", - Short: "creates a load balancer forwarding rule", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a loadBalancerID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - options := &govultr.ForwardingRule{} - rule, err := client.LoadBalancer.CreateForwardingRule(context.Background(), id, options) - if err != nil { - fmt.Printf("error listing load balancer rules : %v\n", err) - os.Exit(1) - } - - printer.LoadBalancerRule(rule) - }, -} - -var ruleGet = &cobra.Command{ - Use: "get rule ", - Short: "Gets a load balancer forwarding rule", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a loadBalancerID and ruleID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - ruleID := args[1] - rule, err := client.LoadBalancer.GetForwardingRule(context.Background(), id, ruleID) - if err != nil { - fmt.Printf("error getting load balancer rule : %v\n", err) - os.Exit(1) - } - - printer.LoadBalancerRule(rule) - }, -} - -var ruleDelete = &cobra.Command{ - Use: "delete rule ", - Short: "deletes a load balancer forwarding rule", - Long: ``, - Aliases: []string{"destroy"}, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errors.New("please provide a loadBalancerID and ruleID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - ruleID := args[1] - - if err := client.LoadBalancer.DeleteForwardingRule(context.Background(), id, ruleID); err != nil { - fmt.Printf("error deleting load balancer rule : %v\n", err) - os.Exit(1) - } - - fmt.Println("Deleted load balancer rule") - }, -} - -// formatFWRules parses forwarding rules into proper format -func formatFWRules(rules []string) ([]govultr.ForwardingRule, error) { - var formattedList []govultr.ForwardingRule - rulesList := strings.Split(rules[0], "-") - - for _, r := range rulesList { - rule := govultr.ForwardingRule{} - fwRule := strings.Split(r, ",") - - if len(fwRule) != 4 { - return nil, fmt.Errorf("unable to format forwarding rules. each rule must include frontend and backend ports and protocols") - } - - for _, f := range fwRule { - ruleKeyVal := strings.Split(f, ":") - - if len(ruleKeyVal) != 2 { - return nil, fmt.Errorf("invalid forwarding rule format") - } - - field := ruleKeyVal[0] - val := ruleKeyVal[1] - - switch true { - case field == "frontend_protocol": - rule.FrontendProtocol = val - case field == "frontend_port": - port, _ := strconv.Atoi(val) - rule.FrontendPort = port - case field == "backend_protocol": - rule.BackendProtocol = val - case field == "backend_port": - port, _ := strconv.Atoi(val) - rule.BackendPort = port - } - } - - formattedList = append(formattedList, rule) - } - - return formattedList, nil -} diff --git a/cmd/loadbalancer/loadbalancer.go b/cmd/loadbalancer/loadbalancer.go new file mode 100644 index 00000000..4951239f --- /dev/null +++ b/cmd/loadbalancer/loadbalancer.go @@ -0,0 +1,1086 @@ +// Package loadbalancer provides CLI command to control load balancers +package loadbalancer + +import ( + "errors" + "fmt" + "os" + "strconv" + "strings" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Get commands available to Load Balancers` + example = ` + # Full example + vultr-cli load-balancer + ` + + listLong = `Get all load balancers on your Vultr account` + listExample = ` + # Full example + vultr-cli load-balancer list + + # Full example with paging + vultr-cli load-balancer list --per-page=1 --cursor="bmV4dF9fQU1T" + + # Shortened with alias commands + vultr-cli lb l + + # Summarized view + vultr-cli load-balancer list --summarize + ` + + createLong = `Create a new Load Balancer with the desired settings` + createExample = ` + # Full example + vultr-cli load-balancer create --region="lax" --balancing-algorithm="roundrobin" --label="Example Load Balancer" \ + --port=80 --check-interval=10 --healthy-threshold=15 + + You must pass --region; other arguments are optional + + #Shortened example with aliases + vultr-cli lb c -r="lax" -b="roundrobin" -l="Example Load Balancer" -p=80 -c=10 + + #Full example with attached VPC + vultr-cli load-balancer create --region="lax" --label="Example Load Balancer with VPC" \ + --vpc="e951822b-10b2-4c5e-b333-bf38033e7175" --balancing-algorithm="leastconn" + ` + updateLong = `Update a Load Balancer with the desired settings` + updateExample = ` + # Full example + vultr-cli load-balancer update 57539f6f-66a2-4580-936b-d0af934bce5d --label="Updated Load Balancer Label" \ + --balancing-algorithm="leastconn" --unhealthy-threshold=20 + + #Shortened example with aliases + vultr-cli lb u 57539f6f-66a2-4580-936b-d0af934bce5d -l="Updated Load Balancer Label" -b="leastconn" -u=20 + + #Full example with attached VPC + vultr-cli load-balancer update 57539f6f-66a2-4580-936b-d0af934bce5d --vpc="bff36707-977e-4357-8f30-bef3339155cc" + ` +) + +// NewCmdLoadBalancer provides the CLI command for load balancers +func NewCmdLoadBalancer(base *cli.Base) *cobra.Command { //nolint:funlen,gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "load-balancer", + Short: "Load balancer commands", + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list ", + Short: "List load balancers", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + summarize, errSu := cmd.Flags().GetBool("summarize") + if errSu != nil { + return fmt.Errorf("error parsing flag 'summarize' for load balancer list : %v", errSu) + } + + lbs, meta, err := o.list() + if err != nil { + return fmt.Errorf("error getting load balancer : %v", err) + } + + var data printer.ResourceOutput + if summarize { + data = &LBsSummaryPrinter{LBs: lbs, Meta: meta} + } else { + data = &LBsPrinter{LBs: lbs, Meta: meta} + } + + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + list.Flags().BoolP("summarize", "", false, "(optional) Summarize the list output. One line per load balancer.") + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "Retrieve a load balancer", + Aliases: []string{"g"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a load balancer ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + lb, err := o.get() + if err != nil { + return fmt.Errorf("error getting load balancer : %v", err) + } + + o.Base.Printer.Display(&LBPrinter{LB: lb}, nil) + + return nil + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "Create a load balancer", + Aliases: []string{"c"}, + Long: createLong, + Example: createExample, + RunE: func(cmd *cobra.Command, args []string) error { + region, errRg := cmd.Flags().GetString("region") + if errRg != nil { + return fmt.Errorf("error parsing flag 'region' for load balancer create : %v", errRg) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for load balancer create : %v", errLa) + } + + algorithm, errAl := cmd.Flags().GetString("balancing-algorithm") + if errAl != nil { + return fmt.Errorf("error parsing flag 'balancing-algorithm' for load balancer create : %v", errAl) + } + + sslRedirect, errSs := cmd.Flags().GetBool("ssl-redirect") + if errSs != nil { + return fmt.Errorf("error parsing flag 'ssl-redirect' for load balancer create : %v", errSs) + } + + proxyProtocol, errPr := cmd.Flags().GetBool("proxy-protocol") + if errPr != nil { + return fmt.Errorf("error parsing flag 'proxy-protocol' for load balancer create : %v", errPr) + } + + cookieName, errCo := cmd.Flags().GetString("cookie-name") + if errCo != nil { + return fmt.Errorf("error parsing flag 'cookie-name' for load balancer create : %v", errCo) + } + + vpc, errVp := cmd.Flags().GetString("vpc") + if errVp != nil { + return fmt.Errorf("error parsing flag 'vpc' for load balancer create : %v", errVp) + } + + rulesInForward, errFw := cmd.Flags().GetStringArray("forwarding-rules") + if errFw != nil { + return fmt.Errorf("error parsing flag 'forwarding-rules' for load balancer create : %v", errFw) + } + + rulesInFire, errFi := cmd.Flags().GetStringArray("firewall-rules") + if errFi != nil { + return fmt.Errorf("error parsing flag 'firewall-rules' for load balancer create : %v", errFi) + } + + protocol, errPo := cmd.Flags().GetString("protocol") + if errPo != nil { + return fmt.Errorf("error parsing flag 'protocol' for load balancer create : %v", errPo) + } + + port, errPo := cmd.Flags().GetInt("port") + if errPo != nil { + return fmt.Errorf("error parsing flag 'port' for load balancer create : %v", errPo) + } + + path, errPa := cmd.Flags().GetString("path") + if errPa != nil { + return fmt.Errorf("error parsing flag 'path' for load balancer create : %v", errPa) + } + + checkInterval, errCh := cmd.Flags().GetInt("check-interval") + if errCh != nil { + return fmt.Errorf("error parsing flag 'check-interval' for load balancer create : %v", errCh) + } + + responseTimeout, errRe := cmd.Flags().GetInt("response-timeout") + if errRe != nil { + return fmt.Errorf("error parsing flag 'response-timeout' for load balancer create : %v", errRe) + } + + unhealthyThreshold, errUn := cmd.Flags().GetInt("unhealthy-threshold") + if errUn != nil { + return fmt.Errorf("error parsing flag 'unhealthy-threshold' for load balancer create : %v", errUn) + } + + healthyThreshold, errHe := cmd.Flags().GetInt("healthy-threshold") + if errHe != nil { + return fmt.Errorf("error parsing flag 'healthy-threshold' for load balancer create : %v", errHe) + } + + privateKey, errPi := cmd.Flags().GetString("private-key") + if errPi != nil { + return fmt.Errorf("error parsing flag 'private-key' for load balancer create : %v", errPi) + } + + certificate, errCe := cmd.Flags().GetString("certificate") + if errCe != nil { + return fmt.Errorf("error parsing flag 'certificate' for load balancer create : %v", errCe) + } + + certificateChain, errCr := cmd.Flags().GetString("certificate-chain") + if errCr != nil { + return fmt.Errorf("error parsing flag 'certificate-chain' for load balancer create : %v", errCr) + } + + instances, errIn := cmd.Flags().GetStringSlice("instances") + if errIn != nil { + return fmt.Errorf("error parsing flag 'instances' for load balancer create : %v", errIn) + } + + o.CreateReq = &govultr.LoadBalancerReq{ + Region: region, + Label: label, + VPC: &vpc, + ProxyProtocol: &proxyProtocol, + SSLRedirect: &sslRedirect, + BalancingAlgorithm: algorithm, + Instances: instances, + HealthCheck: &govultr.HealthCheck{ + Port: port, + Protocol: protocol, + Path: path, + CheckInterval: checkInterval, + ResponseTimeout: responseTimeout, + UnhealthyThreshold: unhealthyThreshold, + HealthyThreshold: healthyThreshold, + }, + SSL: &govultr.SSL{ + PrivateKey: privateKey, + Certificate: certificate, + Chain: certificateChain, + }, + } + + if cmd.Flags().Changed("cookie-name") { + o.CreateReq.StickySessions = &govultr.StickySessions{CookieName: cookieName} + } + + if len(rulesInForward) > 0 { + rulesFo, errFo := formatForwardingRules(rulesInForward) + if errFo != nil { + return fmt.Errorf("error creating load balancer : %v", errFo) + } + + if len(rulesFo) > 0 { + o.CreateReq.ForwardingRules = rulesFo + } + } + + if len(rulesInFire) > 0 { + rulesFi, errFi := formatFirewallRules(rulesInFire) + if errFi != nil { + return fmt.Errorf("error creating load balancer : %v", errFi) + } + + if len(rulesFi) > 0 { + o.CreateReq.FirewallRules = rulesFi + } + } + + lb, err := o.create() + if err != nil { + return fmt.Errorf("error creating load balancer : %v", err) + } + + o.Base.Printer.Display(&LBPrinter{LB: lb}, nil) + + return nil + }, + } + + create.Flags().StringP("region", "r", "", "region id you wish to have the load balancer created in") + if err := create.MarkFlagRequired("region"); err != nil { + fmt.Printf("error marking load-balancer create 'region' flag required: %v", err) + os.Exit(1) + } + + create.Flags().StringP( + "balancing-algorithm", + "b", + "roundrobin", + "(optional) balancing algorithm that determines server selection | roundrobin or leastconn", + ) + create.Flags().BoolP( + "ssl-redirect", + "s", + false, + `(optional) if true, this will redirect HTTP traffic to HTTPS. + You must have an HTTPS rule and SSL certificate installed on the load balancer to enable this option.`, + ) + create.Flags().StringP("proxy-protocol", "p", "", "(optional) if true, you must configure backend nodes to accept Proxy protocol.") + create.Flags().StringArrayP( + "forwarding-rules", + "f", + []string{}, + `(optional) a comma-separated, key-value pair list of forwarding rules. Use - between each new rule. + E.g: "frontend_port:80,frontend_protocol:http,backend_port:80,backend_protocol:http-frontend_port:81, + frontend_protocol:http,backend_port:81,backend_protocol:http"`, + ) + create.Flags().StringP( + "vpc", + "v", + "", + "(optional) the VPC ID to attach to your load balancer. When not provided, load balancer defaults to public network.", + ) + + create.Flags().StringArrayP( + "firewall-rules", + "", + []string{}, + `(optional) a comma-separated, key-value pair list of firewall rules. Use - between each new rule. + E.g: "port:80,ip_type:v4,source:0.0.0.0/0-port:8080,ip_type:v4,source:1.1.1.1/4"`, + ) + + create.Flags().String("protocol", "http", "(optional) the protocol to use for health checks. | https, http, tcp") + create.Flags().Int("port", 80, "(optional) the port to use for health checks.") //nolint: gomnd + create.Flags().String("path", "/", "(optional) HTTP Path to check. only applies if protocol is HTTP or HTTPS.") + create.Flags().IntP("check-interval", "c", 15, "(optional) interval between health checks.") //nolint: gomnd + create.Flags().IntP("response-timeout", "t", 15, "(optional) timeout before health check fails.") //nolint: gomnd + + //nolint: gomnd + create.Flags().IntP( + "unhealthy-threshold", + "u", + 15, + "(optional) number times a check must fail before becoming unhealthy.", + ) + + //nolint: gomnd + create.Flags().Int( + "healthy-threshold", + 15, + "(optional) number times a check must succeed before returning to healthy status.", + ) + + create.Flags().String("cookie-name", "", "(optional) the cookie name to make sticky.") + + create.Flags().String("private-key", "", "(optional) the private key component for a ssl certificate.") + create.Flags().String("certificate", "", "(optional) the SSL certificate.") + create.Flags().String("certificate-chain", "", "(optional) the certificate chain for a ssl certificate.") + + create.Flags().StringP("label", "l", "", "(optional) the label for your load balancer.") + create.Flags().StringSliceP( + "instances", + "i", + []string{}, + "(optional) an array of instances IDs that you want attached to the load balancer.", + ) + + // Update + update := &cobra.Command{ + Use: "update ", + Short: "Updates a load balancer", + Aliases: []string{"u"}, + Long: updateLong, + Example: updateExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a load balancer ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for load balancer update : %v", errLa) + } + + algorithm, errAl := cmd.Flags().GetString("balancing-algorithm") + if errAl != nil { + return fmt.Errorf("error parsing flag 'balancing-algorithm' for load balancer update : %v", errAl) + } + + sslRedirect, errSs := cmd.Flags().GetBool("ssl-redirect") + if errSs != nil { + return fmt.Errorf("error parsing flag 'ssl-redirect' for load balancer update : %v", errSs) + } + + proxyProtocol, errPr := cmd.Flags().GetBool("proxy-protocol") + if errPr != nil { + return fmt.Errorf("error parsing flag 'proxy-protocol' for load balancer update : %v", errPr) + } + + cookieName, errCo := cmd.Flags().GetString("cookie-name") + if errCo != nil { + return fmt.Errorf("error parsing flag 'cookie-name' for load balancer update : %v", errCo) + } + + vpc, errVp := cmd.Flags().GetString("vpc") + if errVp != nil { + return fmt.Errorf("error parsing flag 'vpc' for load balancer update : %v", errVp) + } + + rulesInForward, errFw := cmd.Flags().GetStringArray("forwarding-rules") + if errFw != nil { + return fmt.Errorf("error parsing flag 'forwarding-rules' for load balancer update : %v", errFw) + } + + rulesInFire, errFi := cmd.Flags().GetStringArray("firewall-rules") + if errFi != nil { + return fmt.Errorf("error parsing flag 'firewall-rules' for load balancer update : %v", errFi) + } + + protocol, errPo := cmd.Flags().GetString("protocol") + if errPo != nil { + return fmt.Errorf("error parsing flag 'protocol' for load balancer update : %v", errPo) + } + + port, errPo := cmd.Flags().GetInt("port") + if errPo != nil { + return fmt.Errorf("error parsing flag 'port' for load balancer update : %v", errPo) + } + + path, errPa := cmd.Flags().GetString("path") + if errPa != nil { + return fmt.Errorf("error parsing flag 'path' for load balancer update : %v", errPa) + } + + checkInterval, errCh := cmd.Flags().GetInt("check-interval") + if errCh != nil { + return fmt.Errorf("error parsing flag 'check-interval' for load balancer update : %v", errCh) + } + + responseTimeout, errRe := cmd.Flags().GetInt("response-timeout") + if errRe != nil { + return fmt.Errorf("error parsing flag 'response-timeout' for load balancer update : %v", errRe) + } + + unhealthyThreshold, errUn := cmd.Flags().GetInt("unhealthy-threshold") + if errUn != nil { + return fmt.Errorf("error parsing flag 'unhealthy-threshold' for load balancer update : %v", errUn) + } + + healthyThreshold, errHe := cmd.Flags().GetInt("healthy-threshold") + if errHe != nil { + return fmt.Errorf("error parsing flag 'healthy-threshold' for load balancer update : %v", errHe) + } + + privateKey, errPi := cmd.Flags().GetString("private-key") + if errPi != nil { + return fmt.Errorf("error parsing flag 'private-key' for load balancer update : %v", errPi) + } + + certificate, errCe := cmd.Flags().GetString("certificate") + if errCe != nil { + return fmt.Errorf("error parsing flag 'certificate' for load balancer update : %v", errCe) + } + + certificateChain, errCr := cmd.Flags().GetString("certificate-chain") + if errCr != nil { + return fmt.Errorf("error parsing flag 'certificate-chain' for load balancer update : %v", errCr) + } + + instances, errIn := cmd.Flags().GetStringSlice("instances") + if errIn != nil { + return fmt.Errorf("error parsing flag 'instances' for load balancer update : %v", errIn) + } + + o.UpdateReq = &govultr.LoadBalancerReq{} + + if len(rulesInForward) > 0 { + rulesFo, errFo := formatForwardingRules(rulesInForward) + if errFo != nil { + return fmt.Errorf("error updating load balancer : %v", errFo) + } + + if len(rulesFo) > 0 { + o.UpdateReq.ForwardingRules = rulesFo + } + } + + if len(rulesInFire) > 0 { + rulesFi, errFi := formatFirewallRules(rulesInFire) + if errFi != nil { + return fmt.Errorf("error updating load balancer : %v", errFi) + } + + if len(rulesFi) > 0 { + o.UpdateReq.FirewallRules = rulesFi + } + } + + // Health + if port != 0 || protocol != "" || path != "" || checkInterval != 0 || responseTimeout != 0 || unhealthyThreshold != 0 || healthyThreshold != 0 { //nolint: lll + o.UpdateReq.HealthCheck = &govultr.HealthCheck{} + } + + if port != 0 { + o.UpdateReq.HealthCheck.Port = port + } + + if protocol != "" { + o.UpdateReq.HealthCheck.Protocol = protocol + } + + if path != "" { + o.UpdateReq.HealthCheck.Path = path + } + + if checkInterval != 0 { + o.UpdateReq.HealthCheck.CheckInterval = checkInterval + } + + if responseTimeout != 0 { + o.UpdateReq.HealthCheck.ResponseTimeout = responseTimeout + } + + if unhealthyThreshold != 0 { + o.UpdateReq.HealthCheck.UnhealthyThreshold = unhealthyThreshold + } + + if healthyThreshold != 0 { + o.UpdateReq.HealthCheck.HealthyThreshold = healthyThreshold + } + + // SSL + if privateKey != "" && certificate != "" { + o.UpdateReq.SSL = &govultr.SSL{ + PrivateKey: privateKey, + Certificate: certificate, + Chain: certificateChain, + } + } + + // Generic Info + if cmd.Flags().Changed("label") { + o.UpdateReq.Label = label + } + + if cmd.Flags().Changed("vpc") { + o.UpdateReq.VPC = govultr.StringToStringPtr(vpc) + } + + if cmd.Flags().Changed("proxy-protocol") { + o.UpdateReq.ProxyProtocol = &proxyProtocol + } + + if cmd.Flags().Changed("ssl-redirect") { + o.UpdateReq.SSLRedirect = &sslRedirect + } + + if cmd.Flags().Changed("cookie-name") { + o.UpdateReq.StickySessions = &govultr.StickySessions{ + CookieName: cookieName, + } + } + + if cmd.Flags().Changed("balancing-algorithm") { + o.UpdateReq.BalancingAlgorithm = algorithm + } + + if len(instances) > 0 { + o.UpdateReq.Instances = instances + } + + if err := o.update(); err != nil { + return fmt.Errorf("error updating load balancer : %v", err) + } + + o.Base.Printer.Display(printer.Info("Load balancer has been updated"), nil) + + return nil + }, + } + + update.Flags().StringP( + "balancing-algorithm", + "b", + "roundrobin", + "(optional) balancing algorithm that determines server selection | roundrobin or leastconn", + ) + update.Flags().BoolP( + "ssl-redirect", + "s", + false, + `(optional) if true, this will redirect HTTP traffic to HTTPS. You must have an HTTPS rule + and SSL certificate installed on the load balancer to enable this option.`, + ) + update.Flags().StringP("proxy-protocol", "p", "", "(optional) if true, you must configure backend nodes to accept Proxy protocol.") + update.Flags().StringArrayP( + "forwarding-rules", + "f", + []string{}, + `(optional) a comma-separated, key-value pair list of forwarding rules. Use - between each new rule. + E.g: "frontend_port:80,frontend_protocol:http,backend_port:80,backend_protocol:http-frontend_port:81, + frontend_protocol:http,backend_port:81,backend_protocol:http"`, + ) + update.Flags().StringArrayP( + "firewall-rules", + "", + []string{}, + `(optional) a comma-separated, key-value pair list of firewall rules. Use - between each new rule. + E.g: "port:80,ip_type:v4,source:0.0.0.0/0-port:8080,ip_type:v4,source:1.1.1.1/4"`, + ) + update.Flags().StringP("vpc", "v", "", "(optional) the VPC ID to attach to your load balancer.") + + update.Flags().String("protocol", "", "(optional) the protocol to use for health checks. | https, http, tcp") + update.Flags().Int("port", 0, "(optional) the port to use for health checks.") + update.Flags().String("path", "", "(optional) HTTP Path to check. only applies if protocol is HTTP or HTTPS.") + update.Flags().IntP("check-interval", "c", 0, "(optional) interval between health checks.") + update.Flags().IntP("response-timeout", "t", 0, "(optional) timeout before health check fails.") + update.Flags().IntP("unhealthy-threshold", "u", 0, "(optional) number times a check must fail before becoming unhealthy.") + update.Flags().Int("healthy-threshold", 0, "(optional) number times a check must succeed before returning to healthy status.") + + update.Flags().String("cookie-name", "", "(optional) the cookie name to make sticky.") + + update.Flags().String("private-key", "", "(optional) the private key component for a ssl certificate.") + update.Flags().String("certificate", "", "(optional) the SSL certificate.") + update.Flags().String("certificate-chain", "", "(optional) the certificate chain for a ssl certificate.") + + update.Flags().StringP("label", "l", "", "(optional) the label for your load balancer.") + update.Flags().StringSliceP( + "instances", + "i", + []string{}, + "(optional) an array of instances IDs that you want attached to the load balancer.", + ) + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "Delete a load balancer", + Aliases: []string{"destroy", "d"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a load balancer ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.del(); err != nil { + return fmt.Errorf("error deleting load balancer : %v", err) + } + + o.Base.Printer.Display(printer.Info("Load balancer has been deleted"), nil) + + return nil + }, + } + + // Forwarding Rules + forwarding := &cobra.Command{ + Use: "forwarding", + Short: "Access forwarding rules for a load balancer", + } + + // List Forwarding Rules + listForwardingRules := &cobra.Command{ + Use: "list ", + Short: "List all forwarding rules", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a load balancer ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + rules, meta, err := o.listForwardingRules() + if err != nil { + return fmt.Errorf("error listing load balancer forwarding rules : %v", err) + } + + data := &LBRulesPrinter{LBRules: rules, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + listForwardingRules.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + listForwardingRules.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get Forwarding Rule + getForwardingRule := &cobra.Command{ + Use: "get ", + Short: "Get a forwarding rule", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a load balancer ID and a rule ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + rule, err := o.getForwardingRule() + if err != nil { + return fmt.Errorf("error getting load balancer forwarding rule : %v", err) + } + + data := &LBRulePrinter{LBRule: *rule} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Create Forwarding Rule + createForwardingRule := &cobra.Command{ + Use: "create ", + Short: "Create a forwarding rule", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a load balancer ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + frontProtocol, errFr := cmd.Flags().GetString("frontend-protocol") + if errFr != nil { + return fmt.Errorf("error parsing flag 'frontend-protocol' for forwarding rule create : %v", errFr) + } + + backProtocol, errBa := cmd.Flags().GetString("backend-protocol") + if errBa != nil { + return fmt.Errorf("error parsing flag 'backend-protocol' for forwarding rule create : %v", errBa) + } + + frontPort, errFp := cmd.Flags().GetInt("frontend-port") + if errFp != nil { + return fmt.Errorf("error parsing flag 'frontend-port' for forwarding rule create : %v", errFp) + } + + backPort, errBp := cmd.Flags().GetInt("backend-port") + if errBp != nil { + return fmt.Errorf("error parsing flag 'backend-port' for forwarding rule create : %v", errBp) + } + + o.RuleCreateReq = &govultr.ForwardingRule{ + FrontendProtocol: frontProtocol, + FrontendPort: frontPort, + BackendProtocol: backProtocol, + BackendPort: backPort, + } + + rule, err := o.createForwardingRule() + if err != nil { + return fmt.Errorf("error creating load balancer forwarding rule : %v", err) + } + + data := &LBRulePrinter{LBRule: *rule} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + createForwardingRule.Flags().String( + "frontend-protocol", + "http", + "the protocol on the Load Balancer to forward to the backend. | HTTP, HTTPS, TCP", + ) + if err := createForwardingRule.MarkFlagRequired("frontend-protocol"); err != nil { + fmt.Printf("error marking load-balancer rule create 'frontend-protocol' flag required: %v", err) + os.Exit(1) + } + + createForwardingRule.Flags().String("backend-protocol", "http", "the protocol destination on the backend server. | HTTP, HTTPS, TCP") + if err := createForwardingRule.MarkFlagRequired("backend-protocol"); err != nil { + fmt.Printf("error marking load-balancer rule create 'backend-protocol' flag required: %v", err) + os.Exit(1) + } + + createForwardingRule.Flags().Int("frontend-port", 80, "the port number on the Load Balancer to forward to the backend.") //nolint: gomnd + if err := createForwardingRule.MarkFlagRequired("frontend-port"); err != nil { + fmt.Printf("error marking load-balancer rule create 'frontend-port' flag required: %v", err) + os.Exit(1) + } + + createForwardingRule.Flags().Int("backend-port", 80, "the port number destination on the backend server.") //nolint: gomnd + if err := createForwardingRule.MarkFlagRequired("backend-port"); err != nil { + fmt.Printf("error marking load-balancer rule create 'backend-port' flag required: %v", err) + os.Exit(1) + } + + // Delete Forwarding Rule + deleteForwardingRule := &cobra.Command{ + Use: "delete ", + Short: "Delete a forwarding rule", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a load balancer ID and a rule ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.deleteForwardingRule(); err != nil { + return fmt.Errorf("error deleting load balancer forwarding rule : %v", err) + } + + o.Base.Printer.Display(printer.Info("Forwarding rule has been deleted"), nil) + + return nil + }, + } + + forwarding.AddCommand( + listForwardingRules, + getForwardingRule, + createForwardingRule, + deleteForwardingRule, + ) + + // Firewall + firewall := &cobra.Command{ + Use: "firewall", + Short: "Access firewall rules on a load balancer", + } + + // List Firewall Rules + listFirewallRules := &cobra.Command{ + Use: "list ", + Short: "List all firewall rules", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a load balancer ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + rules, meta, err := o.listFirewallRules() + if err != nil { + return fmt.Errorf("error listing load balancer firewall rules : %v", err) + } + + data := &FWRulesPrinter{Rules: rules, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + listFirewallRules.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + listFirewallRules.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get Firewall Rule + getFirewallRule := &cobra.Command{ + Use: "get ", + Short: "Get a firewall rule", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("please provide a load balancer ID and a rule ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + rule, err := o.getFirewallRule() + if err != nil { + return fmt.Errorf("error getting load balancer firewall rule : %v", err) + } + + data := &FWRulePrinter{Rule: *rule} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + firewall.AddCommand( + listFirewallRules, + getFirewallRule, + ) + + cmd.AddCommand( + list, + get, + create, + update, + del, + forwarding, + firewall, + ) + + return cmd +} + +type options struct { + Base *cli.Base + CreateReq *govultr.LoadBalancerReq + UpdateReq *govultr.LoadBalancerReq + RuleCreateReq *govultr.ForwardingRule +} + +func (o *options) list() ([]govultr.LoadBalancer, *govultr.Meta, error) { + lbs, meta, _, err := o.Base.Client.LoadBalancer.List(o.Base.Context, o.Base.Options) + return lbs, meta, err +} + +func (o *options) get() (*govultr.LoadBalancer, error) { + lb, _, err := o.Base.Client.LoadBalancer.Get(o.Base.Context, o.Base.Args[0]) + return lb, err +} + +func (o *options) create() (*govultr.LoadBalancer, error) { + lb, _, err := o.Base.Client.LoadBalancer.Create(o.Base.Context, o.CreateReq) + return lb, err +} + +func (o *options) update() error { + return o.Base.Client.LoadBalancer.Update(o.Base.Context, o.Base.Args[0], o.UpdateReq) +} + +func (o *options) del() error { + return o.Base.Client.LoadBalancer.Delete(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) listForwardingRules() ([]govultr.ForwardingRule, *govultr.Meta, error) { + rs, meta, _, err := o.Base.Client.LoadBalancer.ListForwardingRules(o.Base.Context, o.Base.Args[0], o.Base.Options) + return rs, meta, err +} + +func (o *options) getForwardingRule() (*govultr.ForwardingRule, error) { + r, _, err := o.Base.Client.LoadBalancer.GetForwardingRule(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) + return r, err +} + +func (o *options) createForwardingRule() (*govultr.ForwardingRule, error) { + r, _, err := o.Base.Client.LoadBalancer.CreateForwardingRule(o.Base.Context, o.Base.Args[0], o.RuleCreateReq) + return r, err +} + +func (o *options) deleteForwardingRule() error { + return o.Base.Client.LoadBalancer.DeleteForwardingRule(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) +} + +func (o *options) listFirewallRules() ([]govultr.LBFirewallRule, *govultr.Meta, error) { + rs, meta, _, err := o.Base.Client.LoadBalancer.ListFirewallRules(o.Base.Context, o.Base.Args[0], o.Base.Options) + return rs, meta, err +} + +func (o *options) getFirewallRule() (*govultr.LBFirewallRule, error) { + r, _, err := o.Base.Client.LoadBalancer.GetFirewallRule(o.Base.Context, o.Base.Args[0], o.Base.Args[1]) + return r, err +} + +// ====================================== + +// formatFirewallRules parses forwarding rules into proper format +func formatFirewallRules(rules []string) ([]govultr.LBFirewallRule, error) { + var formattedList []govultr.LBFirewallRule + rulesList := strings.Split(rules[0], "-") + + for i := range rulesList { + rule := govultr.LBFirewallRule{} + fwRule := strings.Split(rulesList[i], ",") + + if len(fwRule) != 3 { + return nil, fmt.Errorf("unable to format firewall rules. each rule must include ip_type, source, and port") + } + + for j := range fwRule { + ruleKeyVal := strings.Split(fwRule[j], ":") + + if len(ruleKeyVal) != 2 { + return nil, fmt.Errorf("invalid firewall rule format") + } + + field := ruleKeyVal[0] + val := ruleKeyVal[1] + + switch { + case field == "ip_type": + rule.IPType = val + case field == "port": + port, errCon := strconv.Atoi(val) + if errCon != nil { + return nil, fmt.Errorf("unable to parse firewall rule port value") + } + rule.Port = port + case field == "source": + rule.Source = val + } + } + + formattedList = append(formattedList, rule) + } + + return formattedList, nil +} + +// formatForwardingRules parses forwarding rules into proper format +func formatForwardingRules(rules []string) ([]govultr.ForwardingRule, error) { + var formattedList []govultr.ForwardingRule + var rulePartNum = 4 + rulesList := strings.Split(rules[0], "-") + + for i := range rulesList { + rule := govultr.ForwardingRule{} + fwRule := strings.Split(rulesList[i], ",") + + if len(fwRule) != rulePartNum { + return nil, fmt.Errorf("unable to format forwarding rules. each rule must include frontend and backend ports and protocols") + } + + for j := range fwRule { + ruleKeyVal := strings.Split(fwRule[j], ":") + + if len(ruleKeyVal) != 2 { + return nil, fmt.Errorf("invalid forwarding rule format") + } + + field := ruleKeyVal[0] + val := ruleKeyVal[1] + + switch { + case field == "frontend_protocol": + rule.FrontendProtocol = val + case field == "frontend_port": + port, errCon := strconv.Atoi(val) + if errCon != nil { + return nil, fmt.Errorf("unable to parse fowarding rule frontend port value") + } + rule.FrontendPort = port + case field == "backend_protocol": + rule.BackendProtocol = val + case field == "backend_port": + port, errCon := strconv.Atoi(val) + if errCon != nil { + return nil, fmt.Errorf("unable to parse fowarding rule backend port value") + } + rule.BackendPort = port + } + } + + formattedList = append(formattedList, rule) + } + + return formattedList, nil +} diff --git a/cmd/loadbalancer/printer.go b/cmd/loadbalancer/printer.go new file mode 100644 index 00000000..73636361 --- /dev/null +++ b/cmd/loadbalancer/printer.go @@ -0,0 +1,491 @@ +package loadbalancer + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// LBsPrinter ... +type LBsPrinter struct { + LBs []govultr.LoadBalancer `json:"load_balancers"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (l *LBsPrinter) JSON() []byte { + return printer.MarshalObject(l, "json") +} + +// YAML ... +func (l *LBsPrinter) YAML() []byte { + return printer.MarshalObject(l, "yaml") +} + +// Columns ... +func (l *LBsPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (l *LBsPrinter) Data() [][]string { + if len(l.LBs) == 0 { + return [][]string{0: {"No active load balancers"}} + } + + var data [][]string + for i := range l.LBs { + data = append(data, + []string{"---------------------------"}, + []string{"ID", l.LBs[i].ID}, + []string{"DATE CREATED", l.LBs[i].DateCreated}, + []string{"REGION", l.LBs[i].Region}, + []string{"LABEL", l.LBs[i].Label}, + []string{"STATUS", l.LBs[i].Status}, + []string{"IPV4", l.LBs[i].IPV4}, + []string{"IPV6", l.LBs[i].IPV6}, + []string{"HAS SSL", strconv.FormatBool(*l.LBs[i].SSLInfo)}, + []string{"INSTANCES", printer.ArrayOfStringsToString(l.LBs[i].Instances)}, + + []string{" "}, + []string{"HEALTH CHECKS"}, + []string{"PROTOCOL", "PORT", "PATH", "CHECK INTERVAL", "RESPONSE TIMEOUT", "UNHEALTHY THRESHOLD", "HEALTHY THRESHOLD"}, + []string{ + l.LBs[i].HealthCheck.Protocol, + strconv.Itoa(l.LBs[i].HealthCheck.Port), + l.LBs[i].HealthCheck.Path, + strconv.Itoa(l.LBs[i].HealthCheck.CheckInterval), + strconv.Itoa(l.LBs[i].HealthCheck.ResponseTimeout), + strconv.Itoa(l.LBs[i].HealthCheck.UnhealthyThreshold), + strconv.Itoa(l.LBs[i].HealthCheck.HealthyThreshold), + }, + + []string{" "}, + []string{"GENERIC INFO"}, + []string{"BALANCING ALGORITHM", "SSL REDIRECT", "COOKIE NAME", "PROXY PROTOCOL", "VPC"}, + []string{ + l.LBs[i].GenericInfo.BalancingAlgorithm, + strconv.FormatBool(*l.LBs[i].GenericInfo.SSLRedirect), + l.LBs[i].GenericInfo.StickySessions.CookieName, + strconv.FormatBool(*l.LBs[i].GenericInfo.ProxyProtocol), + l.LBs[i].GenericInfo.VPC, + }, + + []string{" "}, + []string{"FORWARDING RULES"}, + []string{"RULEID", "FRONTEND PROTOCOL", "FRONTEND PORT", "BACKEND PROTOCOL", "BACKEND PORT"}, + ) + + if len(l.LBs[i].ForwardingRules) == 0 { + data = append(data, []string{"---", "---", "---", "---", "---"}) + } else { + for j := range l.LBs[i].ForwardingRules { + data = append(data, + []string{ + l.LBs[i].ForwardingRules[j].RuleID, + l.LBs[i].ForwardingRules[j].FrontendProtocol, + strconv.Itoa(l.LBs[i].ForwardingRules[j].FrontendPort), + l.LBs[i].ForwardingRules[j].BackendProtocol, + strconv.Itoa(l.LBs[i].ForwardingRules[j].BackendPort), + }, + ) + } + } + + data = append(data, + []string{" "}, + []string{"FIREWALL RULES"}, + []string{"RULEID", "PORT", "SOURCE", "IP_TYPE"}, + ) + + if len(l.LBs[i].FirewallRules) == 0 { + data = append(data, []string{"---", "---", "---", "---"}) + } else { + for j := range l.LBs[i].FirewallRules { + data = append(data, + []string{ + l.LBs[i].FirewallRules[j].RuleID, + strconv.Itoa(l.LBs[i].FirewallRules[j].Port), + l.LBs[i].FirewallRules[j].Source, + l.LBs[i].FirewallRules[j].IPType, + }, + ) + } + } + + data = append(data, []string{" "}) + } + + return data +} + +// Paging ... +func (l *LBsPrinter) Paging() [][]string { + return printer.NewPaging(l.Meta.Total, &l.Meta.Links.Next, &l.Meta.Links.Prev).Compose() +} + +// ====================================== + +// LBPrinter ... +type LBPrinter struct { + LB *govultr.LoadBalancer `json:"load_balancer"` +} + +// JSON ... +func (l *LBPrinter) JSON() []byte { + return printer.MarshalObject(l, "json") +} + +// YAML ... +func (l *LBPrinter) YAML() []byte { + return printer.MarshalObject(l, "yaml") +} + +// Columns ... +func (l *LBPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (l *LBPrinter) Data() [][]string { + var data [][]string + data = append(data, + []string{"ID", l.LB.ID}, + []string{"DATE CREATED", l.LB.DateCreated}, + []string{"REGION", l.LB.Region}, + []string{"LABEL", l.LB.Label}, + []string{"STATUS", l.LB.Status}, + []string{"IPV4", l.LB.IPV4}, + []string{"IPV6", l.LB.IPV6}, + []string{"HAS SSL", strconv.FormatBool(*l.LB.SSLInfo)}, + []string{"INSTANCES", printer.ArrayOfStringsToString(l.LB.Instances)}, + + []string{" "}, + []string{"HEALTH CHECKS"}, + []string{"PROTOCOL", "PORT", "PATH", "CHECK INTERVAL", "RESPONSE TIMEOUT", "UNHEALTHY THRESHOLD", "HEALTHY THRESHOLD"}, + []string{ + l.LB.HealthCheck.Protocol, + strconv.Itoa(l.LB.HealthCheck.Port), + l.LB.HealthCheck.Path, + strconv.Itoa(l.LB.HealthCheck.CheckInterval), + strconv.Itoa(l.LB.HealthCheck.ResponseTimeout), + strconv.Itoa(l.LB.HealthCheck.UnhealthyThreshold), + strconv.Itoa(l.LB.HealthCheck.HealthyThreshold), + }, + + []string{" "}, + []string{"GENERIC INFO"}, + []string{"BALANCING ALGORITHM", "SSL REDIRECT", "COOKIE NAME", "PROXY PROTOCOL", "VPC"}, + []string{ + l.LB.GenericInfo.BalancingAlgorithm, + strconv.FormatBool(*l.LB.GenericInfo.SSLRedirect), + l.LB.GenericInfo.StickySessions.CookieName, + strconv.FormatBool(*l.LB.GenericInfo.ProxyProtocol), + l.LB.GenericInfo.VPC, + }, + + []string{" "}, + []string{"FORWARDING RULES"}, + []string{"RULEID", "FRONTEND PROTOCOL", "FRONTEND PORT", "BACKEND PROTOCOL", "BACKEND PORT"}, + ) + + if len(l.LB.ForwardingRules) == 0 { + data = append(data, []string{"---", "---", "---", "---", "---"}) + } else { + for i := range l.LB.ForwardingRules { + data = append(data, + []string{ + l.LB.ForwardingRules[i].RuleID, + l.LB.ForwardingRules[i].FrontendProtocol, + strconv.Itoa(l.LB.ForwardingRules[i].FrontendPort), + l.LB.ForwardingRules[i].BackendProtocol, + strconv.Itoa(l.LB.ForwardingRules[i].BackendPort), + }, + ) + } + } + + data = append(data, + []string{" "}, + []string{"FIREWALL RULES"}, + []string{"RULEID", "PORT", "SOURCE", "IP_TYPE"}, + ) + + if len(l.LB.FirewallRules) == 0 { + data = append(data, []string{"---", "---", "---", "---"}) + } else { + for i := range l.LB.FirewallRules { + data = append(data, + []string{ + l.LB.FirewallRules[i].RuleID, + strconv.Itoa(l.LB.FirewallRules[i].Port), + l.LB.FirewallRules[i].Source, + l.LB.FirewallRules[i].IPType, + }, + ) + } + } + + return data +} + +// Paging ... +func (l *LBPrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// LBsSummaryPrinter ... +type LBsSummaryPrinter struct { + LBs []govultr.LoadBalancer `json:"load_balancers"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (l *LBsSummaryPrinter) JSON() []byte { + return printer.MarshalObject(l, "json") +} + +// YAML ... +func (l *LBsSummaryPrinter) YAML() []byte { + return printer.MarshalObject(l, "yaml") +} + +// Columns ... +func (l *LBsSummaryPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "LABEL", + "STATUS", + "REGION", + "INSTANCE#", + "FORWARD#", + "FIREWALL#", + }} +} + +// Data ... +func (l *LBsSummaryPrinter) Data() [][]string { + if len(l.LBs) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range l.LBs { + forwardRuleCount := len(l.LBs[i].ForwardingRules) + firewallRuleCount := len(l.LBs[i].FirewallRules) + instanceCount := len(l.LBs[i].Instances) + + data = append(data, []string{ + + l.LBs[i].ID, + l.LBs[i].Label, + l.LBs[i].Status, + l.LBs[i].Region, + strconv.Itoa(instanceCount), + strconv.Itoa(forwardRuleCount), + strconv.Itoa(firewallRuleCount), + }) + } + + return data +} + +// Paging ... +func (l *LBsSummaryPrinter) Paging() [][]string { + return printer.NewPaging(l.Meta.Total, &l.Meta.Links.Next, &l.Meta.Links.Prev).Compose() +} + +// ====================================== + +// LBRulesPrinter ... +type LBRulesPrinter struct { + LBRules []govultr.ForwardingRule `json:"forwarding_rules"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (l *LBRulesPrinter) JSON() []byte { + return printer.MarshalObject(l, "json") +} + +// YAML ... +func (l *LBRulesPrinter) YAML() []byte { + return printer.MarshalObject(l, "yaml") +} + +// Columns ... +func (l *LBRulesPrinter) Columns() [][]string { + return [][]string{0: { + "RULEID", + "FRONTEND PROTOCOL", + "FRONTEND PORT", + "BACKEND PROTOCOL", + "BACKEND PORT", + }} +} + +// Data ... +func (l *LBRulesPrinter) Data() [][]string { + if len(l.LBRules) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range l.LBRules { + data = append(data, []string{ + l.LBRules[i].RuleID, + l.LBRules[i].FrontendProtocol, + strconv.Itoa(l.LBRules[i].FrontendPort), + l.LBRules[i].BackendProtocol, + strconv.Itoa(l.LBRules[i].BackendPort), + }) + } + + return data +} + +// Paging ... +func (l *LBRulesPrinter) Paging() [][]string { + return printer.NewPaging(l.Meta.Total, &l.Meta.Links.Next, &l.Meta.Links.Prev).Compose() +} + +// ====================================== + +// LBRulePrinter ... +type LBRulePrinter struct { + LBRule govultr.ForwardingRule `json:"forwarding_rule"` +} + +// JSON ... +func (l *LBRulePrinter) JSON() []byte { + return printer.MarshalObject(l, "json") +} + +// YAML ... +func (l *LBRulePrinter) YAML() []byte { + return printer.MarshalObject(l, "yaml") +} + +// Columns ... +func (l *LBRulePrinter) Columns() [][]string { + return [][]string{0: { + "RULEID", + "FRONTEND PROTOCOL", + "FRONTEND PORT", + "BACKEND PROTOCOL", + "BACKEND PORT", + }} +} + +// Data ... +func (l *LBRulePrinter) Data() [][]string { + return [][]string{0: { + l.LBRule.RuleID, + l.LBRule.FrontendProtocol, + strconv.Itoa(l.LBRule.FrontendPort), + l.LBRule.BackendProtocol, + strconv.Itoa(l.LBRule.BackendPort), + }} +} + +// Paging ... +func (l *LBRulePrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// FWRulesPrinter ... +type FWRulesPrinter struct { + Rules []govultr.LBFirewallRule `json:"firewall_rules"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (f *FWRulesPrinter) JSON() []byte { + return printer.MarshalObject(f, "json") +} + +// YAML ... +func (f *FWRulesPrinter) YAML() []byte { + return printer.MarshalObject(f, "yaml") +} + +// Columns ... +func (f *FWRulesPrinter) Columns() [][]string { + return [][]string{0: { + "RULEID", + "PORT", + "SOURCE", + "IP_TYPE", + }} +} + +// Data ... +func (f *FWRulesPrinter) Data() [][]string { + if len(f.Rules) == 0 { + return [][]string{0: {"---", "---", "---", "---"}} + } + + var data [][]string + for i := range f.Rules { + data = append(data, []string{ + f.Rules[i].RuleID, + strconv.Itoa(f.Rules[i].Port), + f.Rules[i].Source, + f.Rules[i].IPType, + }) + } + + return data +} + +// Paging ... +func (f *FWRulesPrinter) Paging() [][]string { + return printer.NewPaging(f.Meta.Total, &f.Meta.Links.Next, &f.Meta.Links.Prev).Compose() +} + +// ====================================== + +// FWRulePrinter ... +type FWRulePrinter struct { + Rule govultr.LBFirewallRule `json:"firewall_rule"` +} + +// JSON ... +func (f *FWRulePrinter) JSON() []byte { + return printer.MarshalObject(f, "json") +} + +// YAML ... +func (f *FWRulePrinter) YAML() []byte { + return printer.MarshalObject(f, "yaml") +} + +// Columns ... +func (f *FWRulePrinter) Columns() [][]string { + return [][]string{0: { + "RULEID", + "PORT", + "SOURCE", + "IP_TYPE", + }} +} + +// Data ... +func (f *FWRulePrinter) Data() [][]string { + return [][]string{0: { + f.Rule.RuleID, + strconv.Itoa(f.Rule.Port), + f.Rule.Source, + f.Rule.IPType, + }} +} + +// Paging ... +func (f *FWRulePrinter) Paging() [][]string { + return nil +} diff --git a/cmd/marketplace/marketplace.go b/cmd/marketplace/marketplace.go new file mode 100644 index 00000000..29d64888 --- /dev/null +++ b/cmd/marketplace/marketplace.go @@ -0,0 +1,99 @@ +// Package marketplace provides the command for the CLI to access marketplace +// functionality +package marketplace + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Get commands available to marketplace` + example = ` + # Full example + vultr-cli marketplace + ` + appLong = `Get commands available to marketplace apps` + appExample = ` + # Full example + vultr-cli marketplace app + ` + listAppVariablesLong = `List all user-supplied variables for a given marketplace app` + listAppVariablesExample = ` + # Full example + vultr-cli marketplace app list-variables drupal + ` +) + +// NewCmdMarketplace provides the CLI command for marketplace functions +func NewCmdMarketplace(base *cli.Base) *cobra.Command { + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "marketplace", + Short: "Commands to interact with marketplace functions", + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + return nil + }, + } + + // App + app := &cobra.Command{ + Use: "app", + Short: "Commands to interact with vultr marketplace apps", + Long: appLong, + Example: appExample, + } + + // List Variables + listVariables := &cobra.Command{ + Use: "list-variables", + Short: "List all user-supplied variables for a marketplace app", + Aliases: []string{"l"}, + Long: listAppVariablesLong, + Example: listAppVariablesExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an image ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + vars, err := o.listVariables() + if err != nil { + return fmt.Errorf("error getting list of marketplace app variables : %v", err) + } + + o.Base.Printer.Display(&VariablesPrinter{Variables: vars}, nil) + + return nil + }, + } + + app.AddCommand( + listVariables, + ) + + cmd.AddCommand( + app, + ) + + return cmd +} + +type options struct { + Base *cli.Base +} + +func (o *options) listVariables() ([]govultr.MarketplaceAppVariable, error) { + vars, _, err := o.Base.Client.Marketplace.ListAppVariables(o.Base.Context, o.Base.Args[0]) + return vars, err +} diff --git a/cmd/marketplace/printer.go b/cmd/marketplace/printer.go new file mode 100644 index 00000000..40e8b6cc --- /dev/null +++ b/cmd/marketplace/printer.go @@ -0,0 +1,55 @@ +package marketplace + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// VariablesPrinter ... +type VariablesPrinter struct { + Variables []govultr.MarketplaceAppVariable `json:"variables"` +} + +// JSON ... +func (v *VariablesPrinter) JSON() []byte { + return printer.MarshalObject(v, "json") +} + +// YAML ... +func (v *VariablesPrinter) YAML() []byte { + return printer.MarshalObject(v, "yaml") +} + +// Columns ... +func (v *VariablesPrinter) Columns() [][]string { + return [][]string{0: { + "NAME", + "DESCRIPTION", + "REQUIRED", + }} +} + +// Data ... +func (v *VariablesPrinter) Data() [][]string { + if len(v.Variables) == 0 { + return [][]string{0: {"---", "---", "---"}} + } + + var data [][]string + for i := range v.Variables { + data = append(data, []string{ + v.Variables[i].Name, + v.Variables[i].Description, + strconv.FormatBool(*v.Variables[i].Required), + }) + } + + return data +} + +// Paging ... +func (v *VariablesPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/network.go b/cmd/network.go deleted file mode 100644 index e48a6dae..00000000 --- a/cmd/network.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// Network represents the network command -func Network() *cobra.Command { - networkCmd := &cobra.Command{ - Use: "network", - Short: "network interacts with network actions", - Long: ``, - } - - networkCmd.AddCommand(networkGet, networkList, networkDelete, networkCreate) - networkCreate.Flags().StringP("region-id", "r", "", "id of the region you wish to create the network") - networkCreate.Flags().StringP("description", "d", "", "description of the network") - networkCreate.Flags().StringP("subnet", "s", "", "The IPv4 network in CIDR notation.") - networkCreate.Flags().IntP("size", "z", 0, "The number of bits for the netmask in CIDR notation.") - networkCreate.MarkFlagRequired("region-id") - networkCreate.MarkFlagRequired("description") - networkCreate.MarkFlagRequired("subnet") - networkCreate.MarkFlagRequired("size") - - networkList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - networkList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return networkCmd -} - -var networkGet = &cobra.Command{ - Use: "get ", - Short: "get a private network", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a networkID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - network, err := client.Network.Get(context.Background(), id) - if err != nil { - fmt.Printf("error getting network : %v\n", err) - os.Exit(1) - } - - printer.Network(network) - }, -} - -var networkList = &cobra.Command{ - Use: "list", - Short: "list all private networks", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - network, meta, err := client.Network.List(context.Background(), options) - if err != nil { - fmt.Printf("error getting network list : %v\n", err) - os.Exit(1) - } - - printer.NetworkList(network, meta) - }, -} - -var networkDelete = &cobra.Command{ - Use: "delete ", - Short: "delete a private network", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a networkID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.Network.Delete(context.Background(), id); err != nil { - fmt.Printf("error deleting network : %v\n", err) - os.Exit(1) - } - - fmt.Println("Deleted network") - }, -} - -var networkCreate = &cobra.Command{ - Use: "create", - Short: "create a private network", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - region, _ := cmd.Flags().GetString("region-id") - description, _ := cmd.Flags().GetString("description") - subnet, _ := cmd.Flags().GetString("subnet") - size, _ := cmd.Flags().GetInt("size") - - options := &govultr.NetworkReq{ - Region: region, - Description: description, - V4Subnet: subnet, - V4SubnetMask: size, - } - - network, err := client.Network.Create(context.Background(), options) - if err != nil { - fmt.Printf("error creating network : %v\n", err) - os.Exit(1) - } - - printer.Network(network) - }, -} diff --git a/cmd/objectStorage.go b/cmd/objectStorage.go deleted file mode 100644 index 109d7c63..00000000 --- a/cmd/objectStorage.go +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// ObjectStorageCmd represents the objStorageCmd command -func ObjectStorageCmd() *cobra.Command { - objStorageCmd := &cobra.Command{ - Use: "object-storage", - Aliases: []string{"objStorage"}, - Short: "object storage commands", - Long: `object-storage is used to interact with the object-storage api`, - } - - objStorageCmd.AddCommand(objStorageCreate, objStorageLabelSet, objStorageList, objStorageGet, objStorageClusterList, objStorageS3KeyRegenerate, objStorageDestroy) - - // Create - objStorageCreate.Flags().StringP("label", "l", "", "label you want your object storage to have") - objStorageCreate.Flags().IntP("obj-store-clusterid", "o", 0, "obj-store-clusterid you want to create the object storage in") - objStorageCreate.MarkFlagRequired("obj-store-clusterid") - - // Label - objStorageLabelSet.Flags().StringP("label", "l", "", "label you want your object storage to have") - objStorageLabelSet.MarkFlagRequired("label") - - // List - objStorageList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - objStorageList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - // Regenerate - objStorageS3KeyRegenerate.Flags().StringP("s3-access-key", "s", "", "access key for a given object storage subscription") - - // Cluster List - objStorageClusterList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - objStorageClusterList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return objStorageCmd -} - -var objStorageCreate = &cobra.Command{ - Use: "create", - Short: "create a new object storage subscription", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - objectStoreClusterID, _ := cmd.Flags().GetInt("obj-store-clusterid") - label, _ := cmd.Flags().GetString("label") - - objStorage, err := client.ObjectStorage.Create(context.TODO(), objectStoreClusterID, label) - if err != nil { - fmt.Printf("error creating object storage : %v\n", err) - os.Exit(1) - } - - printer.SingleObjectStorage(objStorage) - }, -} - -var objStorageLabelSet = &cobra.Command{ - Use: "label ", - Short: "change the label for object storage subscription", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an objectStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - label, _ := cmd.Flags().GetString("label") - - err := client.ObjectStorage.Update(context.TODO(), id, label) - if err != nil { - fmt.Printf("error setting label : %v\n", err) - os.Exit(1) - } - - fmt.Printf("set label on object storage : %v\n", id) - }, -} - -var objStorageList = &cobra.Command{ - Use: "list", - Short: "retrieves a list of active object storage subscriptions", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - objStorage, meta, err := client.ObjectStorage.List(context.TODO(), options) - if err != nil { - fmt.Printf("error getting object storage : %v\n", err) - os.Exit(1) - } - - printer.ObjectStorages(objStorage, meta) - }, -} - -var objStorageGet = &cobra.Command{ - Use: "get ", - Short: "retrieves a given object storage subscription", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an objectStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - objStorage, err := client.ObjectStorage.Get(context.TODO(), id) - if err != nil { - fmt.Printf("error getting object storage : %v\n", err) - os.Exit(1) - } - - printer.SingleObjectStorage(objStorage) - }, -} - -var objStorageClusterList = &cobra.Command{ - Use: "list-cluster", - Short: "retrieves a list of object storage clusters", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - cluster, meta, err := client.ObjectStorage.ListCluster(context.TODO(), options) - if err != nil { - fmt.Printf("error getting object storage clusters : %v\n", err) - os.Exit(1) - } - - printer.ObjectStorageClusterList(cluster, meta) - }, -} - -var objStorageS3KeyRegenerate = &cobra.Command{ - Use: "s3key-regenerate ", - Short: "regenerate the S3 API keys of an object storage subscription", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an objectStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - s3Keys, err := client.ObjectStorage.RegenerateKeys(context.TODO(), id) - if err != nil { - fmt.Printf("error regenerating object storage keys : %v\n", err) - os.Exit(1) - } - - printer.ObjStorageS3KeyRegenerate(s3Keys) - }, -} - -var objStorageDestroy = &cobra.Command{ - Use: "delete ", - Short: "deletes an object storage subscription", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide an objectStorageID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.ObjectStorage.Delete(context.TODO(), id); err != nil { - fmt.Printf("error destroying object storage subscription : %v\n", err) - os.Exit(1) - } - - fmt.Println("destroyed object storage subscription") - }, -} diff --git a/cmd/objectstorage/objectstorage.go b/cmd/objectstorage/objectstorage.go new file mode 100644 index 00000000..f26fc9c9 --- /dev/null +++ b/cmd/objectstorage/objectstorage.go @@ -0,0 +1,272 @@ +// Package objectstorage provides the object storage commands for the CLI +package objectstorage + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +// NewCmdObjectStorage provides the CLI command for object storage functions +func NewCmdObjectStorage(base *cli.Base) *cobra.Command { //nolint:gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "object-storage", + Short: "object storage commands", + Long: `object-storage is used to interact with object storages`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "retrieves a list of active object storages", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + oss, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving object storage list : %v", err) + } + + data := &ObjectStoragesPrinter{ObjectStorages: oss, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + "(optional) Number of items requested per page. Default is 100 and Max is 500.", + ) + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "retrieves a given object storage", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an object storage ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + os, err := o.get() + if err != nil { + return fmt.Errorf("error getting object storage info : %v", err) + } + + data := &ObjectStoragePrinter{ObjectStorage: os} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Create + create := &cobra.Command{ + Use: "create", + Short: "create a new object storage", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + clusterID, errCl := cmd.Flags().GetInt("cluster-id") + if errCl != nil { + return fmt.Errorf("error parsing flag 'cluster-id' for object storage create : %v", errCl) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for object storage create : %v", errLa) + } + + o.ClusterID = clusterID + o.Label = label + + os, err := o.create() + if err != nil { + return fmt.Errorf("error creating object storage : %v", err) + } + + data := &ObjectStoragePrinter{ObjectStorage: os} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + create.Flags().StringP("label", "l", "", "label you want your object storage to have") + create.Flags().IntP("cluster-id", "i", 0, "ID of the cluster in which to create the object storage") + if err := create.MarkFlagRequired("cluster-id"); err != nil { + printer.Error(fmt.Errorf("error marking object storage create 'cluster-id' flag required : %v", err)) + os.Exit(1) + } + + // Label + label := &cobra.Command{ + Use: "label ", + Short: "change the label for object storage subscription", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an object storage ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for object storage label : %v", errLa) + } + + o.Label = label + if err := o.update(); err != nil { + return fmt.Errorf("error updating object storage label : %v", err) + } + + o.Base.Printer.Display(printer.Info("object storage label has been set"), nil) + return nil + }, + } + + label.Flags().StringP("label", "l", "", "label you want your object storage to have") + if err := label.MarkFlagRequired("label"); err != nil { + printer.Error(fmt.Errorf("error marking object storage update 'label' flag required: %v", err)) + os.Exit(1) + } + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "delete specified object storage", + Aliases: []string{"destroy"}, + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an object storage ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.del(); err != nil { + return fmt.Errorf("unable to delete object storage : %v", err) + } + + o.Base.Printer.Display(printer.Info("object storage has been deleted"), nil) + return nil + }, + } + + // Regenerate Keys + regenerateKeys := &cobra.Command{ + Use: "regenerate-keys ", + Short: "regenerate the S3 API keys for object storage", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide an object storage ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + key, err := o.regenerateKeys() + if err != nil { + return fmt.Errorf("unable to regenerate keys for object storage : %v", err) + } + + data := &ObjectStorageKeysPrinter{Keys: key} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // List Clusters + listClusters := &cobra.Command{ + Use: "list-clusters", + Short: "retrieve a list of all available object storage clusters", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + clusters, meta, err := o.listClusters() + if err != nil { + return fmt.Errorf("error retrieving object storage cluster list : %v", err) + } + + data := &ObjectStorageClustersPrinter{Clusters: clusters, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + cmd.AddCommand( + list, + get, + create, + label, + del, + regenerateKeys, + listClusters, + ) + + return cmd +} + +type options struct { + Base *cli.Base + ClusterID int + Label string +} + +func (o *options) list() ([]govultr.ObjectStorage, *govultr.Meta, error) { + oss, meta, _, err := o.Base.Client.ObjectStorage.List(o.Base.Context, o.Base.Options) + return oss, meta, err +} + +func (o *options) get() (*govultr.ObjectStorage, error) { + os, _, err := o.Base.Client.ObjectStorage.Get(o.Base.Context, o.Base.Args[0]) + return os, err +} + +func (o *options) create() (*govultr.ObjectStorage, error) { + os, _, err := o.Base.Client.ObjectStorage.Create(o.Base.Context, o.ClusterID, o.Label) + return os, err +} + +func (o *options) update() error { + return o.Base.Client.ObjectStorage.Update(o.Base.Context, o.Base.Args[0], o.Label) +} + +func (o *options) del() error { + return o.Base.Client.ObjectStorage.Delete(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) listClusters() ([]govultr.ObjectStorageCluster, *govultr.Meta, error) { + clusters, meta, _, err := o.Base.Client.ObjectStorage.ListCluster(o.Base.Context, o.Base.Options) + return clusters, meta, err +} + +func (o *options) regenerateKeys() (*govultr.S3Keys, error) { + keys, _, err := o.Base.Client.ObjectStorage.RegenerateKeys(o.Base.Context, o.Base.Args[0]) + return keys, err +} diff --git a/cmd/objectstorage/printer.go b/cmd/objectstorage/printer.go new file mode 100644 index 00000000..e1989167 --- /dev/null +++ b/cmd/objectstorage/printer.go @@ -0,0 +1,212 @@ +package objectstorage + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// ObjectStoragesPrinter ... +type ObjectStoragesPrinter struct { + ObjectStorages []govultr.ObjectStorage `json:"object_storages"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (o *ObjectStoragesPrinter) JSON() []byte { + return printer.MarshalObject(o, "json") +} + +// YAML ... +func (o *ObjectStoragesPrinter) YAML() []byte { + return printer.MarshalObject(o, "yaml") +} + +// Columns ... +func (o *ObjectStoragesPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "REGION", + "CLUSTER ID", + "STATUS", + "LABEL", + "DATE CREATED", + "S3 HOSTNAME", + "S3 ACCESS KEY", + "S3 SECRET KEY", + }} +} + +// Data ... +func (o *ObjectStoragesPrinter) Data() [][]string { + if len(o.ObjectStorages) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range o.ObjectStorages { + data = append(data, []string{ + o.ObjectStorages[i].ID, + o.ObjectStorages[i].Region, + strconv.Itoa(o.ObjectStorages[i].ObjectStoreClusterID), + o.ObjectStorages[i].Status, + o.ObjectStorages[i].Label, + o.ObjectStorages[i].DateCreated, + o.ObjectStorages[i].S3Keys.S3Hostname, + o.ObjectStorages[i].S3Keys.S3AccessKey, + o.ObjectStorages[i].S3Keys.S3SecretKey, + }) + } + + return data +} + +// Paging ... +func (o *ObjectStoragesPrinter) Paging() [][]string { + return printer.NewPaging(o.Meta.Total, &o.Meta.Links.Next, &o.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ObjectStoragePrinter ... +type ObjectStoragePrinter struct { + ObjectStorage *govultr.ObjectStorage `json:"object_storage"` +} + +// JSON ... +func (o *ObjectStoragePrinter) JSON() []byte { + return printer.MarshalObject(o, "json") +} + +// YAML ... +func (o *ObjectStoragePrinter) YAML() []byte { + return printer.MarshalObject(o, "yaml") +} + +// Columns ... +func (o *ObjectStoragePrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "REGION", + "CLUSTER ID", + "STATUS", + "LABEL", + "DATE CREATED", + "S3 HOSTNAME", + "S3 ACCESS KEY", + "S3 SECRET KEY", + }} +} + +// Data ... +func (o *ObjectStoragePrinter) Data() [][]string { + return [][]string{0: { + o.ObjectStorage.ID, + o.ObjectStorage.Region, + strconv.Itoa(o.ObjectStorage.ObjectStoreClusterID), + o.ObjectStorage.Status, + o.ObjectStorage.Label, + o.ObjectStorage.DateCreated, + o.ObjectStorage.S3Keys.S3Hostname, + o.ObjectStorage.S3Keys.S3AccessKey, + o.ObjectStorage.S3Keys.S3SecretKey, + }} +} + +// Paging ... +func (o *ObjectStoragePrinter) Paging() [][]string { + return nil +} + +// ====================================== + +// ObjectStorageClustersPrinter ... +type ObjectStorageClustersPrinter struct { + Clusters []govultr.ObjectStorageCluster `json:"clusters"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (o *ObjectStorageClustersPrinter) JSON() []byte { + return printer.MarshalObject(o, "json") +} + +// YAML ... +func (o *ObjectStorageClustersPrinter) YAML() []byte { + return printer.MarshalObject(o, "yaml") +} + +// Columns ... +func (o *ObjectStorageClustersPrinter) Columns() [][]string { + return [][]string{0: { + "CLUSTER ID", + "REGION ID", + "HOSTNAME", + "DEPLOY", + }} +} + +// Data ... +func (o *ObjectStorageClustersPrinter) Data() [][]string { + if len(o.Clusters) == 0 { + return [][]string{0: {"---", "---", "---", "---"}} + } + + var data [][]string + for i := range o.Clusters { + data = append(data, []string{ + strconv.Itoa(o.Clusters[i].ID), + o.Clusters[i].Region, + o.Clusters[i].Hostname, + o.Clusters[i].Deploy, + }) + } + + return data +} + +// Paging ... +func (o *ObjectStorageClustersPrinter) Paging() [][]string { + return printer.NewPaging(o.Meta.Total, &o.Meta.Links.Next, &o.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ObjectStorageKeysPrinter ... +type ObjectStorageKeysPrinter struct { + Keys *govultr.S3Keys `json:"s3_credentials"` +} + +// JSON ... +func (o *ObjectStorageKeysPrinter) JSON() []byte { + return printer.MarshalObject(o, "json") +} + +// YAML ... +func (o *ObjectStorageKeysPrinter) YAML() []byte { + return printer.MarshalObject(o, "yaml") +} + +// Columns ... +func (o *ObjectStorageKeysPrinter) Columns() [][]string { + return [][]string{0: { + "S3 HOSTNAME", + "S3 ACCESS KEY", + "S3 SECRET KEY", + }} +} + +// Data ... +func (o *ObjectStorageKeysPrinter) Data() [][]string { + return [][]string{0: { + o.Keys.S3Hostname, + o.Keys.S3AccessKey, + o.Keys.S3SecretKey, + }} +} + +// Paging ... +func (o *ObjectStorageKeysPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/operatingSystems/operatingSystems.go b/cmd/operatingSystems/operatingSystems.go deleted file mode 100644 index fb5cb828..00000000 --- a/cmd/operatingSystems/operatingSystems.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package operatingSystems - -import ( - "context" - "fmt" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" - "github.com/vultr/vultr-cli/cmd/utils" - "github.com/vultr/vultr-cli/pkg/cli" -) - -var ( - long = `OS will retrieve available operating systems that can be deployed` - example = ` - # Example - vultr-cli os - ` - - listLong = `List all operating systems available to be deployed on Vultr` - listExample = ` - # Full example - vultr-cli os list - - # Full example with paging - vultr-cli os list --per-page=1 --cursor="bmV4dF9fMTI0" - - # Shortened with alias commands - vultr-cli o l - ` -) - -// Interface for os -type Interface interface { - validate(cmd *cobra.Command, args []string) - List() ([]govultr.OS, *govultr.Meta, error) -} - -// Options for os -type Options struct { - Base *cli.Base -} - -// NewOSOptions returns Options struct -func NewOSOptions(base *cli.Base) *Options { - return &Options{Base: base} -} - -// NewCmdOS creates cobra command for OS -func NewCmdOS(base *cli.Base) *cobra.Command { - o := NewOSOptions(base) - - cmd := &cobra.Command{ - Use: "os", - Short: "list available operating systems", - Aliases: []string{"o"}, - Long: long, - Example: example, - } - - list := &cobra.Command{ - Use: "list", - Short: "list all available operating systems", - Aliases: []string{"l"}, - Long: listLong, - Example: listExample, - Run: func(cmd *cobra.Command, args []string) { - o.validate(cmd, args) - os, meta, err := o.List() - data := &printer.OS{OS: os, Meta: meta} - - fmt.Println(o.Base.Printer.Output) - o.Base.Printer.Display(data, err) - }, - } - - list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - list.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - cmd.AddCommand(list) - return cmd -} - -func (o *Options) validate(cmd *cobra.Command, args []string) { - o.Base.Args = args - o.Base.Options = utils.GetPaging(cmd) - o.Base.Printer.Output = viper.GetString("output") -} - -// List all os -func (o *Options) List() ([]govultr.OS, *govultr.Meta, error) { - return o.Base.Client.OS.List(context.Background(), o.Base.Options) -} diff --git a/cmd/operatingSystems/operatingSystems_test.go b/cmd/operatingSystems/operatingSystems_test.go deleted file mode 100644 index 2400ff1e..00000000 --- a/cmd/operatingSystems/operatingSystems_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package operatingSystems - -import ( - "context" - "reflect" - "testing" - - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/pkg/cli" -) - -type mockVultrOS struct { - client *govultr.Client -} - -func (m mockVultrOS) List(ctx context.Context, options *govultr.ListOptions) ([]govultr.OS, *govultr.Meta, error) { - return []govultr.OS{{ - ID: 127, - Name: "CentOS 6 x64", - Arch: "x64", - Family: "centos", - }}, &govultr.Meta{ - Total: 0, - Links: nil, - }, nil -} - -func TestOptions_List(t *testing.T) { - os := NewOSOptions(&cli.Base{Client: &govultr.Client{OS: mockVultrOS{nil}}}) - - expectedOS := []govultr.OS{{ - ID: 127, - Name: "CentOS 6 x64", - Arch: "x64", - Family: "centos", - }} - - expectedMeta := &govultr.Meta{ - Total: 0, - Links: nil, - } - - osList, meta, _ := os.List() - - if !reflect.DeepEqual(osList, expectedOS) { - t.Errorf("OSOptions.list returned %v expected %v", osList, expectedMeta) - } - - if !reflect.DeepEqual(expectedMeta, meta) { - t.Errorf("PlanOptions.metal returned %v expected %v", meta, expectedMeta) - } -} - -func TestNewCmdOS(t *testing.T) { - cmd := NewCmdOS(&cli.Base{Client: &govultr.Client{OS: mockVultrOS{nil}}}) - - if cmd.Short != "list available operating systems" { - t.Errorf("invalid short") - } - - if cmd.Use != "os" { - t.Errorf("invalid os") - } - - alias := []string{"o"} - if !reflect.DeepEqual(cmd.Aliases, alias) { - t.Errorf("expected alias %v got %v", alias, cmd.Aliases) - } -} - -func TestNewOSOptions(t *testing.T) { - options := NewOSOptions(&cli.Base{Client: &govultr.Client{OS: mockVultrOS{nil}}}) - - ref := reflect.TypeOf(options) - if _, ok := ref.MethodByName("List"); !ok { - t.Errorf("Missing list function") - } - - if _, ok := ref.MethodByName("validate"); ok { - t.Errorf("validate isn't exported shouldn't be accessible") - } - - oInterface := reflect.TypeOf(new(Interface)).Elem() - if !ref.Implements(oInterface) { - t.Errorf("Options does not implement Interface") - } -} diff --git a/cmd/operatingsystems/operatingsystems.go b/cmd/operatingsystems/operatingsystems.go new file mode 100644 index 00000000..d7254af0 --- /dev/null +++ b/cmd/operatingsystems/operatingsystems.go @@ -0,0 +1,90 @@ +// Package operatingsystems provides the operating systems functionality for +// the CLI +package operatingsystems + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `OS will retrieve available operating systems that can be deployed` + example = ` + # Example + vultr-cli os + ` + + listLong = `List all operating systems available to be deployed on Vultr` + listExample = ` + # Full example + vultr-cli os list + + # Full example with paging + vultr-cli os list --per-page=1 --cursor="bmV4dF9fMTI0" + + # Shortened with alias commands + vultr-cli o l + ` +) + +// NewCmdOS provides the command for operating systems to the CLI +func NewCmdOS(base *cli.Base) *cobra.Command { + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "os", + Short: "list available operating systems", + Aliases: []string{"o"}, + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "list all available operating systems", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + RunE: func(cmd *cobra.Command, args []string) error { + os, meta, err := o.list() + if err != nil { + return fmt.Errorf("error getting operating systems : %v", err) + } + + data := &OSPrinter{OperatingSystems: os, Meta: meta} + o.Base.Printer.Display(data, err) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + cmd.AddCommand(list) + return cmd +} + +type options struct { + Base *cli.Base +} + +func (o *options) list() ([]govultr.OS, *govultr.Meta, error) { + list, meta, _, err := o.Base.Client.OS.List(context.Background(), o.Base.Options) + return list, meta, err +} diff --git a/cmd/operatingsystems/printer.go b/cmd/operatingsystems/printer.go new file mode 100644 index 00000000..49dcd751 --- /dev/null +++ b/cmd/operatingsystems/printer.go @@ -0,0 +1,60 @@ +package operatingsystems + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// OSPrinter represents the plans data from the API +type OSPrinter struct { + OperatingSystems []govultr.OS `json:"os"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON provides the JSON formatted byte data +func (o *OSPrinter) JSON() []byte { + return printer.MarshalObject(o, "json") +} + +// YAML provides the YAML formatted byte data +func (o *OSPrinter) YAML() []byte { + return printer.MarshalObject(o, "yaml") +} + +// Columns provides the plan columns for the printer +func (o *OSPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "NAME", + "ARCH", + "FAMILY", + }} +} + +// Data provides the plan data for the printer +func (o *OSPrinter) Data() [][]string { + var data [][]string + + if len(o.OperatingSystems) == 0 { + data = append(data, []string{"---", "---", "---", "---"}) + return data + } + + for i := range o.OperatingSystems { + data = append(data, []string{ + strconv.Itoa(o.OperatingSystems[i].ID), + o.OperatingSystems[i].Name, + o.OperatingSystems[i].Arch, + o.OperatingSystems[i].Family, + }) + } + + return data +} + +// Paging validates and forms the paging data for output +func (o *OSPrinter) Paging() [][]string { + return printer.NewPaging(o.Meta.Total, &o.Meta.Links.Next, &o.Meta.Links.Prev).Compose() +} diff --git a/cmd/plans/plans.go b/cmd/plans/plans.go index 29348aba..4297d7a1 100644 --- a/cmd/plans/plans.go +++ b/cmd/plans/plans.go @@ -1,28 +1,14 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Package plans provides the functionality for the CLI to access plans package plans import ( "context" + "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" - "github.com/vultr/vultr-cli/cmd/utils" - "github.com/vultr/vultr-cli/pkg/cli" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" ) var ( @@ -44,8 +30,8 @@ var ( vultr-cli p l ` - metalLong = `List all available bare metal plans on Vultr.` - metalExample = ` + metalListLong = `Get plans for bare-metal servers` + metalListExample = ` # Full example vultr-cli plans metal @@ -57,27 +43,9 @@ var ( ` ) -// PlanOptionsInterface interface -type PlanOptionsInterface interface { - validate(cmd *cobra.Command, args []string) - List() ([]govultr.Plan, *govultr.Meta, error) - MetalList() ([]govultr.BareMetalPlan, *govultr.Meta, error) -} - -// PlanOptions struct specific for plans -type PlanOptions struct { - Base *cli.Base - PlanType string -} - -// NewPlanOptions returns a PlanOptions struct -func NewPlanOptions(Base *cli.Base) *PlanOptions { - return &PlanOptions{Base: Base} -} - // NewCmdPlan returns the cobra command for Plans -func NewCmdPlan(Base *cli.Base) *cobra.Command { - p := NewPlanOptions(Base) +func NewCmdPlan(base *cli.Base) *cobra.Command { + o := &options{Base: base} cmd := &cobra.Command{ Use: "plans", @@ -85,72 +53,101 @@ func NewCmdPlan(Base *cli.Base) *cobra.Command { Aliases: []string{"p", "plan"}, Long: planLong, Example: planExample, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + return nil + }, } list := &cobra.Command{ Use: "list", - Short: "list all instance plans", + Short: "List all instance plans", Aliases: []string{"l"}, Long: listLong, Example: listExample, - Run: func(cmd *cobra.Command, args []string) { - p.validate(cmd, args) - plans, meta, err := p.List() - data := &printer.Plans{Plan: plans, Meta: meta} - p.Base.Printer.Display(data, err) + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + planType, errTy := cmd.Flags().GetString("type") + if errTy != nil { + return fmt.Errorf("error parsing flag 'type' for plan list: %v", errTy) + } + + o.PlanType = planType + + plans, meta, err := o.list() + if err != nil { + return fmt.Errorf("error getting plans : %v", err) + } + + data := &PlansPrinter{Plans: plans, Meta: meta} + o.Base.Printer.Display(data, err) + + return nil }, } list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - list.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - list.Flags().StringP("type", "t", "", "(optional) The type of plans to return. Possible values: 'vc2', 'vdc', 'vhf', 'dedicated'. Defaults to all Instances plans.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + list.Flags().StringP( + "type", + "t", + "", + "(optional) The type of plans to return. Possible values: 'vc2', 'vdc', 'vhf', 'dedicated'. Defaults to all Instances plans.", + ) metal := &cobra.Command{ Use: "metal", - Short: "list all bare metal plans", + Short: "List all bare metal plans", Aliases: []string{"m"}, - Long: metalLong, - Example: metalExample, - Run: func(cmd *cobra.Command, args []string) { - p.validate(cmd, args) - m, meta, err := p.MetalList() - data := &printer.BaremetalPlans{ - Plan: m, - Meta: meta, + Long: metalListLong, + Example: metalListExample, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + m, meta, err := o.metalList() + if err != nil { + return fmt.Errorf("error getting bare metal plans : %v", err) } - p.Base.Printer.Display(data, err) + + data := &MetalPlansPrinter{Plans: m, Meta: meta} + o.Base.Printer.Display(data, err) + + return nil }, } metal.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - metal.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") + metal.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) cmd.AddCommand(list, metal) return cmd } -func (p *PlanOptions) validate(cmd *cobra.Command, args []string) { - p.Base.Args = args - p.Base.Options = utils.GetPaging(cmd) - p.PlanType, _ = cmd.Flags().GetString("type") - p.Base.Printer.Output = viper.GetString("output") +type options struct { + Base *cli.Base + PlanType string } -// List retrieves all available instance plans -func (p *PlanOptions) List() ([]govultr.Plan, *govultr.Meta, error) { - plans, meta, err := p.Base.Client.Plan.List(context.Background(), p.PlanType, p.Base.Options) - if err != nil { - return nil, nil, err - } - - return plans, meta, nil +func (o *options) validate(cmd *cobra.Command, args []string) { + o.Base.Args = args } -// MetalList retrieves all available bare metal plans -func (p *PlanOptions) MetalList() ([]govultr.BareMetalPlan, *govultr.Meta, error) { - plans, meta, err := p.Base.Client.Plan.ListBareMetal(context.Background(), p.Base.Options) - if err != nil { - return nil, nil, err - } +func (o *options) list() ([]govultr.Plan, *govultr.Meta, error) { + plans, meta, _, err := o.Base.Client.Plan.List(context.Background(), o.PlanType, o.Base.Options) + return plans, meta, err +} - return plans, meta, nil +func (o *options) metalList() ([]govultr.BareMetalPlan, *govultr.Meta, error) { + plans, meta, _, err := o.Base.Client.Plan.ListBareMetal(context.Background(), o.Base.Options) + return plans, meta, err } diff --git a/cmd/plans/plans_test.go b/cmd/plans/plans_test.go deleted file mode 100644 index f5a74554..00000000 --- a/cmd/plans/plans_test.go +++ /dev/null @@ -1,165 +0,0 @@ -package plans - -import ( - "context" - "reflect" - "testing" - - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/pkg/cli" -) - -type mockVultrPlans struct { - client *govultr.Client -} - -func (m mockVultrPlans) List(ctx context.Context, planType string, options *govultr.ListOptions) ([]govultr.Plan, *govultr.Meta, error) { - plans := []govultr.Plan{ - { - ID: "vhf-8c-32gb", - VCPUCount: 8, - RAM: 32768, - Disk: 512, - DiskCount: 1, - Bandwidth: 6144, - MonthlyCost: 192, - Type: "vhf", - Locations: []string{"ewr"}, - }, - } - - meta := &govultr.Meta{ - Total: 1, - Links: nil, - } - - return plans, meta, nil -} - -func (m mockVultrPlans) ListBareMetal(ctx context.Context, options *govultr.ListOptions) ([]govultr.BareMetalPlan, *govultr.Meta, error) { - metalPlans := []govultr.BareMetalPlan{ - { - ID: "vbm-4c-32gb", - CPUCount: 4, - CPUModel: "E3-1270v6", - CPUThreads: 8, - RAM: 8, - Disk: 20, - DiskCount: 1, - Bandwidth: 10, - MonthlyCost: 120, - Type: "NVMe", - Locations: []string{"ewr"}, - }, - } - meta := &govultr.Meta{ - Total: 1, - Links: nil, - } - return metalPlans, meta, nil -} - -func TestPlanOptions_List(t *testing.T) { - planOption := NewPlanOptions(&cli.Base{Client: &govultr.Client{Plan: mockVultrPlans{nil}}}) - - expectedPlan := []govultr.Plan{ - { - ID: "vhf-8c-32gb", - VCPUCount: 8, - RAM: 32768, - Disk: 512, - DiskCount: 1, - Bandwidth: 6144, - MonthlyCost: 192, - Type: "vhf", - Locations: []string{"ewr"}, - }, - } - expectedMeta := &govultr.Meta{ - Total: 1, - Links: nil, - } - - plan, meta, _ := planOption.List() - - if !reflect.DeepEqual(expectedPlan, plan) { - t.Errorf("PlanOptions.list returned %v expected %v", plan, expectedPlan) - } - - if !reflect.DeepEqual(expectedMeta, meta) { - t.Errorf("PlanOptions.list returned %v expected %v", plan, expectedPlan) - } -} - -func TestPlanOptions_Metal(t *testing.T) { - planMetal := NewPlanOptions(&cli.Base{Client: &govultr.Client{Plan: mockVultrPlans{nil}}}) - - expectedMetal := []govultr.BareMetalPlan{ - { - ID: "vbm-4c-32gb", - CPUCount: 4, - CPUModel: "E3-1270v6", - CPUThreads: 8, - RAM: 8, - Disk: 20, - DiskCount: 1, - Bandwidth: 10, - MonthlyCost: 120, - Type: "NVMe", - Locations: []string{"ewr"}, - }, - } - expectedMeta := &govultr.Meta{ - Total: 1, - Links: nil, - } - - plan, meta, _ := planMetal.MetalList() - - if !reflect.DeepEqual(expectedMetal, plan) { - t.Errorf("PlanOptions.list returned %v expected %v", plan, expectedMetal) - } - - if !reflect.DeepEqual(expectedMeta, meta) { - t.Errorf("PlanOptions.metal returned %v expected %v", meta, expectedMeta) - } -} - -func TestNewCmdPlan(t *testing.T) { - planOptions := NewPlanOptions(&cli.Base{Client: &govultr.Client{Plan: mockVultrPlans{nil}}}) - - ref := reflect.TypeOf(planOptions) - if _, ok := ref.MethodByName("List"); !ok { - t.Errorf("Missing list function") - } - - if _, ok := ref.MethodByName("MetalList"); !ok { - t.Errorf("Missing metal function") - } - - if _, ok := ref.MethodByName("validate"); ok { - t.Errorf("validate isn't exported shouldn't be accessible") - } - - pInterface := reflect.TypeOf(new(PlanOptionsInterface)).Elem() - if !ref.Implements(pInterface) { - t.Errorf("PlanOptions does not implement PlanOptionsInterface") - } -} - -func TestNewPlanOptions(t *testing.T) { - cmd := NewCmdPlan(&cli.Base{Client: &govultr.Client{Plan: mockVultrPlans{nil}}}) - - if cmd.Short != "get information about Vultr plans" { - t.Errorf("invalid short") - } - - if cmd.Use != "plans" { - t.Errorf("invalid plans") - } - - alias := []string{"p", "plan"} - if !reflect.DeepEqual(cmd.Aliases, alias) { - t.Errorf("expected alias %v got %v", alias, cmd.Aliases) - } -} diff --git a/cmd/plans/printer.go b/cmd/plans/printer.go new file mode 100644 index 00000000..ae4c9acc --- /dev/null +++ b/cmd/plans/printer.go @@ -0,0 +1,161 @@ +package plans + +import ( + "encoding/json" + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "gopkg.in/yaml.v3" +) + +// PlansPrinter represents the plans data from the API +type PlansPrinter struct { + Plans []govultr.Plan `json:"plans"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON provides the JSON formatted byte data +func (p *PlansPrinter) JSON() []byte { + js, err := json.MarshalIndent(p, "", " ") + if err != nil { + panic(err.Error()) + } + + return js +} + +// YAML provides the YAML formatted byte data +func (p *PlansPrinter) YAML() []byte { + yml, err := yaml.Marshal(p) + if err != nil { + panic(err.Error()) + } + return yml +} + +// Columns provides the plan columns for the printer +func (p *PlansPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "VCPU COUNT", + "RAM", + "DISK", + "DISK COUNT", + "BANDWIDTH GB", + "PRICE PER MONTH", + "TYPE", + "GPU VRAM", + "GPU TYPE", + "REGIONS", + }} +} + +// Data provides the plan data for the printer +func (p *PlansPrinter) Data() [][]string { + data := [][]string{} + + if len(p.Plans) == 0 { + data = append(data, []string{"---", "---", "---", "---", "---", "---", "---", "---", "---", "---", "---"}) + return data + } + + for i := range p.Plans { + data = append(data, []string{ + p.Plans[i].ID, + strconv.Itoa(p.Plans[i].VCPUCount), + strconv.Itoa(p.Plans[i].RAM), + strconv.Itoa(p.Plans[i].Disk), + strconv.Itoa(p.Plans[i].DiskCount), + strconv.Itoa(p.Plans[i].Bandwidth), + strconv.FormatFloat(float64(p.Plans[i].MonthlyCost), 'f', utils.DecimalPrecision, 32), + p.Plans[i].Type, + strconv.Itoa(p.Plans[i].GPUVRAM), + p.Plans[i].GPUType, + printer.ArrayOfStringsToString(p.Plans[i].Locations), + }) + } + + return data +} + +// Paging validates and forms the paging data for output +func (p *PlansPrinter) Paging() [][]string { + return printer.NewPaging(p.Meta.Total, &p.Meta.Links.Next, &p.Meta.Links.Prev).Compose() +} + +// MetalPlansPrinter represents the bare metal plans data from the API +type MetalPlansPrinter struct { + Plans []govultr.BareMetalPlan `json:"plans_metal"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON provides the JSON formatted byte data +func (m *MetalPlansPrinter) JSON() []byte { + js, err := json.MarshalIndent(m, "", " ") + if err != nil { + panic(err.Error()) + } + + return js +} + +// YAML provides the YAML formatted byte data +func (m *MetalPlansPrinter) YAML() []byte { + yml, err := yaml.Marshal(m) + if err != nil { + panic(err.Error()) + } + return yml +} + +// Columns provides the plan columns for the printer +func (m *MetalPlansPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "CPU COUNT", + "CPU MODEL", + "CPU THREADS", + "RAM", + "DISK", + "DISK COUNT", + "BANDWIDTH GB", + "PRICE PER MONTH", + "TYPE", + "REGIONS", + }} +} + +// Data provides the plan data for the printer +func (m *MetalPlansPrinter) Data() [][]string { + data := [][]string{} + + if len(data) == 0 { + data = append(data, []string{"---", "---", "---", "---", "---", "---", "---", "---", "---", "---", "---"}) + return data + } + + for i := range m.Plans { + data = append(data, []string{ + m.Plans[i].ID, + strconv.Itoa(m.Plans[i].CPUCount), + m.Plans[i].CPUModel, + strconv.Itoa(m.Plans[i].CPUThreads), + strconv.Itoa(m.Plans[i].RAM), + strconv.Itoa(m.Plans[i].Disk), + strconv.Itoa(m.Plans[i].DiskCount), + strconv.Itoa(m.Plans[i].Bandwidth), + strconv.FormatFloat(float64(m.Plans[i].MonthlyCost), 'f', utils.DecimalPrecision, 32), + m.Plans[i].Type, + printer.ArrayOfStringsToString(m.Plans[i].Locations), + }) + } + + return data +} + +// Paging validates and forms the paging data for output +func (m *MetalPlansPrinter) Paging() [][]string { + return printer.NewPaging(m.Meta.Total, &m.Meta.Links.Next, &m.Meta.Links.Prev).Compose() +} diff --git a/cmd/printer/application.go b/cmd/printer/application.go deleted file mode 100644 index 9752b4e2..00000000 --- a/cmd/printer/application.go +++ /dev/null @@ -1,52 +0,0 @@ -package printer - -import ( - "encoding/json" - - "github.com/go-yaml/yaml" - "github.com/vultr/govultr/v2" -) - -var _ ResourceOutput = &Applications{} - -type Applications struct { - Applications []govultr.Application `json:"applications"` - Meta *govultr.Meta -} - -func (a *Applications) JSON() []byte { - prettyJSON, err := json.MarshalIndent(a, "", " ") - if err != nil { - panic("move this into byte") - } - - return prettyJSON -} - -func (a *Applications) Yaml() []byte { - yam, err := yaml.Marshal(a) - if err != nil { - panic("move this into byte") - } - return yam -} - -func (a *Applications) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"ID", "NAME", "SHORT NAME", "DEPLOY NAME"}} -} - -func (a *Applications) Data() map[int][]interface{} { - data := map[int][]interface{}{} - for k, a := range a.Applications { - data[k] = []interface{}{a.ID, a.Name, a.ShortName, a.DeployName} - } - return data -} - -func (a *Applications) Paging() map[int][]interface{} { - return map[int][]interface{}{ - 0: {"======================================"}, - 1: {"TOTAL", "NEXT PAGE", "PREV PAGE"}, - 2: {a.Meta.Total, a.Meta.Links.Next, a.Meta.Links.Prev}, - } -} diff --git a/cmd/printer/backup.go b/cmd/printer/backup.go deleted file mode 100644 index cd272d44..00000000 --- a/cmd/printer/backup.go +++ /dev/null @@ -1,23 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func Backups(bs []govultr.Backup, meta *govultr.Meta) { - col := columns{"ID", "DATE CREATED", "DESCRIPTION", "SIZE", "STATUS"} - display(col) - for _, b := range bs { - display(columns{b.ID, b.DateCreated, b.Description, b.Size, b.Status}) - } - - Meta(meta) - flush() -} - -func Backup(bs *govultr.Backup) { - col := columns{"ID", "DATE CREATED", "DESCRIPTION", "SIZE", "STATUS"} - display(col) - - flush() -} diff --git a/cmd/printer/bareMetal.go b/cmd/printer/bareMetal.go deleted file mode 100644 index 59e86e99..00000000 --- a/cmd/printer/bareMetal.go +++ /dev/null @@ -1,60 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func BareMetal(b *govultr.BareMetalServer) { - col := columns{"ID", "IP", "TAG", "MAC ADDRESS", "LABEL", "OS", "STATUS", "REGION", "CPU", "RAM", "DISK", "FEATURES"} - display(col) - - display(columns{b.ID, b.MainIP, b.Tag, b.MacAddress, b.Label, b.Os, b.Status, b.Region, b.CPUCount, b.RAM, b.Disk, b.Features}) - - flush() -} - -func BareMetalList(bms []govultr.BareMetalServer, meta *govultr.Meta) { - col := columns{"ID", "IP", "TAG", "MAC ADDRESS", "LABEL", "OS", "STATUS", "REGION", "CPU", "RAM", "DISK", "FEATURES"} - display(col) - for _, b := range bms { - display(columns{b.ID, b.MainIP, b.Tag, b.MacAddress, b.Label, b.Os, b.Status, b.Region, b.CPUCount, b.RAM, b.Disk, b.Features}) - } - - Meta(meta) - - flush() -} - -func BareMetalBandwidth(bw *govultr.Bandwidth) { - display(columns{"DATE", "INCOMING BYTES", "OUTGOING BYTES"}) - for k, b := range bw.Bandwidth { - display(columns{k, b.IncomingBytes, b.OutgoingBytes}) - } - flush() -} - -func BareMetalIPV4Info(info []govultr.IPv4, meta *govultr.Meta) { - display(columns{"IP", "NETMASK", "GATEWAY", "TYPE"}) - for _, i := range info { - display(columns{i.IP, i.Netmask, i.Gateway, i.Type}) - } - - Meta(meta) - flush() -} - -func BareMetalIPV6Info(info []govultr.IPv6, meta *govultr.Meta) { - display(columns{"IP", "NETWORK", "NETWORK SIZE", "TYPE"}) - for _, i := range info { - display(columns{i.IP, i.Network, i.NetworkSize, i.Type}) - } - - Meta(meta) - flush() -} - -func BareMetalVNCUrl(vnc *govultr.VNCUrl) { - display(columns{"VNC URL"}) - display(columns{vnc.URL}) - flush() -} diff --git a/cmd/printer/blockStorage.go b/cmd/printer/blockStorage.go deleted file mode 100644 index 21c2b081..00000000 --- a/cmd/printer/blockStorage.go +++ /dev/null @@ -1,27 +0,0 @@ -package printer - -import ( - "fmt" - - "github.com/vultr/govultr/v2" -) - -func BlockStorage(bs []govultr.BlockStorage, meta *govultr.Meta) { - col := columns{"ID", "REGION ID", "INSTANCE ID", "SIZE GB", "STATUS", "LABEL", "DATE CREATED", "MONTHLY COST", "MOUNT ID"} - display(col) - for _, b := range bs { - cost := fmt.Sprintf("$%v", b.Cost) - display(columns{b.ID, b.Region, b.AttachedToInstance, b.SizeGB, b.Status, b.Label, b.DateCreated, cost, b.MountID}) - } - - Meta(meta) - flush() -} - -func SingleBlockStorage(b *govultr.BlockStorage) { - col := columns{"ID", "REGION ID", "INSTANCE ID", "SIZE GB", "STATUS", "LABEL", "DATE CREATED", "MONTHLY COST", "MOUNT ID"} - display(col) - cost := fmt.Sprintf("$%v", b.Cost) - display(columns{b.ID, b.Region, b.AttachedToInstance, b.SizeGB, b.Status, b.Label, b.DateCreated, cost, b.MountID}) - flush() -} diff --git a/cmd/printer/dnsDomain.go b/cmd/printer/dnsDomain.go deleted file mode 100644 index 1798dee3..00000000 --- a/cmd/printer/dnsDomain.go +++ /dev/null @@ -1,38 +0,0 @@ -package printer - -import "github.com/vultr/govultr/v2" - -func SecInfo(info []string) { - col := columns{"DNSSEC INFO"} - display(col) - for _, i := range info { - display(columns{i}) - } - flush() -} - -func DomainList(domain []govultr.Domain, meta *govultr.Meta) { - col := columns{"DOMAIN", "DATE CREATED"} - display(col) - for _, d := range domain { - display(columns{d.Domain, d.DateCreated}) - } - - Meta(meta) - flush() -} - -func Domain(domain *govultr.Domain) { - col := columns{"DOMAIN", "DATE CREATED"} - display(col) - display(columns{domain.Domain, domain.DateCreated}) - - flush() -} - -func SoaInfo(soa *govultr.Soa) { - col := columns{"NS PRIMARY", "EMAIL"} - display(col) - display(columns{soa.NSPrimary, soa.Email}) - flush() -} diff --git a/cmd/printer/dnsRecord.go b/cmd/printer/dnsRecord.go deleted file mode 100644 index e014abd4..00000000 --- a/cmd/printer/dnsRecord.go +++ /dev/null @@ -1,22 +0,0 @@ -package printer - -import "github.com/vultr/govultr/v2" - -func DnsRecordsList(records []govultr.DomainRecord, meta *govultr.Meta) { - col := columns{"ID", "TYPE", "NAME", "DATA", "PRIORITY", "TTL"} - display(col) - for _, r := range records { - display(columns{r.ID, r.Type, r.Name, r.Data, r.Priority, r.TTL}) - } - - Meta(meta) - flush() -} - -func DnsRecord(record *govultr.DomainRecord) { - col := columns{"ID", "TYPE", "NAME", "DATA", "PRIORITY", "TTL"} - display(col) - - display(columns{record.ID, record.Type, record.Name, record.Data, record.Priority, record.TTL}) - flush() -} diff --git a/cmd/printer/error.go b/cmd/printer/error.go index d4c6da4b..c147565f 100644 --- a/cmd/printer/error.go +++ b/cmd/printer/error.go @@ -2,6 +2,7 @@ package printer import ( "encoding/json" + "fmt" "os" ) @@ -11,20 +12,22 @@ type T struct { } func Error(err error) { - t := errorToStruct(err) + // TODO make errors uniform + // t := errorToStruct(err) col := columns{"ERROR MESSAGE", "STATUS CODE"} display(col) - display(columns{t.Error, t.Status}) + // display(columns{t.Error, t.Status}) + fmt.Printf("%v", err) flush() os.Exit(1) } -func errorToStruct(err error) *T { +func errorToStruct(err error) *T { //nolint:unused t := &T{} - if err := json.Unmarshal([]byte(err.Error()), t); err != nil { - panic(err) + if errMar := json.Unmarshal([]byte(err.Error()), t); errMar != nil { + panic(errMar) } return t } diff --git a/cmd/printer/firewallGroup.go b/cmd/printer/firewallGroup.go deleted file mode 100644 index b0f6f85a..00000000 --- a/cmd/printer/firewallGroup.go +++ /dev/null @@ -1,24 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func FirewallGroups(fwg []govultr.FirewallGroup, meta *govultr.Meta) { - col := columns{"ID", "DATE CREATED", "DATE MODIFIED", "INSTANCE COUNT", "RULE COUNT", "MAX RULE COUNT", "DESCRIPTION"} - display(col) - for _, f := range fwg { - display(columns{f.ID, f.DateCreated, f.DateModified, f.InstanceCount, f.RuleCount, f.MaxRuleCount, f.Description}) - } - - Meta(meta) - flush() -} - -func FirewallGroup(fwg *govultr.FirewallGroup) { - col := columns{"ID", "DATE CREATED", "DATE MODIFIED", "INSTANCE COUNT", "RULE COUNT", "MAX RULE COUNT", "DESCRIPTION"} - display(col) - - display(columns{fwg.ID, fwg.DateCreated, fwg.DateModified, fwg.InstanceCount, fwg.RuleCount, fwg.MaxRuleCount, fwg.Description}) - flush() -} diff --git a/cmd/printer/firewallRule.go b/cmd/printer/firewallRule.go deleted file mode 100644 index e1244bbb..00000000 --- a/cmd/printer/firewallRule.go +++ /dev/null @@ -1,24 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func FirewallRules(fwr []govultr.FirewallRule, meta *govultr.Meta) { - col := columns{"RULE NUMBER", "ACTION", "PROTOCOL", "PORT", "NETWORK", "NOTES"} - display(col) - for _, f := range fwr { - display(columns{f.ID, f.Action, f.Protocol, f.Port, f.Subnet, f.Notes}) - } - - Meta(meta) - flush() -} - -func FirewallRule(fwr *govultr.FirewallRule) { - col := columns{"RULE NUMBER", "ACTION", "PROTOCOL", "PORT", "NETWORK", "NOTES"} - display(col) - - display(columns{fwr.ID, fwr.Action, fwr.Protocol, fwr.Port, fwr.Subnet, fwr.Notes}) - flush() -} diff --git a/cmd/printer/generic.go b/cmd/printer/generic.go index 843ff786..079ab32b 100644 --- a/cmd/printer/generic.go +++ b/cmd/printer/generic.go @@ -3,15 +3,17 @@ package printer import ( "encoding/json" - "github.com/go-yaml/yaml" + "gopkg.in/yaml.v3" ) var _ ResourceOutput = &Generic{} +// Generic ... type Generic struct { - Message string + Message string `json:"message"` } +// JSON ... func (g *Generic) JSON() []byte { prettyJSON, err := json.MarshalIndent(g, "", " ") if err != nil { @@ -21,7 +23,8 @@ func (g *Generic) JSON() []byte { return prettyJSON } -func (g *Generic) Yaml() []byte { +// YAML ... +func (g *Generic) YAML() []byte { yam, err := yaml.Marshal(g) if err != nil { panic(err.Error()) @@ -29,14 +32,17 @@ func (g *Generic) Yaml() []byte { return yam } -func (g *Generic) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"message"}} +// Columns ... +func (g *Generic) Columns() [][]string { + return [][]string{0: {"MESSAGE"}} } -func (g *Generic) Data() map[int][]interface{} { - return map[int][]interface{}{0: {g.Message}} +// Data ... +func (g *Generic) Data() [][]string { + return [][]string{0: {g.Message}} } -func (g *Generic) Paging() map[int][]interface{} { +// Paging ... +func (g *Generic) Paging() [][]string { return nil } diff --git a/cmd/printer/info.go b/cmd/printer/info.go new file mode 100644 index 00000000..48cbbff9 --- /dev/null +++ b/cmd/printer/info.go @@ -0,0 +1,6 @@ +package printer + +// Info intializes and returns a generic resource output struct +func Info(msg string) *Generic { + return &Generic{Message: msg} +} diff --git a/cmd/printer/instance.go b/cmd/printer/instance.go deleted file mode 100644 index 75f9efc8..00000000 --- a/cmd/printer/instance.go +++ /dev/null @@ -1,128 +0,0 @@ -package printer - -import "github.com/vultr/govultr/v2" - -func InstanceBandwidth(bandwidth *govultr.Bandwidth) { - col := columns{"DATE", "INCOMING BYTES", "OUTGOING BYTES"} - display(col) - for k, b := range bandwidth.Bandwidth { - display(columns{k, b.IncomingBytes, b.OutgoingBytes}) - } - flush() -} - -func InstanceIPV4(ip []govultr.IPv4, meta *govultr.Meta) { - col := columns{"IP", "NETMASK", "GATEWAY", "TYPE", "REVERSE"} - display(col) - for _, i := range ip { - display(columns{i.IP, i.Netmask, i.Gateway, i.Type, i.Reverse}) - } - - Meta(meta) - flush() -} - -func InstanceIPV6(ip []govultr.IPv6, meta *govultr.Meta) { - col := columns{"IP", "NETWORK", "NETWORK SIZE", "TYPE"} - display(col) - for _, i := range ip { - display(columns{i.IP, i.Network, i.NetworkSize, i.Type}) - } - - Meta(meta) - flush() -} - -func InstanceList(instance []govultr.Instance, meta *govultr.Meta) { - col := columns{"ID", "IP", "LABEL", "OS", "STATUS", "Region", "CPU", "RAM", "DISK", "BANDWIDTH"} - display(col) - for _, s := range instance { - display(columns{s.ID, s.MainIP, s.Label, s.Os, s.Status, s.Region, s.VCPUCount, s.RAM, s.Disk, s.AllowedBandwidth}) - } - - Meta(meta) - flush() -} - -func Instance(instance *govultr.Instance) { - col := columns{"INSTANCE INFO"} - display(col) - display(columns{"ID", instance.ID}) - display(columns{"Os", instance.Os}) - display(columns{"RAM", instance.RAM}) - display(columns{"DISK", instance.Disk}) - display(columns{"MAIN IP", instance.MainIP}) - display(columns{"VCPU COUNT", instance.VCPUCount}) - display(columns{"REGION", instance.Region}) - display(columns{"DATE CREATED", instance.DateCreated}) - display(columns{"STATUS", instance.Status}) - display(columns{"ALLOWED BANDWIDTH", instance.AllowedBandwidth}) - display(columns{"NETMASK V4", instance.NetmaskV4}) - display(columns{"GATEWAY V4", instance.GatewayV4}) - display(columns{"POWER STATUS", instance.PowerStatus}) - display(columns{"SERVER STATE", instance.ServerStatus}) - display(columns{"PLAN", instance.Plan}) - display(columns{"LABEL", instance.Label}) - display(columns{"INTERNAL IP", instance.InternalIP}) - display(columns{"KVM URL", instance.KVM}) - display(columns{"TAG", instance.Tag}) - display(columns{"OsID", instance.OsID}) - display(columns{"AppID", instance.AppID}) - display(columns{"FIREWALL GROUP ID", instance.FirewallGroupID}) - display(columns{"V6 MAIN IP", instance.V6MainIP}) - display(columns{"V6 NETWORK", instance.V6Network}) - display(columns{"V6 NETWORK SIZE", instance.V6NetworkSize}) - display(columns{"FEATURES", instance.Features}) - - flush() -} - -func OsList(os []govultr.OS) { - col := columns{"ID", "NAME", "ARCH", "FAMILY"} - display(col) - for _, o := range os { - display(columns{o.ID, o.Name, o.Arch, o.Family}) - } - flush() -} - -func AppList(app []govultr.Application) { - col := columns{"ID", "NAME", "SHORT NAME", "DEPLOY NAME"} - display(col) - for _, a := range app { - display(columns{a.ID, a.Name, a.ShortName, a.DeployName}) - } - flush() -} - -func BackupsGet(b *govultr.BackupSchedule) { - col := columns{"ENABLED", "CRON TYPE", "NEXT RUN", "HOUR", "DOW", "DOM"} - display(col) - display(columns{*b.Enabled, b.Type, b.NextScheduleTimeUTC, b.Hour, b.Dow, b.Dom}) - flush() -} - -func IsoStatus(iso *govultr.Iso) { - col := columns{"ISO ID", "STATE"} - display(col) - display(columns{iso.IsoID, iso.State}) - flush() -} - -func PlansList(plans []string) { - col := columns{"PLAN NAME"} - display(col) - for _, p := range plans { - display(columns{p}) - } - flush() -} - -func ReverseIpv6(rip []govultr.ReverseIP) { - col := columns{"IP", "REVERSE"} - display(col) - for _, r := range rip { - display(columns{r.IP, r.Reverse}) - } - flush() -} diff --git a/cmd/printer/iso.go b/cmd/printer/iso.go deleted file mode 100644 index 276311f1..00000000 --- a/cmd/printer/iso.go +++ /dev/null @@ -1,32 +0,0 @@ -package printer - -import "github.com/vultr/govultr/v2" - -func IsoPrivates(iso []govultr.ISO, meta *govultr.Meta) { - col := columns{"ID", "FILE NAME", "SIZE", "STATUS", "MD5SUM", "SHA512SUM", "DATE CREATED"} - display(col) - for _, i := range iso { - display(columns{i.ID, i.FileName, i.Size, i.Status, i.MD5Sum, i.SHA512Sum, i.DateCreated}) - } - - Meta(meta) - flush() -} - -func IsoPrivate(iso *govultr.ISO) { - col := columns{"ID", "FILE NAME", "SIZE", "STATUS", "MD5SUM", "SHA512SUM", "DATE CREATED"} - display(col) - display(columns{iso.ID, iso.FileName, iso.Size, iso.Status, iso.MD5Sum, iso.SHA512Sum, iso.DateCreated}) - flush() -} - -func IsoPublic(iso []govultr.PublicISO, meta *govultr.Meta) { - col := columns{"ID", "NAME", "DESCRIPTION"} - display(col) - for _, i := range iso { - display(columns{i.ID, i.Name, i.Description}) - } - - Meta(meta) - flush() -} diff --git a/cmd/printer/loadBalancer.go b/cmd/printer/loadBalancer.go deleted file mode 100644 index d1a6173f..00000000 --- a/cmd/printer/loadBalancer.go +++ /dev/null @@ -1,88 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func LoadBalancerList(loadbalancer []govultr.LoadBalancer, meta *govultr.Meta) { - for _, lb := range loadbalancer { - display(columns{"ID", lb.ID}) - display(columns{"DATE CREATED", lb.DateCreated}) - display(columns{"REGION", lb.Region}) - display(columns{"LABEL", lb.Label}) - display(columns{"STATUS", lb.Status}) - display(columns{"IPV4", lb.IPV4}) - display(columns{"IPV6", lb.IPV6}) - display(columns{"HAS SSL", *lb.SSLInfo}) - display(columns{"INSTANCES", lb.Instances}) - - display(columns{" "}) - display(columns{"HEALTH CHECKS"}) - display(columns{"PROTOCOL", "PORT", "PATH", "CHECK INTERVAL", "RESPONSE TIMEOUT", "UNHEALTHY THRESHOLD", "HEALTHY THRESHOLD"}) - display(columns{lb.HealthCheck.Protocol, lb.HealthCheck.Port, lb.HealthCheck.Path, lb.HealthCheck.CheckInterval, lb.HealthCheck.ResponseTimeout, lb.HealthCheck.UnhealthyThreshold, lb.HealthCheck.HealthyThreshold}) - - display(columns{" "}) - display(columns{"GENERIC INFO"}) - display(columns{"BALANCING ALGORITHM", "SSL REDIRECT", "COOKIE NAME", "PROXY PROTOCOL"}) - display(columns{lb.GenericInfo.BalancingAlgorithm, *lb.GenericInfo.SSLRedirect, lb.GenericInfo.StickySessions.CookieName, *lb.GenericInfo.ProxyProtocol}) - - display(columns{" "}) - display(columns{"FORWARDING RULES"}) - display(columns{"RULEID", "FRONTEND PROTOCOL", "FRONTEND PORT", "BACKEND PROTOCOL", "BACKEND PORT"}) - for _, r := range lb.ForwardingRules { - display(columns{r.RuleID, r.FrontendProtocol, r.FrontendPort, r.BackendProtocol, r.BackendPort}) - } - display(columns{"---------------------------"}) - } - - Meta(meta) - flush() -} - -func LoadBalancer(lb *govultr.LoadBalancer) { - display(columns{"ID", lb.ID}) - display(columns{"DATE CREATED", lb.DateCreated}) - display(columns{"REGION", lb.Region}) - display(columns{"LABEL", lb.Label}) - display(columns{"STATUS", lb.Status}) - display(columns{"IPV4", lb.IPV4}) - display(columns{"IPV6", lb.IPV6}) - display(columns{"HAS SSL", *lb.SSLInfo}) - display(columns{"INSTANCES", lb.Instances}) - - display(columns{" "}) - display(columns{"HEALTH CHECKS"}) - display(columns{"PROTOCOL", "PORT", "PATH", "CHECK INTERVAL", "RESPONSE TIMEOUT", "UNHEALTHY THRESHOLD", "HEALTHY THRESHOLD"}) - display(columns{lb.HealthCheck.Protocol, lb.HealthCheck.Port, lb.HealthCheck.Path, lb.HealthCheck.CheckInterval, lb.HealthCheck.ResponseTimeout, lb.HealthCheck.UnhealthyThreshold, lb.HealthCheck.HealthyThreshold}) - - display(columns{" "}) - display(columns{"GENERIC INFO"}) - display(columns{"BALANCING ALGORITHM", "SSL REDIRECT", "COOKIE NAME", "PROXY PROTOCOL"}) - display(columns{lb.GenericInfo.BalancingAlgorithm, *lb.GenericInfo.SSLRedirect, lb.GenericInfo.StickySessions.CookieName, *lb.GenericInfo.ProxyProtocol}) - - display(columns{" "}) - display(columns{"FORWARDING RULES"}) - display(columns{"RULEID", "FRONTEND PROTOCOL", "FRONTEND PORT", "BACKEND PROTOCOL", "BACKEND PORT"}) - for _, r := range lb.ForwardingRules { - display(columns{r.RuleID, r.FrontendProtocol, r.FrontendPort, r.BackendProtocol, r.BackendPort}) - } - flush() -} - -func LoadBalancerRuleList(rules []govultr.ForwardingRule, meta *govultr.Meta) { - display(columns{"RULEID", "FRONTEND PROTOCOL", "FRONTEND PORT", "BACKEND PROTOCOL", "BACKEND PORT"}) - - for _, r := range rules { - display(columns{r.RuleID, r.FrontendProtocol, r.FrontendPort, r.BackendProtocol, r.BackendPort}) - } - - Meta(meta) - flush() -} - -func LoadBalancerRule(rule *govultr.ForwardingRule) { - display(columns{"RULEID", "FRONTEND PROTOCOL", "FRONTEND PORT", "BACKEND PROTOCOL", "BACKEND PORT"}) - display(columns{rule.RuleID, rule.FrontendProtocol, rule.FrontendPort, rule.BackendProtocol, rule.BackendPort}) - - flush() -} diff --git a/cmd/printer/network.go b/cmd/printer/network.go deleted file mode 100644 index ab846a3b..00000000 --- a/cmd/printer/network.go +++ /dev/null @@ -1,21 +0,0 @@ -package printer - -import "github.com/vultr/govultr/v2" - -func NetworkList(network []govultr.Network, meta *govultr.Meta) { - col := columns{"ID", "REGION", "DESCRIPTION", "V4 SUBNET", "V4 SUBNET MASK", "DATE CREATED"} - display(col) - for _, n := range network { - display(columns{n.NetworkID, n.Region, n.Description, n.V4Subnet, n.V4SubnetMask, n.DateCreated}) - } - - Meta(meta) - flush() -} - -func Network(network *govultr.Network) { - display(columns{"ID", "REGION", "DESCRIPTION", "V4 SUBNET", "V4 SUBNET MASK", "DATE CREATED"}) - display(columns{network.NetworkID, network.Region, network.Description, network.V4Subnet, network.V4SubnetMask, network.DateCreated}) - - flush() -} diff --git a/cmd/printer/objectStorage.go b/cmd/printer/objectStorage.go deleted file mode 100644 index b7105b3c..00000000 --- a/cmd/printer/objectStorage.go +++ /dev/null @@ -1,39 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func ObjectStorages(obj []govultr.ObjectStorage, meta *govultr.Meta) { - display(columns{"ID", "REGION", "OBJSTORECLUSTER ID", "STATUS", "LABEL", "DATE CREATED", "S3 HOSTNAME", "S3 ACCESS KEY", "S3 SECRET KEY"}) - for _, o := range obj { - vals := columns{o.ID, o.Region, o.ObjectStoreClusterID, o.Status, o.Label, o.DateCreated, o.S3Keys.S3Hostname, o.S3Keys.S3AccessKey, o.S3Keys.S3SecretKey} - display(vals) - } - - Meta(meta) - flush() -} - -func SingleObjectStorage(obj *govultr.ObjectStorage) { - display(columns{"ID", "REGION", "OBJSTORECLUSTER ID", "STATUS", "LABEL", "DATE CREATED", "S3 HOSTNAME", "S3 ACCESS KEY", "S3 SECRET KEY"}) - display(columns{obj.ID, obj.Region, obj.ObjectStoreClusterID, obj.Status, obj.Label, obj.DateCreated, obj.S3Keys.S3Hostname, obj.S3Keys.S3AccessKey, obj.S3Keys.S3SecretKey}) - - flush() -} - -func ObjectStorageClusterList(cluster []govultr.ObjectStorageCluster, meta *govultr.Meta) { - display(columns{"OBJSTORECLUSTER", "REGION ID", "HOSTNAME", "DEPLOY"}) - for _, c := range cluster { - display(columns{c.ID, c.Region, c.Hostname, c.Deploy}) - } - - Meta(meta) - flush() -} - -func ObjStorageS3KeyRegenerate(key *govultr.S3Keys) { - display(columns{"S3 HOSTNAME", "S3 ACCESS KEY", "S3 SECRET KEY"}) - display(columns{key.S3Hostname, key.S3AccessKey, key.S3SecretKey}) - flush() -} diff --git a/cmd/printer/os.go b/cmd/printer/os.go deleted file mode 100644 index 3d9eb359..00000000 --- a/cmd/printer/os.go +++ /dev/null @@ -1,50 +0,0 @@ -package printer - -import ( - "encoding/json" - "github.com/go-yaml/yaml" - "github.com/vultr/govultr/v2" -) - -var _ ResourceOutput = &OS{} - -type OS struct { - OS []govultr.OS - Meta *govultr.Meta -} - -func (o *OS) JSON() []byte { - prettyJSON, err := json.MarshalIndent(o, "", " ") - if err != nil { - panic("move this into byte") - } - - return prettyJSON -} - -func (o *OS) Yaml() []byte { - yam, err := yaml.Marshal(o) - if err != nil { - panic("move this into byte") - } - return yam -} - -func (o *OS) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"ID", "NAME", "ARCH", "FAMILY"}} -} -func (o *OS) Data() map[int][]interface{} { - data := map[int][]interface{}{} - for k, os := range o.OS { - data[k] = []interface{}{os.ID, os.Name, os.Arch, os.Family} - } - return data -} - -func (o *OS) Paging() map[int][]interface{} { - return map[int][]interface{}{ - 0: {"======================================"}, - 1: {"TOTAL", "NEXT PAGE", "PREV PAGE"}, - 2: {o.Meta.Total, o.Meta.Links.Next, o.Meta.Links.Prev}, - } -} diff --git a/cmd/printer/plans.go b/cmd/printer/plans.go deleted file mode 100644 index 7e6865a3..00000000 --- a/cmd/printer/plans.go +++ /dev/null @@ -1,96 +0,0 @@ -package printer - -import ( - "encoding/json" - - "github.com/go-yaml/yaml" - "github.com/vultr/govultr/v2" -) - -var _ ResourceOutput = &Plans{} - -type Plans struct { - Plan []govultr.Plan - Meta *govultr.Meta -} - -func (p *Plans) JSON() []byte { - prettyJSON, err := json.MarshalIndent(p, "", " ") - if err != nil { - panic("move this into byte") - } - - return prettyJSON -} - -func (p *Plans) Yaml() []byte { - yam, err := yaml.Marshal(p) - if err != nil { - panic("move this into byte") - } - return yam -} - -func (p *Plans) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"ID", "VCPU COUNT", "RAM", "DISK", "DISK COUNT", "BANDWIDTH GB", "PRICE PER MONTH", "TYPE", "REGIONS"}} -} - -func (p *Plans) Data() map[int][]interface{} { - data := map[int][]interface{}{} - for k, p := range p.Plan { - data[k] = []interface{}{p.ID, p.VCPUCount, p.RAM, p.Disk, p.DiskCount, p.Bandwidth, p.MonthlyCost, p.Type, p.Locations} - } - return data -} - -func (p *Plans) Paging() map[int][]interface{} { - return map[int][]interface{}{ - 0: {"======================================"}, - 1: {"TOTAL", "NEXT PAGE", "PREV PAGE"}, - 2: {p.Meta.Total, p.Meta.Links.Next, p.Meta.Links.Prev}, - } -} - -var _ ResourceOutput = &BaremetalPlans{} - -type BaremetalPlans struct { - Plan []govultr.BareMetalPlan - Meta *govultr.Meta -} - -func (b *BaremetalPlans) JSON() []byte { - prettyJSON, err := json.MarshalIndent(b, "", " ") - if err != nil { - panic("move this into byte") - } - - return prettyJSON -} - -func (b *BaremetalPlans) Yaml() []byte { - yam, err := yaml.Marshal(b) - if err != nil { - panic("move this into byte") - } - return yam -} - -func (b *BaremetalPlans) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"ID", "CPU COUNT", "CPU MODEL", "CPU THREADS", "RAM", "DISK", "DISK COUNT", "BANDWIDTH GB", "PRICE PER MONTH", "TYPE", "REGIONS"}} -} - -func (b *BaremetalPlans) Data() map[int][]interface{} { - data := map[int][]interface{}{} - for k, p := range b.Plan { - data[k] = []interface{}{p.ID, p.CPUCount, p.CPUModel, p.CPUThreads, p.RAM, p.Disk, p.DiskCount, p.Bandwidth, p.MonthlyCost, p.Type, p.Locations} - } - return data -} - -func (b *BaremetalPlans) Paging() map[int][]interface{} { - return map[int][]interface{}{ - 0: {"======================================"}, - 1: {"TOTAL", "NEXT PAGE", "PREV PAGE"}, - 2: {b.Meta.Total, b.Meta.Links.Next, b.Meta.Links.Prev}, - } -} diff --git a/cmd/printer/printer.go b/cmd/printer/printer.go index d0a9ecb7..d135d8f3 100644 --- a/cmd/printer/printer.go +++ b/cmd/printer/printer.go @@ -1,20 +1,34 @@ +// Package printer provides the console printing functionality for the CLI package printer import ( + "encoding/json" "fmt" "os" + "strconv" "strings" "text/tabwriter" - "github.com/vultr/govultr/v2" + "github.com/vultr/govultr/v3" + "gopkg.in/yaml.v3" +) + +const ( + twMinWidth int = 0 + twTabWidth int = 8 + twPadding int = 2 + twPadChar byte = '\t' + twFlags uint = 0 + emptyPlaceholder string = "---" + JSONIndent string = " " ) type ResourceOutput interface { JSON() []byte - Yaml() []byte - Columns() map[int][]interface{} - Data() map[int][]interface{} - Paging() map[int][]interface{} + YAML() []byte + Columns() [][]string + Data() [][]string + Paging() [][]string } type Printer interface { @@ -28,16 +42,26 @@ type Output struct { Output string } -type columns2 map[int][]interface{} type columns []interface{} var tw = new(tabwriter.Writer) func init() { - tw.Init(os.Stdout, 0, 8, 2, '\t', 0) + tw.Init( + os.Stdout, + twMinWidth, + twTabWidth, + twPadding, + twPadChar, + twFlags, + ) } +// Display confirms the output format then displays the ResourceOutput data to +// the CLI. If there is an error, that is displayed instead via Error func (o *Output) Display(r ResourceOutput, err error) { + defer o.flush() + if err != nil { //todo move this so it can follow the flow of the other printers and support json/yaml Error(err) @@ -45,10 +69,10 @@ func (o *Output) Display(r ResourceOutput, err error) { if strings.ToLower(o.Output) == "json" { o.displayNonText(r.JSON()) - os.Exit(1) + os.Exit(0) } else if strings.ToLower(o.Output) == "yaml" { - o.displayNonText(r.Yaml()) - os.Exit(1) + o.displayNonText(r.YAML()) + os.Exit(0) } o.display(r.Columns()) @@ -56,33 +80,140 @@ func (o *Output) Display(r ResourceOutput, err error) { if r.Paging() != nil { o.display(r.Paging()) } - defer o.flush() } -func (o *Output) display(d columns2) { - for _, values := range d { - for i, value := range values { +func (o *Output) display(d [][]string) { + for n := range d { + for i := range d[n] { format := "\t%s" if i == 0 { format = "%s" } - fmt.Fprintf(tw, format, fmt.Sprintf("%v", value)) + fmt.Fprintf(tw, format, fmt.Sprintf("%v", d[n][i])) } fmt.Fprintf(tw, "\n") } } func (o *Output) flush() { - tw.Flush() + if err := tw.Flush(); err != nil { + panic(fmt.Errorf("unable to flush display : %v", err)) + } } func (o *Output) displayNonText(data []byte) { fmt.Printf("%s\n", string(data)) } -//////////////////////////////////////////////////////////////// -func display(values columns) { +// Paging struct holds the values used by the Meta section in the printer +// output +type Paging struct { + Total int + CursorNext string + CursorPrev string +} + +// NewPaging validates and intializes the paging data +func NewPaging(total int, next, prev *string) *Paging { + p := new(Paging) + p.Total = total + + if next != nil { + p.CursorNext = *next + } else { + p.CursorNext = emptyPlaceholder + } + + if prev != nil { + p.CursorPrev = *prev + } else { + p.CursorPrev = emptyPlaceholder + } + + return p +} + +// Compose returns the paging data for output +func (p *Paging) Compose() [][]string { + var display [][]string + display = append(display, + []string{"======================================"}, + []string{"TOTAL", "NEXT PAGE", "PREV PAGE"}, + []string{strconv.Itoa(p.Total), p.CursorNext, p.CursorPrev}, + ) + + return display +} +// Total holds the values used by the Meta section in the printer +// output for outputs that don't include paging cursors +type Total struct { + Total int +} + +// Compose returns the total data for output +func (t *Total) Compose() [][]string { + var display [][]string + display = append(display, + []string{"======================================"}, + []string{"TOTAL"}, + []string{strconv.Itoa(t.Total)}, + ) + + return display +} + +// MarshalObject ... +func MarshalObject(input interface{}, format string) []byte { + var output []byte + if format == "json" { + j, errJ := json.MarshalIndent(input, "", JSONIndent) + if errJ != nil { + panic(fmt.Errorf("error marshaling JSON : %v", errJ)) + } + output = j + } else if format == "yaml" { + y, errY := yaml.Marshal(input) + if errY != nil { + panic(fmt.Errorf("error marshaling YAML : %v", errY)) + } + output = y + } + + return output +} + +// ArrayOfStringsToString will build a delimited string from an array for +// display in the printer functions. It defaults to comma-delimited and +// enclosed in square brackets to maintain consistency with array Fprintf +func ArrayOfStringsToString(a []string) string { + delimiter := ", " + var sb strings.Builder + sb.WriteString("[") + sb.WriteString(strings.Join(a, delimiter)) + sb.WriteString("]") + + return sb.String() +} + +// ArrayOfIntsToString will build a delimited string from an array for +// display in the printer functions. It defaults to comma-delimited and +// enclosed in square brackets to maintain consistency with array Fprintf +func ArrayOfIntsToString(a []int) string { + delimiter := ", " + var sb strings.Builder + sb.WriteString("[") + for i := range a { + sb.WriteString(strconv.Itoa(a[i])) + sb.WriteString(delimiter) + } + sb.WriteString("]") + + return sb.String() +} + +// OLD funcs to be re-written ////////////////////////////////////////////////////////////// +func display(values columns) { for i, value := range values { format := "\t%s" if i == 0 { @@ -93,14 +224,43 @@ func display(values columns) { fmt.Fprintf(tw, "\n") } +// displayString will `Fprintln` a string to the tabwriter +func displayString(message string) { + fmt.Fprintln(tw, message) +} + func flush() { - tw.Flush() + if err := tw.Flush(); err != nil { + panic("could not flush buffer") + } } +// Meta prints out the pagination details TODO: old func Meta(meta *govultr.Meta) { - display(columns{"======================================"}) - col := columns{"TOTAL", "NEXT PAGE", "PREV PAGE"} - display(col) + var pageNext string + var pagePrev string + + if meta.Links.Next == "" { + pageNext = "---" + } else { + pageNext = meta.Links.Next + } + + if meta.Links.Prev == "" { + pagePrev = "---" + } else { + pagePrev = meta.Links.Prev + } + + displayString("======================================") + display(columns{"TOTAL", "NEXT PAGE", "PREV PAGE"}) + display(columns{meta.Total, pageNext, pagePrev}) +} + +// MetaDBaaS prints out the pagination details used by database commands +func MetaDBaaS(meta *govultr.Meta) { + displayString("======================================") + display(columns{"TOTAL"}) - display(columns{meta.Total, meta.Links.Next, meta.Links.Prev}) + display(columns{meta.Total}) } diff --git a/cmd/printer/regions.go b/cmd/printer/regions.go deleted file mode 100644 index 31e0eff8..00000000 --- a/cmd/printer/regions.go +++ /dev/null @@ -1,88 +0,0 @@ -package printer - -import ( - "encoding/json" - - "github.com/go-yaml/yaml" - "github.com/vultr/govultr/v2" -) - -var _ ResourceOutput = &Regions{} - -type Regions struct { - Regions []govultr.Region `json:"regions"` - Meta *govultr.Meta -} - -func (r *Regions) JSON() []byte { - prettyJSON, err := json.MarshalIndent(r, "", " ") - if err != nil { - panic("move this into byte") - } - - return prettyJSON -} - -func (r *Regions) Yaml() []byte { - yam, err := yaml.Marshal(r) - if err != nil { - panic("move this into byte") - } - return yam -} - -func (r *Regions) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"ID", "CITY", "COUNTRY", "CONTINENT", "OPTIONS"}} -} -func (r *Regions) Data() map[int][]interface{} { - data := map[int][]interface{}{} - for k, r := range r.Regions { - data[k] = []interface{}{r.ID, r.City, r.Country, r.Continent, r.Options} - } - return data -} - -func (r *Regions) Paging() map[int][]interface{} { - return map[int][]interface{}{ - 0: {"======================================"}, - 1: {"TOTAL", "NEXT PAGE", "PREV PAGE"}, - 2: {r.Meta.Total, r.Meta.Links.Next, r.Meta.Links.Prev}, - } -} - -type RegionsAvailability struct { - AvailablePlans *govultr.PlanAvailability `json:"available_plans"` -} - -func (r *RegionsAvailability) JSON() []byte { - prettyJSON, err := json.MarshalIndent(r.AvailablePlans, "", " ") - if err != nil { - panic("move this into byte") - } - - return prettyJSON -} - -func (r *RegionsAvailability) Yaml() []byte { - yam, err := yaml.Marshal(r.AvailablePlans) - if err != nil { - panic("move this into byte") - } - return yam -} - -func (r *RegionsAvailability) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"AVAILABLE PLANS"}} -} - -func (r *RegionsAvailability) Data() map[int][]interface{} { - data := map[int][]interface{}{} - for k, r := range r.AvailablePlans.AvailablePlans { - data[k] = []interface{}{r} - } - return data -} - -func (r RegionsAvailability) Paging() map[int][]interface{} { - return nil -} diff --git a/cmd/printer/reservedIP.go b/cmd/printer/reservedIP.go deleted file mode 100644 index 89c45b4e..00000000 --- a/cmd/printer/reservedIP.go +++ /dev/null @@ -1,22 +0,0 @@ -package printer - -import "github.com/vultr/govultr/v2" - -func ReservedIPList(reservedIP []govultr.ReservedIP, meta *govultr.Meta) { - col := columns{"ID", "REGION", "IP TYPE", "SUBNET", "SUBNET SIZE", "LABEL", "ATTACHED TO"} - display(col) - for _, r := range reservedIP { - display(columns{r.ID, r.Region, r.IPType, r.Subnet, r.SubnetSize, r.Label, r.InstanceID}) - } - - Meta(meta) - flush() -} - -func ReservedIP(reservedIP *govultr.ReservedIP) { - col := columns{"ID", "REGION", "IP TYPE", "SUBNET", "SUBNET SIZE", "LABEL", "ATTACHED TO"} - display(col) - display(columns{reservedIP.ID, reservedIP.Region, reservedIP.IPType, reservedIP.Subnet, reservedIP.SubnetSize, reservedIP.Label, reservedIP.InstanceID}) - - flush() -} diff --git a/cmd/printer/script.go b/cmd/printer/script.go deleted file mode 100644 index 25204dc5..00000000 --- a/cmd/printer/script.go +++ /dev/null @@ -1,22 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func ScriptList(script []govultr.StartupScript, meta *govultr.Meta) { - col := columns{"ID", "DATE CREATED", "DATE MODIFIED", "TYPE", "NAME"} - display(col) - for _, s := range script { - display(columns{s.ID, s.DateCreated, s.DateModified, s.Type, s.Name}) - } - - Meta(meta) - flush() -} - -func Script(script *govultr.StartupScript) { - display(columns{"ID", "DATE CREATED", "DATE MODIFIED", "TYPE", "NAME"}) - display(columns{script.ID, script.DateCreated, script.DateModified, script.Type, script.Name}) - flush() -} diff --git a/cmd/printer/snapshot.go b/cmd/printer/snapshot.go deleted file mode 100644 index 9526474b..00000000 --- a/cmd/printer/snapshot.go +++ /dev/null @@ -1,23 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func Snapshot(snapshot *govultr.Snapshot) { - display(columns{"ID", "DATE CREATED", "SIZE", "STATUS", "OSID", "APPID", "DESCRIPTION"}) - display(columns{snapshot.ID, snapshot.DateCreated, snapshot.Size, snapshot.Status, snapshot.OsID, snapshot.AppID, snapshot.Description}) - - flush() -} - -func Snapshots(snapshot []govultr.Snapshot, meta *govultr.Meta) { - col := columns{"ID", "DATE CREATED", "SIZE", "STATUS", "OSID", "APPID", "DESCRIPTION"} - display(col) - for _, s := range snapshot { - display(columns{s.ID, s.DateCreated, s.Size, s.Status, s.OsID, s.AppID, s.Description}) - } - - Meta(meta) - flush() -} diff --git a/cmd/printer/user.go b/cmd/printer/user.go deleted file mode 100644 index 6b15966e..00000000 --- a/cmd/printer/user.go +++ /dev/null @@ -1,23 +0,0 @@ -package printer - -import ( - "github.com/vultr/govultr/v2" -) - -func Users(user []govultr.User, meta *govultr.Meta) { - col := columns{"ID", "NAME", "EMAIL", "API", "ACL"} - display(col) - for _, u := range user { - display(columns{u.ID, u.Name, u.Email, *u.APIEnabled, u.ACL}) - } - - Meta(meta) - flush() -} - -func User(user *govultr.User) { - display(columns{"ID", "NAME", "EMAIL", "API", "ACL"}) - display(columns{user.ID, user.Name, user.Email, *user.APIEnabled, user.ACL}) - - flush() -} diff --git a/cmd/printer/userData.go b/cmd/printer/userData.go deleted file mode 100644 index 365e2723..00000000 --- a/cmd/printer/userData.go +++ /dev/null @@ -1,20 +0,0 @@ -package printer - -import ( - "encoding/base64" - "fmt" - "os" - - "github.com/vultr/govultr/v2" -) - -func UserData(u *govultr.UserData) { - display(columns{"USERDATA"}) - data, err := base64.StdEncoding.DecodeString(u.Data) - if err != nil { - fmt.Printf("Error decoding user-data: %v\n", err) - os.Exit(1) - } - display(columns{string(data)}) - flush() -} diff --git a/cmd/printer/version.go b/cmd/printer/version.go deleted file mode 100644 index c55a9039..00000000 --- a/cmd/printer/version.go +++ /dev/null @@ -1,44 +0,0 @@ -package printer - -import ( - "encoding/json" - - "github.com/go-yaml/yaml" -) - -var _ ResourceOutput = &Version{} - -type Version struct { - Version string -} - -func (v *Version) JSON() []byte { - prettyJSON, err := json.MarshalIndent(v, "", " ") - if err != nil { - panic("move this into byte") - } - - return prettyJSON -} - -func (v *Version) Yaml() []byte { - yam, err := yaml.Marshal(v) - if err != nil { - panic("move this into byte") - } - return yam -} - -func (v *Version) Columns() map[int][]interface{} { - return map[int][]interface{}{0: {"version"}} - -} - -func (v *Version) Data() map[int][]interface{} { - return map[int][]interface{}{0: {v.Version}} - -} - -func (v Version) Paging() map[int][]interface{} { - return nil -} diff --git a/cmd/regions/printer.go b/cmd/regions/printer.go new file mode 100644 index 00000000..34d2724c --- /dev/null +++ b/cmd/regions/printer.go @@ -0,0 +1,111 @@ +package regions + +import ( + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// RegionsPrinter represents the regions data from the API and contains the +// methods to format and print the data via the ResourceOutput interface +type RegionsPrinter struct { + Regions []govultr.Region `json:"regions"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON provides the JSON formatted byte data +func (r *RegionsPrinter) JSON() []byte { + return printer.MarshalObject(r, "json") +} + +// YAML provides the YAML formatted byte data +func (r *RegionsPrinter) YAML() []byte { + return printer.MarshalObject(r, "yaml") +} + +// Columns provides the columns for the printer +func (r *RegionsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "CITY", + "COUNTRY", + "CONTINENT", + "OPTIONS", + }} +} + +// Data provides the data for the printer +func (r *RegionsPrinter) Data() [][]string { + data := [][]string{} + + if len(r.Regions) == 0 { + data = append(data, []string{"---", "---", "---", "---", "---"}) + return data + } + + for i := range r.Regions { + data = append(data, []string{ + r.Regions[i].ID, + r.Regions[i].City, + r.Regions[i].Country, + r.Regions[i].Continent, + printer.ArrayOfStringsToString(r.Regions[i].Options), + }) + } + + return data +} + +// Paging validates and forms the paging data for output +func (r *RegionsPrinter) Paging() [][]string { + return printer.NewPaging(r.Meta.Total, &r.Meta.Links.Next, &r.Meta.Links.Prev).Compose() +} + +// ====================================== + +// RegionsAvailabilityPrinter represents the plan availability data for a +// region from the API and contains the methods to format and print the data +// via the ResourceOutput interface +type RegionsAvailabilityPrinter struct { + Plans *govultr.PlanAvailability `json:"available_plans"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON provides the JSON formatted byte data +func (r *RegionsAvailabilityPrinter) JSON() []byte { + return printer.MarshalObject(r, "json") +} + +// YAML provides the YAML formatted byte data +func (r *RegionsAvailabilityPrinter) YAML() []byte { + return printer.MarshalObject(r, "yaml") +} + +// Columns provides the available plans columns for the printer +func (r *RegionsAvailabilityPrinter) Columns() [][]string { + return [][]string{0: { + "AVAILABLE PLANS", + }} +} + +// Data provides the region availability plan data for the printer +func (r *RegionsAvailabilityPrinter) Data() [][]string { + data := [][]string{} + + if len(r.Plans.AvailablePlans) == 0 { + data = append(data, []string{"---"}) + return data + } + + for i := range r.Plans.AvailablePlans { + data = append(data, []string{ + r.Plans.AvailablePlans[i], + }) + } + + return data +} + +// Paging validates and forms the paging data for output +func (r *RegionsAvailabilityPrinter) Paging() [][]string { + return printer.NewPaging(r.Meta.Total, &r.Meta.Links.Next, &r.Meta.Links.Prev).Compose() +} diff --git a/cmd/regions/regions.go b/cmd/regions/regions.go index 62024286..e185c7a9 100644 --- a/cmd/regions/regions.go +++ b/cmd/regions/regions.go @@ -1,29 +1,15 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Package regions provides the functionality for the CLI to access regions package regions import ( "context" "errors" + "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" - "github.com/vultr/vultr-cli/cmd/utils" - "github.com/vultr/vultr-cli/pkg/cli" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" ) var ( @@ -58,90 +44,101 @@ var ( ` ) -// Interface for regions -type Interface interface { - Availability() (*govultr.PlanAvailability, error) - List() ([]govultr.Region, *govultr.Meta, error) - validate(cmd *cobra.Command, args []string) -} - -// Options for regions -type Options struct { - Base *cli.Base - PlanType string -} - -// NewRegionOptions returns Options struct -func NewRegionOptions(base *cli.Base) *Options { - return &Options{Base: base} -} - // NewCmdRegion creates a cobra command for Regions func NewCmdRegion(base *cli.Base) *cobra.Command { - o := NewRegionOptions(base) + o := &options{Base: base} cmd := &cobra.Command{ Use: "regions", - Short: "get regions", + Short: "Get regions", Aliases: []string{"r", "region"}, Long: regionLong, Example: regionExample, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + return nil + }, } list := &cobra.Command{ Use: "list", - Short: "list regions", + Short: "List regions", Aliases: []string{"l"}, Long: listLong, Example: listExample, - Run: func(cmd *cobra.Command, args []string) { - o.validate(cmd, args) + RunE: func(cmd *cobra.Command, args []string) error { o.Base.Options = utils.GetPaging(cmd) - regions, meta, err := o.List() - data := &printer.Regions{Regions: regions, Meta: meta} + + regions, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving region list : %v", err) + } + + data := &RegionsPrinter{Regions: regions, Meta: meta} o.Base.Printer.Display(data, err) + + return nil }, } - list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - list.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") + list.Flags().StringP("cursor", "c", "", "(optional) cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) availability := &cobra.Command{ - Use: "availability ", - Short: "list available plans in region", + Use: "availability ", + Short: "List available plans in region", Aliases: []string{"a"}, Long: availLong, Example: availExample, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return errors.New("please provide a regionID") + return errors.New("please provide a region ID") } return nil }, - Run: func(cmd *cobra.Command, args []string) { - o.validate(cmd, args) - avail, err := o.Availability() - data := &printer.RegionsAvailability{AvailablePlans: avail} + RunE: func(cmd *cobra.Command, args []string) error { + avail, err := o.availability() + if err != nil { + return fmt.Errorf("error retrieving region availability : %v", err) + } + + data := &RegionsAvailabilityPrinter{Plans: avail} o.Base.Printer.Display(data, err) + + return nil }, } - availability.Flags().StringP("type", "t", "", "type of plans for which to include availability. Possible values: 'vc2', 'vdc, 'vhf', 'vbm'. Defaults to all Instances plans.") + availability.Flags().StringP( + "type", + "t", + "", + `type of plans for which to include availability. Possible values: +'vc2', 'vdc, 'vhf', 'vbm'. Defaults to all Instances plans.`, + ) + + cmd.AddCommand( + list, + availability, + ) - cmd.AddCommand(list, availability) return cmd } -func (o *Options) validate(cmd *cobra.Command, args []string) { - o.Base.Args = args - o.PlanType, _ = cmd.Flags().GetString("type") - o.Base.Printer.Output = viper.GetString("output") +type options struct { + Base *cli.Base + PlanType string } -// List all regions -func (o *Options) List() ([]govultr.Region, *govultr.Meta, error) { - return o.Base.Client.Region.List(context.Background(), o.Base.Options) +func (o *options) list() ([]govultr.Region, *govultr.Meta, error) { + list, meta, _, err := o.Base.Client.Region.List(context.Background(), o.Base.Options) + return list, meta, err } -// Availability returns all available plans for a given region -func (o *Options) Availability() (*govultr.PlanAvailability, error) { - return o.Base.Client.Region.Availability(context.Background(), o.Base.Args[0], o.PlanType) +func (o *options) availability() (*govultr.PlanAvailability, error) { + avail, _, err := o.Base.Client.Region.Availability(context.Background(), o.Base.Args[0], o.PlanType) + return avail, err } diff --git a/cmd/regions/regions_test.go b/cmd/regions/regions_test.go deleted file mode 100644 index 22598a9e..00000000 --- a/cmd/regions/regions_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package regions - -import ( - "context" - "reflect" - "testing" - - "github.com/vultr/vultr-cli/pkg/cli" - - "github.com/vultr/govultr/v2" -) - -type mockVultrRegions struct { - client *govultr.Client -} - -func (m mockVultrRegions) Availability(ctx context.Context, regionID string, planType string) (*govultr.PlanAvailability, error) { - return &govultr.PlanAvailability{AvailablePlans: []string{"1"}}, nil -} - -func (m mockVultrRegions) List(ctx context.Context, options *govultr.ListOptions) ([]govultr.Region, *govultr.Meta, error) { - return []govultr.Region{{ - ID: "ewr", - City: "NJ", - Country: "US", - Continent: "NA", - Options: []string{"test"}, - }}, &govultr.Meta{ - Total: 0, - Links: nil, - }, nil -} - -func TestOptions_List(t *testing.T) { - avail := NewRegionOptions(&cli.Base{Client: &govultr.Client{Region: mockVultrRegions{nil}}}) - - expected := []govultr.Region{{ - ID: "ewr", - City: "NJ", - Country: "US", - Continent: "NA", - Options: []string{"test"}, - }} - expectedMeta := &govultr.Meta{ - Total: 0, - Links: nil, - } - - regions, meta, _ := avail.List() - if !reflect.DeepEqual(expected, regions) { - t.Errorf("RegionOptions.list returned %v expected %v", regions, expected) - } - - if !reflect.DeepEqual(expectedMeta, meta) { - t.Errorf("RegionOptions.list returned %v expected %v", meta, expectedMeta) - } -} - -func TestOptions_Availability(t *testing.T) { - avail := NewRegionOptions(&cli.Base{Client: &govultr.Client{Region: mockVultrRegions{nil}}, Args: []string{"test"}}) - expected := &govultr.PlanAvailability{AvailablePlans: []string{"1"}} - - a, _ := avail.Availability() - if !reflect.DeepEqual(expected, a) { - t.Errorf("RegionAvailability.list returned %v expected %v", a, expected) - } -} - -func TestNewCmdRegion(t *testing.T) { - cmd := NewCmdRegion(&cli.Base{Client: &govultr.Client{Region: mockVultrRegions{nil}}}) - - if cmd.Short != "get regions" { - t.Errorf("invalid short") - } - - if cmd.Use != "regions" { - t.Errorf("invalid regions") - } - - alias := []string{"r", "region"} - if !reflect.DeepEqual(cmd.Aliases, alias) { - t.Errorf("expected alias %v got %v", alias, cmd.Aliases) - } -} - -func TestNewRegionOptions(t *testing.T) { - options := NewRegionOptions(&cli.Base{Client: &govultr.Client{Region: mockVultrRegions{nil}}}) - - ref := reflect.TypeOf(options) - if _, ok := ref.MethodByName("List"); !ok { - t.Errorf("Missing list function") - } - - if _, ok := ref.MethodByName("validate"); ok { - t.Errorf("validate isn't exported shouldn't be accessible") - } - - rInterface := reflect.TypeOf(new(Interface)).Elem() - if !ref.Implements(rInterface) { - t.Errorf("Options does not implement Interface") - } -} diff --git a/cmd/reservedIP.go b/cmd/reservedIP.go deleted file mode 100644 index b3c41dae..00000000 --- a/cmd/reservedIP.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// ReservedIP represents the reservedip command -func ReservedIP() *cobra.Command { - reservedIPCmd := &cobra.Command{ - Use: "reserved-ip", - Aliases: []string{"rip"}, - Short: "reserved-ip lets you interact with reserved-ip ", - Long: ``, - } - - reservedIPCmd.AddCommand(reservedIPGet, reservedIPList, reservedIPDelete, reservedIPAttach, reservedIPDetach, reservedIPConvert, reservedIPCreate) - - // List - reservedIPList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - reservedIPList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - // Attach - reservedIPAttach.Flags().StringP("instance-id", "i", "", "id of instance you want to attach") - reservedIPAttach.MarkFlagRequired("instance-id") - - // Convert - reservedIPConvert.Flags().StringP("ip", "i", "", "ip you wish to convert") - reservedIPConvert.MarkFlagRequired("ip") - reservedIPConvert.Flags().StringP("label", "l", "", "label") - - // Create - reservedIPCreate.Flags().StringP("region", "r", "", "id of region") - reservedIPCreate.MarkFlagRequired("region") - reservedIPCreate.Flags().StringP("type", "t", "", "type of IP : v4 or v6") - reservedIPCreate.MarkFlagRequired("type") - reservedIPCreate.Flags().StringP("label", "l", "", "label") - - return reservedIPCmd -} - -var reservedIPGet = &cobra.Command{ - Use: "get ", - Short: "delete a reserved ip", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a reservedIP ID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - ip := args[0] - if err := client.ReservedIP.Delete(context.Background(), ip); err != nil { - fmt.Printf("error getting reserved IPs : %v\n", err) - os.Exit(1) - } - - fmt.Println("Deleted reserved ip") - }, -} - -var reservedIPAttach = &cobra.Command{ - Use: "attach ", - Short: "attach a reservedIP to an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a reservedIP ID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - ip := args[0] - instance, _ := cmd.Flags().GetString("instance-id") - if err := client.ReservedIP.Attach(context.Background(), ip, instance); err != nil { - fmt.Printf("error attaching reserved IPs : %v\n", err) - os.Exit(1) - } - - fmt.Println("Attached reserved ip") - }, -} - -var reservedIPDetach = &cobra.Command{ - Use: "detach ", - Short: "detach a reservedIP to an instance", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a reservedIP ID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - ip := args[0] - if err := client.ReservedIP.Detach(context.Background(), ip); err != nil { - fmt.Printf("error detaching reserved IPs : %v\n", err) - os.Exit(1) - } - - fmt.Println("Detached reserved ip") - }, -} - -var reservedIPConvert = &cobra.Command{ - Use: "convert ", - Short: "convert IP address to reservedIP", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - ip, _ := cmd.Flags().GetString("ip") - label, _ := cmd.Flags().GetString("label") - options := &govultr.ReservedIPConvertReq{ - IPAddress: ip, - Label: label, - } - - r, err := client.ReservedIP.Convert(context.Background(), options) - if err != nil { - fmt.Printf("error converting IP to reserved IPs : %v\n", err) - os.Exit(1) - } - - printer.ReservedIP(r) - }, -} - -var reservedIPCreate = &cobra.Command{ - Use: "create ", - Short: "create reservedIP", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - region, _ := cmd.Flags().GetString("region") - ipType, _ := cmd.Flags().GetString("type") - label, _ := cmd.Flags().GetString("label") - - options := &govultr.ReservedIPReq{ - Region: region, - IPType: ipType, - Label: label, - } - - r, err := client.ReservedIP.Create(context.Background(), options) - if err != nil { - fmt.Printf("error creating reserved IPs : %v\n", err) - os.Exit(1) - } - - printer.ReservedIP(r) - }, -} diff --git a/cmd/reservedip/printer.go b/cmd/reservedip/printer.go new file mode 100644 index 00000000..00e5056c --- /dev/null +++ b/cmd/reservedip/printer.go @@ -0,0 +1,114 @@ +package reservedip + +import ( + "strconv" + + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// ReservedIPsPrinter ... +type ReservedIPsPrinter struct { + IPs []govultr.ReservedIP `json:"reserved_ips"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (r *ReservedIPsPrinter) JSON() []byte { + return printer.MarshalObject(r, "json") +} + +// YAML ... +func (r *ReservedIPsPrinter) YAML() []byte { + return printer.MarshalObject(r, "yaml") +} + +// Columns ... +func (r *ReservedIPsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "REGION", + "IP TYPE", + "SUBNET", + "SUBNET SIZE", + "LABEL", + "ATTACHED TO", + }} +} + +// Data ... +func (r *ReservedIPsPrinter) Data() [][]string { + if len(r.IPs) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range r.IPs { + data = append(data, []string{ + r.IPs[i].ID, + r.IPs[i].Region, + r.IPs[i].IPType, + r.IPs[i].Subnet, + strconv.Itoa(r.IPs[i].SubnetSize), + r.IPs[i].Label, + r.IPs[i].InstanceID, + }) + } + + return data +} + +// Paging ... +func (r *ReservedIPsPrinter) Paging() [][]string { + return printer.NewPaging(r.Meta.Total, &r.Meta.Links.Next, &r.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ReservedIPPrinter ... +type ReservedIPPrinter struct { + IP *govultr.ReservedIP `json:"reserved_ip"` +} + +// JSON ... +func (r *ReservedIPPrinter) JSON() []byte { + return printer.MarshalObject(r, "json") +} + +// YAML ... +func (r *ReservedIPPrinter) YAML() []byte { + return printer.MarshalObject(r, "yaml") +} + +// Columns ... +func (r *ReservedIPPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "REGION", + "IP TYPE", + "SUBNET", + "SUBNET SIZE", + "LABEL", + "ATTACHED TO", + }} +} + +// Data ... +func (r *ReservedIPPrinter) Data() [][]string { + return [][]string{0: { + r.IP.ID, + r.IP.Region, + r.IP.IPType, + r.IP.Subnet, + strconv.Itoa(r.IP.SubnetSize), + r.IP.Label, + r.IP.InstanceID, + }} +} + +// Paging ... +func (r *ReservedIPPrinter) Paging() [][]string { + return nil +} + +// ====================================== diff --git a/cmd/reservedip/reservedip.go b/cmd/reservedip/reservedip.go new file mode 100644 index 00000000..acecffa4 --- /dev/null +++ b/cmd/reservedip/reservedip.go @@ -0,0 +1,449 @@ +// Package reservedip provides the reserved-ip commands to the CLI +package reservedip + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +var ( + long = `Get all available commands for reserved IPs` + example = ` + # Full example + vultr-cli reserved-ip + + # Shortened with aliased commands + vultr-cli rip + ` + + createLong = `Create a reserved IP on your Vultr account` + createExample = ` + # Full Example + vultr-cli reserved-ip create --region="yto" --type="v4" --label="new IP" + + # Shortened with alias commands + vultr-cli rip c -r="yto" -t="v4" -l="new IP" + ` + + getLong = `Get info for a reserved IP on your Vultr account` + getExample = ` + # Full example + vultr-cli reserved-ip get 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 + + # Shortened with alias commands + vultr-cli rip g 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 + ` + + listLong = `List all reserved IPs on your Vultr account` + listExample = ` + # Full example + vultr-cli reserved-ip list + + # Shortened with alias commands + vultr-cli rip l + ` + + attachLong = `Attach a reserved IP to an instance on your Vultr account` + attachExample = ` + # Full example + vultr-cli reserved-ip attach 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 --instance-id="2b9bf5fb-1644-4e0a-b706-1116ab64d783" + + # Shortened with alias commands + vultr-cli rip a 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 -i="2b9bf5fb-1644-4e0a-b706-1116ab64d783" + ` + + detachLong = `Detach a reserved IP from an instance on your Vultr account` + detachExample = ` + # Full example + vultr-cli reserved-ip detach 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 + + # Shortened with alias commands + vultr-cli rip d 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 + ` + + convertLong = `Convert an instance IP to a reserved IP on your Vultr account` + convertExample = ` + # Full example + vultr-cli reserved-ip convert --ip="192.0.2.123" --label="new label converted" + + # Shortened with alias commands + vultr-cli rip v -i="192.0.2.123" -l="new label converted" + ` + + updateLong = `Update a reserved IP on your Vultr account` + updateExample = ` + # Full example + vultr-cli reserved-ip update 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 --label="new label" + + # Shortened with alias commands + vultr-cli rip u 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 -l="new label" + ` + + deleteLong = `Delete a reserved IP from your Vultr account` + deleteExample = ` + # Full example + vultr-cli reserved-ip delete 6a31648d-ebfa-4d43-9a00-9c9f0e5048f5 + ` +) + +// NewCmdReservedIP provides the CLI command for reserved IP functions +func NewCmdReservedIP(base *cli.Base) *cobra.Command { //nolint:gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "reserved-ip", + Short: "Commands to interact with reserved IPs", + Aliases: []string{"rip"}, + Long: long, + Example: example, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "List all reserved IPs", + Aliases: []string{"l"}, + Long: listLong, + Example: listExample, + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + ips, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving reserved IP list : %v", err) + } + + data := &ReservedIPsPrinter{IPs: ips, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get + get := &cobra.Command{ + Use: "get ", + Short: "Get a reserved IP", + Long: getLong, + Example: getExample, + Aliases: []string{"g"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a reserved IP ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + ip, err := o.get() + if err != nil { + return fmt.Errorf("error getting reserved IP : %v", err) + } + + data := &ReservedIPPrinter{IP: ip} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + // Create + create := &cobra.Command{ + Use: "create ", + Short: "Create reserved IP", + Aliases: []string{"c"}, + Long: createLong, + Example: createExample, + RunE: func(cmd *cobra.Command, args []string) error { + region, errRe := cmd.Flags().GetString("region") + if errRe != nil { + return fmt.Errorf("error parsing flag 'region' for reserved-ip create : %v", errRe) + } + + ipType, errIP := cmd.Flags().GetString("type") + if errIP != nil { + return fmt.Errorf("error parsing flag 'type' for reserved-ip create : %v", errIP) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for reserved-ip create : %v", errLa) + } + + o.CreateReq = &govultr.ReservedIPReq{ + Region: region, + IPType: ipType, + Label: label, + } + + ip, err := o.create() + if err != nil { + return fmt.Errorf("error creating reserved IP : %v", err) + } + + data := &ReservedIPPrinter{IP: ip} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + create.Flags().StringP("region", "r", "", "id of region") + if err := create.MarkFlagRequired("region"); err != nil { + fmt.Printf("error marking reserved-ip create 'region' flag required: %v", err) + os.Exit(1) + } + create.Flags().StringP("type", "t", "", "type of IP : v4 or v6") + if err := create.MarkFlagRequired("type"); err != nil { + fmt.Printf("error marking reserved-ip create 'type' flag required: %v", err) + os.Exit(1) + } + create.Flags().StringP("label", "l", "", "label") + + // Update + update := &cobra.Command{ + Use: "update ", + Short: "Update reserved IP", + Aliases: []string{"u"}, + Long: updateLong, + Example: updateExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a reserved IP ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for reserved-ip update : %v", errLa) + } + + o.UpdateReq = &govultr.ReservedIPUpdateReq{ + Label: govultr.StringToStringPtr(label), + } + + ip, err := o.update() + if err != nil { + return fmt.Errorf("error updating reserved IP : %v", err) + } + + data := &ReservedIPPrinter{IP: ip} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + update.Flags().StringP("label", "l", "", "label") + if err := update.MarkFlagRequired("label"); err != nil { + fmt.Printf("error marking reserved-ip update 'label' flag required: %v", err) + os.Exit(1) + } + + // Attach + attach := &cobra.Command{ + Use: "attach ", + Short: "Attach a reserved IP to an instance", + Aliases: []string{"a"}, + Long: attachLong, + Example: attachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a reserved IP ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + instanceID, errIn := cmd.Flags().GetString("instance-id") + if errIn != nil { + return fmt.Errorf("error parsing flag 'instance-id' for reserved-ip attach : %v", errIn) + } + + o.InstanceID = instanceID + + if err := o.attach(); err != nil { + return fmt.Errorf("error attaching reserved IP : %v", err) + } + + o.Base.Printer.Display(printer.Info("reserved IP has been attached to instance"), nil) + + return nil + }, + } + + attach.Flags().StringP("instance-id", "i", "", "id of instance you want to attach") + if err := attach.MarkFlagRequired("instance-id"); err != nil { + fmt.Printf("error marking reserved-ip attach 'instance-id' flag required: %v", err) + os.Exit(1) + } + + // Detach + detach := &cobra.Command{ + Use: "detach ", + Short: "Detach a reserved IP from an instance", + Aliases: []string{"d"}, + Long: detachLong, + Example: detachExample, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a reserved IP ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.detach(); err != nil { + return fmt.Errorf("error detaching reserved IP : %v", err) + } + + o.Base.Printer.Display(printer.Info("reserved IP has been detached"), nil) + + return nil + }, + } + + // Convert + convert := &cobra.Command{ + Use: "convert ", + Short: "Convert IP address to reserved IP", + Aliases: []string{"v"}, + Long: convertLong, + Example: convertExample, + RunE: func(cmd *cobra.Command, args []string) error { + ip, errIP := cmd.Flags().GetString("ip") + if errIP != nil { + return fmt.Errorf("error parsing flag 'ip' for reserved-ip convert : %v", errIP) + } + + label, errLa := cmd.Flags().GetString("label") + if errLa != nil { + return fmt.Errorf("error parsing flag 'label' for reserved-ip convert : %v", errLa) + } + + o.ConvertReq = &govultr.ReservedIPConvertReq{ + IPAddress: ip, + Label: label, + } + + newIP, err := o.convert() + if err != nil { + return fmt.Errorf("error converting reserved IP : %v", err) + } + + data := &ReservedIPPrinter{IP: newIP} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + convert.Flags().StringP("ip", "i", "", "ip you wish to convert") + if err := convert.MarkFlagRequired("ip"); err != nil { + fmt.Printf("error marking reserved-ip convert 'ip' flag required: %v", err) + os.Exit(1) + } + convert.Flags().StringP("label", "l", "", "label") + + // Delete + del := &cobra.Command{ + Use: "delete ", + Short: "delete a reserved ip", + Long: deleteLong, + Example: deleteExample, + Aliases: []string{"destroy"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("please provide a reserved IP ID") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.detach(); err != nil { + return fmt.Errorf("error detaching reserved IP : %v", err) + } + + o.Base.Printer.Display(printer.Info("reserved IP has been detached"), nil) + + return nil + }, + } + + cmd.AddCommand( + list, + get, + create, + update, + attach, + detach, + convert, + del, + ) + + return cmd +} + +type options struct { + Base *cli.Base + CreateReq *govultr.ReservedIPReq + UpdateReq *govultr.ReservedIPUpdateReq + ConvertReq *govultr.ReservedIPConvertReq + InstanceID string +} + +func (o *options) list() ([]govultr.ReservedIP, *govultr.Meta, error) { + rips, meta, _, err := o.Base.Client.ReservedIP.List(o.Base.Context, o.Base.Options) + return rips, meta, err +} + +func (o *options) get() (*govultr.ReservedIP, error) { + rip, _, err := o.Base.Client.ReservedIP.Get(o.Base.Context, o.Base.Args[0]) + return rip, err +} + +func (o *options) create() (*govultr.ReservedIP, error) { + rip, _, err := o.Base.Client.ReservedIP.Create(o.Base.Context, o.CreateReq) + return rip, err +} + +func (o *options) update() (*govultr.ReservedIP, error) { + rip, _, err := o.Base.Client.ReservedIP.Update(o.Base.Context, o.Base.Args[0], o.UpdateReq) + return rip, err +} + +func (o *options) attach() error { + return o.Base.Client.ReservedIP.Attach(o.Base.Context, o.Base.Args[0], o.InstanceID) +} + +func (o *options) detach() error { + return o.Base.Client.ReservedIP.Detach(o.Base.Context, o.Base.Args[0]) +} + +func (o *options) convert() (*govultr.ReservedIP, error) { + ip, _, err := o.Base.Client.ReservedIP.Convert(o.Base.Context, o.ConvertReq) + return ip, err +} + +func (o *options) del() error { //nolint:unused + return o.Base.Client.ReservedIP.Delete(o.Base.Context, o.Base.Args[0]) +} diff --git a/cmd/root.go b/cmd/root.go index 51af2d55..1f164892 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,100 +1,122 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - +// Package cmd implements the command line commands relevant to the vultr-cli package cmd import ( "fmt" "os" - - "github.com/vultr/vultr-cli/cmd/account" - "github.com/vultr/vultr-cli/cmd/operatingSystems" - "github.com/vultr/vultr-cli/cmd/sshkeys" + "path/filepath" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/applications" - "github.com/vultr/vultr-cli/cmd/plans" - "github.com/vultr/vultr-cli/cmd/regions" - "github.com/vultr/vultr-cli/cmd/users" - "github.com/vultr/vultr-cli/cmd/version" - "github.com/vultr/vultr-cli/pkg/cli" + "github.com/vultr/vultr-cli/v3/cmd/account" + "github.com/vultr/vultr-cli/v3/cmd/applications" + "github.com/vultr/vultr-cli/v3/cmd/backups" + "github.com/vultr/vultr-cli/v3/cmd/baremetal" + "github.com/vultr/vultr-cli/v3/cmd/billing" + "github.com/vultr/vultr-cli/v3/cmd/blockstorage" + "github.com/vultr/vultr-cli/v3/cmd/containerregistry" + "github.com/vultr/vultr-cli/v3/cmd/database" + "github.com/vultr/vultr-cli/v3/cmd/dns" + "github.com/vultr/vultr-cli/v3/cmd/firewall" + "github.com/vultr/vultr-cli/v3/cmd/instance" + "github.com/vultr/vultr-cli/v3/cmd/iso" + "github.com/vultr/vultr-cli/v3/cmd/kubernetes" + "github.com/vultr/vultr-cli/v3/cmd/loadbalancer" + "github.com/vultr/vultr-cli/v3/cmd/marketplace" + "github.com/vultr/vultr-cli/v3/cmd/objectstorage" + "github.com/vultr/vultr-cli/v3/cmd/operatingsystems" + "github.com/vultr/vultr-cli/v3/cmd/plans" + "github.com/vultr/vultr-cli/v3/cmd/regions" + "github.com/vultr/vultr-cli/v3/cmd/reservedip" + "github.com/vultr/vultr-cli/v3/cmd/script" + "github.com/vultr/vultr-cli/v3/cmd/snapshot" + "github.com/vultr/vultr-cli/v3/cmd/sshkeys" + "github.com/vultr/vultr-cli/v3/cmd/users" + "github.com/vultr/vultr-cli/v3/cmd/version" + "github.com/vultr/vultr-cli/v3/cmd/vpc" + "github.com/vultr/vultr-cli/v3/cmd/vpc2" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +const ( + userAgent = "vultr-cli/" + version.Version + perPageDefault int = 100 ) var ( cfgFile string output string - base *cli.Base - client *govultr.Client ) // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "vultr-cli", - Short: "vultr-cli is a command line interface for the Vultr API", - Long: ``, - Aliases: []string{"vultrctl"}, + Use: "vultr-cli", + Short: "vultr-cli is a command line interface for the Vultr API", + Long: ``, + SilenceUsage: true, } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := rootCmd.Execute(); err != nil { - fmt.Println(err) os.Exit(1) } } func init() { + // init the config file with viper initConfig() rootCmd.PersistentFlags().StringVar(&cfgFile, "config", configHome(), "config file (default is $HOME/.vultr-cli.yaml)") - viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config")) - - rootCmd.PersistentFlags().StringVar(&output, "output", "text", "out of data json | yaml | text. text is default") - viper.BindPFlag("output", rootCmd.PersistentFlags().Lookup("output")) - - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - rootCmd.AddCommand(account.NewCmdAccount(base)) - rootCmd.AddCommand(applications.NewCmdApplications(base)) - rootCmd.AddCommand(Backups()) - rootCmd.AddCommand(BareMetal()) - rootCmd.AddCommand(BlockStorageCmd()) - rootCmd.AddCommand(DNS()) - rootCmd.AddCommand(Firewall()) - rootCmd.AddCommand(ISO()) - rootCmd.AddCommand(LoadBalancer()) - rootCmd.AddCommand(Network()) - rootCmd.AddCommand(operatingSystems.NewCmdOS(base)) - rootCmd.AddCommand(ObjectStorageCmd()) - rootCmd.AddCommand(plans.NewCmdPlan(base)) - rootCmd.AddCommand(regions.NewCmdRegion(base)) - rootCmd.AddCommand(ReservedIP()) - rootCmd.AddCommand(Script()) - rootCmd.AddCommand(Instance()) - rootCmd.AddCommand(Snapshot()) - rootCmd.AddCommand(sshkeys.NewCmdSSHKey(base)) - rootCmd.AddCommand(users.NewCmdUser(base)) - rootCmd.AddCommand(version.NewCmdVersion()) - cobra.OnInitialize(initConfig) + if err := viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config")); err != nil { + fmt.Printf("error binding root pflag 'config': %v\n", err) + } + rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "text", "output format [ text | json | yaml ]") + if err := viper.BindPFlag("output", rootCmd.PersistentFlags().Lookup("output")); err != nil { + fmt.Printf("error binding root pflag 'output': %v\n", err) + } + + base := cli.NewCLIBase( + os.Getenv("VULTR_API_KEY"), + userAgent, + output, + ) + + rootCmd.AddCommand( + account.NewCmdAccount(base), + applications.NewCmdApplications(base), + backups.NewCmdBackups(base), + baremetal.NewCmdBareMetal(base), + billing.NewCmdBilling(base), + blockstorage.NewCmdBlockStorage(base), + containerregistry.NewCmdContainerRegistry(base), + database.NewCmdDatabase(base), + dns.NewCmdDNS(base), + firewall.NewCmdFirewall(base), + iso.NewCmdISO(base), + kubernetes.NewCmdKubernetes(base), + loadbalancer.NewCmdLoadBalancer(base), + marketplace.NewCmdMarketplace(base), + operatingsystems.NewCmdOS(base), + objectstorage.NewCmdObjectStorage(base), + plans.NewCmdPlan(base), + regions.NewCmdRegion(base), + reservedip.NewCmdReservedIP(base), + script.NewCmdScript(base), + instance.NewCmdInstance(base), + snapshot.NewCmdSnapshot(base), + sshkeys.NewCmdSSHKey(base), + users.NewCmdUser(base), + version.NewCmdVersion(base), + vpc.NewCmdVPC(base), + vpc2.NewCmdVPC2(base), + ) } // initConfig reads in config file and ENV variables if set. func initConfig() { - var token string configPath := viper.GetString("config") if configPath == "" { @@ -112,53 +134,41 @@ func initConfig() { if err := viper.ReadInConfig(); err != nil { fmt.Println("Error Reading in file:", viper.ConfigFileUsed()) } - - token = viper.GetString("api-key") - if token == "" { - token = os.Getenv("VULTR_API_KEY") - } - - if token == "" { - fmt.Println("Please export your VULTR API key as an environment variable or add `api-key` to your config file, eg:") - fmt.Println("export VULTR_API_KEY=''") - os.Exit(1) - } - - base = cli.NewCLIBase(token, viper.GetString("output")) } -func getPaging(cmd *cobra.Command) *govultr.ListOptions { - options := &govultr.ListOptions{} - - cursor, _ := cmd.Flags().GetString("cursor") - perPage, _ := cmd.Flags().GetInt("per-page") - - if cursor != "" { - options.Cursor = cursor +func configHome() string { + // check for a config file at ~/.config/vultr-cli.yaml + configFolder, errConfig := os.UserConfigDir() + if errConfig != nil { + os.Exit(1) } - if perPage != 0 { - options.PerPage = perPage + configFile := fmt.Sprintf("%s/vultr-cli.yaml", configFolder) + if _, err := os.Stat(configFile); err == nil { + // if one exists, return the path + return configFile } - return options -} - -func configHome() string { - configHome, err := os.UserHomeDir() - if err != nil { + // check for a config file at ~/.vultr-cli.yaml + configFolder, errHome := os.UserHomeDir() + if errHome != nil { os.Exit(1) } - configHome = fmt.Sprintf("%s/.vultr-cli.yaml", configHome) - if _, err := os.Stat(configHome); err != nil { - f, err := os.Create(configHome) + configFile = fmt.Sprintf("%s/.vultr-cli.yaml", configFolder) + if _, err := os.Stat(configFile); err != nil { + // if it doesn't exist, create one + f, err := os.Create(filepath.Clean(configFile)) if err != nil { os.Exit(1) } - defer f.Close() + defer func() { + if errCls := f.Close(); errCls != nil { + fmt.Printf("failed to close config file.. error: %v", errCls) + } + }() } - return configHome + return configFile } diff --git a/cmd/script.go b/cmd/script.go deleted file mode 100644 index 6521a1ce..00000000 --- a/cmd/script.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright © 2019 The Vultr-cli Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/vultr/govultr/v2" - "github.com/vultr/vultr-cli/cmd/printer" -) - -// Script represents the script command -func Script() *cobra.Command { - cmd := &cobra.Command{ - Use: "script", - Aliases: []string{"ss"}, - Short: "startup script commands", - Long: `script is used to access startup script commands`, - } - - cmd.AddCommand(scriptCreate, scriptGet, scriptDelete, scriptList, scriptUpdate) - - scriptCreate.Flags().StringP("name", "n", "", "Name of the newly created startup script.") - scriptCreate.Flags().StringP("script", "s", "", "Startup script contents.") - scriptCreate.Flags().StringP("type", "t", "", "(Optional) Type of startup script. Possible values: 'boot', 'pxe'. Default is 'boot'.") - - scriptCreate.MarkFlagRequired("name") - scriptCreate.MarkFlagRequired("script") - - scriptUpdate.Flags().StringP("name", "n", "", "Name of the startup script.") - scriptUpdate.Flags().StringP("script", "s", "", "Startup script contents.") - scriptUpdate.Flags().StringP("type", "t", "", "Type of startup script. Possible values: 'boot', 'pxe'. Default is 'boot'.") - - scriptList.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") - scriptList.Flags().IntP("per-page", "p", 100, "(optional) Number of items requested per page. Default is 100 and Max is 500.") - - return cmd -} - -// Create startup script command -var scriptCreate = &cobra.Command{ - Use: "create", - Short: "Create a startup script", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - name, _ := cmd.Flags().GetString("name") - script, _ := cmd.Flags().GetString("script") - scriptType, _ := cmd.Flags().GetString("type") - - options := &govultr.StartupScriptReq{ - Name: name, - Script: script, - Type: scriptType, - } - - startup, err := client.StartupScript.Create(context.Background(), options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.Script(startup) - }, -} - -// Delete startup script command -var scriptDelete = &cobra.Command{ - Use: "delete ", - Short: "Delete a startup script", - Aliases: []string{"destroy"}, - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a scriptID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - if err := client.StartupScript.Delete(context.Background(), id); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("Startup script has been deleted") - }, -} - -// List all startup scripts command -var scriptList = &cobra.Command{ - Use: "list", - Short: "List all startup scripts", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - options := getPaging(cmd) - list, meta, err := client.StartupScript.List(context.Background(), options) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.ScriptList(list, meta) - }, -} - -// Displays the contents of a specified script -var scriptGet = &cobra.Command{ - Use: "get ", - Short: "Displays the contents of specified script", - Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a scriptID") - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - script, err := client.StartupScript.Get(context.Background(), id) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - printer.Script(script) - }, -} - -// Update startup script command -var scriptUpdate = &cobra.Command{ - Use: "update ", - Short: "Update startup script", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("please provide a scriptID") - } - return nil - }, - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - id := args[0] - name, _ := cmd.Flags().GetString("name") - script, _ := cmd.Flags().GetString("script") - scriptType, _ := cmd.Flags().GetString("type") - - s := &govultr.StartupScriptReq{} - - if name != "" { - s.Name = name - } - - if script != "" { - s.Script = script - } - - if scriptType != "" { - s.Type = scriptType - } - - if err := client.StartupScript.Update(context.Background(), id, s); err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - fmt.Println("Startup script has been updated") - }, -} diff --git a/cmd/script/printer.go b/cmd/script/printer.go new file mode 100644 index 00000000..db2d57e9 --- /dev/null +++ b/cmd/script/printer.go @@ -0,0 +1,97 @@ +package script + +import ( + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" +) + +// ScriptsPrinter ... +type ScriptsPrinter struct { + Scripts []govultr.StartupScript `json:"startup_scripts"` + Meta *govultr.Meta `json:"meta"` +} + +// JSON ... +func (s *ScriptsPrinter) JSON() []byte { + return printer.MarshalObject(s, "json") +} + +// YAML ... +func (s *ScriptsPrinter) YAML() []byte { + return printer.MarshalObject(s, "yaml") +} + +// Columns ... +func (s *ScriptsPrinter) Columns() [][]string { + return [][]string{0: { + "ID", + "DATE CREATED", + "DATE MODIFIED", + "TYPE", + "NAME", + }} +} + +// Data ... +func (s *ScriptsPrinter) Data() [][]string { + if len(s.Scripts) == 0 { + return [][]string{0: {"---", "---", "---", "---", "---"}} + } + + var data [][]string + for i := range s.Scripts { + data = append(data, []string{ + s.Scripts[i].ID, + s.Scripts[i].DateCreated, + s.Scripts[i].DateModified, + s.Scripts[i].Type, + s.Scripts[i].Name, + }) + } + + return data +} + +// Paging ... +func (s *ScriptsPrinter) Paging() [][]string { + return printer.NewPaging(s.Meta.Total, &s.Meta.Links.Next, &s.Meta.Links.Prev).Compose() +} + +// ====================================== + +// ScriptPrinter ... +type ScriptPrinter struct { + Script *govultr.StartupScript `json:"startup_script"` +} + +// JSON ... +func (s *ScriptPrinter) JSON() []byte { + return printer.MarshalObject(s, "json") +} + +// YAML ... +func (s *ScriptPrinter) YAML() []byte { + return printer.MarshalObject(s, "yaml") +} + +// Columns ... +func (s *ScriptPrinter) Columns() [][]string { + return nil +} + +// Data ... +func (s *ScriptPrinter) Data() [][]string { + return [][]string{ + 0: {"ID", s.Script.ID}, + 1: {"DATE CREATED", s.Script.DateCreated}, + 2: {"DATE MODIFIED", s.Script.DateModified}, + 3: {"TYPE", s.Script.Type}, + 4: {"NAME", s.Script.Name}, + 5: {"SCRIPT", s.Script.Script}, + } +} + +// Paging ... +func (s *ScriptPrinter) Paging() [][]string { + return nil +} diff --git a/cmd/script/script.go b/cmd/script/script.go new file mode 100644 index 00000000..08b4dc66 --- /dev/null +++ b/cmd/script/script.go @@ -0,0 +1,255 @@ +// Package script provides the script commands to the CLI +package script + +import ( + "errors" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vultr/govultr/v3" + "github.com/vultr/vultr-cli/v3/cmd/printer" + "github.com/vultr/vultr-cli/v3/cmd/utils" + "github.com/vultr/vultr-cli/v3/pkg/cli" +) + +// NewCmdScript provides the CLI command for startup script functions +func NewCmdScript(base *cli.Base) *cobra.Command { //nolint:gocyclo + o := &options{Base: base} + + cmd := &cobra.Command{ + Use: "script", + Short: "Commands to interact with startup scripts", + Aliases: []string{"ss", "startup-script"}, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + utils.SetOptions(o.Base, cmd, args) + if !o.Base.HasAuth { + return errors.New(utils.APIKeyError) + } + return nil + }, + } + + // List + list := &cobra.Command{ + Use: "list", + Short: "List all startup scripts", + RunE: func(cmd *cobra.Command, args []string) error { + o.Base.Options = utils.GetPaging(cmd) + + scripts, meta, err := o.list() + if err != nil { + return fmt.Errorf("error retrieving startup script list : %v", err) + } + + data := &ScriptsPrinter{Scripts: scripts, Meta: meta} + o.Base.Printer.Display(data, nil) + + return nil + }, + } + + list.Flags().StringP("cursor", "c", "", "(optional) Cursor for paging.") + list.Flags().IntP( + "per-page", + "p", + utils.PerPageDefault, + fmt.Sprintf("(optional) Number of items requested per page. Default is %d and Max is 500.", utils.PerPageDefault), + ) + + // Get + get := &cobra.Command{ + Use: "get