From 1a2c50fa5dc4346e44ae4bf5aaa1df5e1c2410bc Mon Sep 17 00:00:00 2001 From: Zhanwei Wang Date: Wed, 14 Jul 2021 16:29:50 +0800 Subject: [PATCH 01/10] Update qingcloud sdk --- .../qingcloud-sdk-go/.github/workflows/go.yml | 20 ++ .../yunify/qingcloud-sdk-go/.gitignore | 33 ++ .../yunify/qingcloud-sdk-go/.travis.yml | 35 ++ .../yunify/qingcloud-sdk-go/CHANGELOG.md | 247 ++++++++++++++ .../yunify/qingcloud-sdk-go/MAINTAINERS | 1 + .../yunify/qingcloud-sdk-go/Makefile | 144 ++++++++ .../yunify/qingcloud-sdk-go/README.md | 51 +++ .../yunify/qingcloud-sdk-go/config/config.go | 8 + .../qingcloud-sdk-go/config/config_test.go | 120 +++++++ .../qingcloud-sdk-go/docs/configuration.md | 84 +++++ .../qingcloud-sdk-go/docs/installation.md | 36 ++ .../docs/qingcloud_service_usage.md | 99 ++++++ .../yunify/qingcloud-sdk-go/glide.yaml | 14 + .../github.com/yunify/qingcloud-sdk-go/go.mod | 16 + .../github.com/yunify/qingcloud-sdk-go/go.sum | 23 ++ .../qingcloud-sdk-go/request/builder.go | 4 + .../qingcloud-sdk-go/request/builder_test.go | 111 +++++++ .../qingcloud-sdk-go/request/request.go | 101 ++++++ .../qingcloud-sdk-go/request/signer_test.go | 67 ++++ .../qingcloud-sdk-go/request/unpacker_test.go | 198 +++++++++++ .../service/convert_types_test.go | 311 ++++++++++++++++++ .../yunify/qingcloud-sdk-go/service/image.go | 1 + .../yunify/qingcloud-sdk-go/service/types.go | 8 +- .../yunify/qingcloud-sdk-go/service/vxnet.go | 5 +- .../qingcloud-sdk-go/template/manifest.json | 8 + .../qingcloud-sdk-go/template/service.tmpl | 48 +++ .../qingcloud-sdk-go/template/shared.tmpl | 240 ++++++++++++++ .../template/sub_service.tmpl | 61 ++++ .../qingcloud-sdk-go/template/types.tmpl | 39 +++ .../yunify/qingcloud-sdk-go/test/.gitignore | 3 + .../qingcloud-sdk-go/test/config.yaml.example | 13 + .../test/features/qingcloud.feature | 46 +++ .../qingcloud-sdk-go/test/instance_test.go | 153 +++++++++ .../yunify/qingcloud-sdk-go/test/job_test.go | 65 ++++ .../yunify/qingcloud-sdk-go/test/main_test.go | 96 ++++++ .../qingcloud-sdk-go/test/service_test.go | 73 ++++ .../test/test_config.yaml.example | 6 + .../yunify/qingcloud-sdk-go/test/utils.go | 33 ++ .../qingcloud-sdk-go/utils/json_test.go | 79 +++++ .../qingcloud-sdk-go/utils/time_test.go | 57 ++++ .../qingcloud-sdk-go/utils/wait_test.go | 49 +++ .../qingcloud-sdk-go/utils/yaml_test.go | 75 +++++ .../yunify/qingcloud-sdk-go/version.go | 23 ++ .../yunify/qingcloud-sdk-go/version_test.go | 28 ++ 44 files changed, 2927 insertions(+), 5 deletions(-) create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/.github/workflows/go.yml create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/.gitignore create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/.travis.yml create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/CHANGELOG.md create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/MAINTAINERS create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/Makefile create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/README.md create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/config/config_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/docs/configuration.md create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/docs/installation.md create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/docs/qingcloud_service_usage.md create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/glide.yaml create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/go.mod create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/go.sum create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/request/builder_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/request/signer_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/request/unpacker_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/service/convert_types_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/template/manifest.json create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/template/service.tmpl create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/template/shared.tmpl create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/template/sub_service.tmpl create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/template/types.tmpl create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/.gitignore create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/config.yaml.example create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/features/qingcloud.feature create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/instance_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/job_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/main_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/service_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/test_config.yaml.example create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/test/utils.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/utils/json_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/utils/time_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/utils/wait_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/utils/yaml_test.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/version.go create mode 100644 vendor/github.com/yunify/qingcloud-sdk-go/version_test.go diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/.github/workflows/go.yml b/vendor/github.com/yunify/qingcloud-sdk-go/.github/workflows/go.yml new file mode 100644 index 000000000..1a308cb9f --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/.github/workflows/go.yml @@ -0,0 +1,20 @@ +name: Go + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + strategy: + matrix: + go: [1.13, 1.14, 1.15, 1.16] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: go get -u golang.org/x/lint/golint + - run: make check + - run: make release + - run: GO_VERSION=${{ matrix.go }} make unit-runtime diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/.gitignore b/vendor/github.com/yunify/qingcloud-sdk-go/.gitignore new file mode 100644 index 000000000..5d0ba7ce4 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/.gitignore @@ -0,0 +1,33 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +out +gen +coverage +release +.idea +glide.lock + +snips diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/.travis.yml b/vendor/github.com/yunify/qingcloud-sdk-go/.travis.yml new file mode 100644 index 000000000..990a0f86a --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/.travis.yml @@ -0,0 +1,35 @@ +sudo: required + +services: + - docker + +language: go +go: + - 1.13 + +env: + matrix: + - GO_VERSION=1.13 + +cache: + directories: + - ${HOME}/source + +before_install: + - pushd ${HOME}/source + - if [[ ! -d "./make-4.0" ]]; then + wget http://ftp.gnu.org/gnu/make/make-4.0.tar.gz && + tar -vxzf make-4.0.tar.gz && + pushd make-4.0 && ./configure && make && popd; + fi + - pushd make-4.0 && sudo make install && popd + - popd + - /usr/local/bin/make --version + +install: + - go get -u golang.org/x/lint/golint; + +script: + - /usr/local/bin/make check + - /usr/local/bin/make release + - /usr/local/bin/make unit-runtime-go-${GO_VERSION} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/CHANGELOG.md b/vendor/github.com/yunify/qingcloud-sdk-go/CHANGELOG.md new file mode 100644 index 000000000..e8baccb6c --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/CHANGELOG.md @@ -0,0 +1,247 @@ +# Change Log +All notable changes to QingCloud SDK for Go will be documented in this file. + +## [v2.0.0-alpha.29] - 2018-03-26 + +### Added + +- CeaseInstances @calvinyv + +## [v2.0.0-alpha.28] - 2018-01-08 + +### Fixed + +- DescribeLoadBalancerBackends field error @runzexia + +## [v2.0.0-alpha.27] - 2018-01-08 + +### Fixed + +- ModifyLoadBalancerBackendAttributes field error @runzexia + +## [v2.0.0-alpha.26] - 2018-01-04 + +### Fixed + +- fix loadblancer listener timeout location + +## [v2.0.0-alpha.25] - 2018-01-04 + +### Fixed + +- add loadblancer listener timeout + +## [v2.0.0-alpha.24] - 2018-01-04 + +### Fixed + +- add ModifyLoadBalancerListenerAttributes missing field listener_option + +## [v2.0.0-alpha.23] - 2017-12-24 + +### Fixed + +- DescribeServerCertificatesInput field type error + +## [v2.0.0-alpha.22] - 2017-12-23 + +### Add + +- Add api to get vpn certificate + +### Fixed + +- Add loadbalancer attr cluster +- POST query escape +- CreateServerCertificate use POST + +## [v2.0.0-alpha.21] - 2017-12-15 + +### Fixed + +- Change loadbalancer attr cluster to eips + + +## [v2.0.0-alpha.20] - 2017-12-15 + +### Fixed + +- Fix loadbalancer http_header_size location + + +## [v2.0.0-alpha.19] - 2017-12-15 + +### Fixed + +- Add loadbalancer http_header_size + +## [v2.0.0-alpha.18] - 2017-12-14 + +### Fixed + +- Add loadbalancer node_count + + +## [v2.0.0-alpha.17] - 2017-12-13 + +### Fixed + +- Fix new config with endpoint + +## [v2.0.0-alpha.16] - 2017-12-12 + +### Added + +- Add new config with endpoint + +### Fixed + +- Fix cluster api bugs +- Fix userdata_path. userdata_file default value error + +## [v2.0.0-alpha.15] - 2017-12-07 + +### Added + +- Add service cluster for go sdk + +### Fixed + +- Add requset type POST support + +## [v2.0.0-alpha.14] - 2017-11-24 + +### Fixed + +- Add router static missing field +- Update template to fit snips change + +## [v2.0.0-alpha.13] - 2017-11-09 + +### Fixed + +- Delete DescribeSecurityGroupRules SecurityGroup Required + +## [v2.0.0-alpha.12] - 2017-11-09 + +### Fixed + +- DescribeSecurityGroupRules direction default value + +## [v2.0.0-alpha.11] - 2017-11-08 + +### Fixed + +- Fix wrong type of job id for stop lb output +- Add Makefile generate target to generate service code +- Fix ModifySecurityGroupRuleAttributes field type error +- Fix SecurityGroupRule field missing error + +## [v2.0.0-alpha.10] - 2017-08-28 + +### Fixed + +- Fixed loadbalancers section in request of StopLoadBalancers + +## [v2.0.0-alpha.9] - 2017-08-23 + +### Added + +- Add vxnetid and loadbalancertype parms for load balancer + +### Fixed + +- Fixed vxnet section in response of DescribeInstances + +## [v2.0.0-alpha.8] - 2017-08-13 + +### Added + +- Add missing parameter for describenic +- Add vxnet parms for instances + +## [v2.0.0-alpha.7] - 2017-08-02 + +### Added + +- Add timeout parameter for http client +- Add missing parameters in nic, router and security groups + +## [v2.0.0-alpha.6] - 2017-07-17 + +### Added + +- Update advanced client. [@jolestar] +- Fix several APIs. [@jolestar] + +## [v2.0.0-alpha.5] - 2017-03-27 + +### Added + +- Add advanced client for simple instance management. [@jolestar] +- Add wait utils for waiting a job to finish. [@jolestar] + +## [v2.0.0-alpha.4] - 2017-03-14 + +### Fixed + +- Fix Features type in RouterVxNet. + +## [v2.0.0-alpha.3] - 2017-01-15 + +### Changed + +- Fix request signer. + +## [v2.0.0-alpha.2] - 2017-01-05 + +### Changed + +- Fix logger output format, don't parse special characters. +- Rename package "errs" to "errors". + +### Added + +- Add type converters. + +### BREAKING CHANGES + +- Change value type in input and output to pointer. + +## v2.0.0-alpha.1 - 2016-12-03 + +### Added + +- QingCloud SDK for the Go programming language. +[v2.0.0-alpha.29]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.28...v2.0.0-alpha.29 +[v2.0.0-alpha.28]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.27...v2.0.0-alpha.28 +[v2.0.0-alpha.27]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.26...v2.0.0-alpha.27 +[v2.0.0-alpha.26]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.25...v2.0.0-alpha.26 +[v2.0.0-alpha.25]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.24...v2.0.0-alpha.25 +[v2.0.0-alpha.24]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.23...v2.0.0-alpha.24 +[v2.0.0-alpha.23]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.22...v2.0.0-alpha.23 +[v2.0.0-alpha.22]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.21...v2.0.0-alpha.22 +[v2.0.0-alpha.21]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.20...v2.0.0-alpha.21 +[v2.0.0-alpha.20]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.19...v2.0.0-alpha.20 +[v2.0.0-alpha.19]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.18...v2.0.0-alpha.19 +[v2.0.0-alpha.18]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.17...v2.0.0-alpha.18 +[v2.0.0-alpha.17]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.16...v2.0.0-alpha.17 +[v2.0.0-alpha.16]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.15...v2.0.0-alpha.16 +[v2.0.0-alpha.15]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.14...v2.0.0-alpha.15 +[v2.0.0-alpha.14]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.13...v2.0.0-alpha.14 +[v2.0.0-alpha.13]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.12...v2.0.0-alpha.13 +[v2.0.0-alpha.12]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.11...v2.0.0-alpha.12 +[v2.0.0-alpha.11]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.10...v2.0.0-alpha.11 +[v2.0.0-alpha.10]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.9...v2.0.0-alpha.10 +[v2.0.0-alpha.9]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.8...v2.0.0-alpha.9 +[v2.0.0-alpha.8]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.7...v2.0.0-alpha.8 +[v2.0.0-alpha.7]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.6...v2.0.0-alpha.7 +[v2.0.0-alpha.6]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.5...v2.0.0-alpha.6 +[v2.0.0-alpha.5]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.4...v2.0.0-alpha.5 +[v2.0.0-alpha.4]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.3...v2.0.0-alpha.4 +[v2.0.0-alpha.3]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.2...v2.0.0-alpha.3 +[v2.0.0-alpha.2]: https://github.com/yunify/qingcloud-sdk-go/compare/v2.0.0-alpha.1...v2.0.0-alpha.2 + +[@jolestar]: https://github.com/jolestar +[@calvinyv]: https://github.com/calvinyv +[@runzexia]: https://github.com/runzexia diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/MAINTAINERS b/vendor/github.com/yunify/qingcloud-sdk-go/MAINTAINERS new file mode 100644 index 000000000..de002f414 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/MAINTAINERS @@ -0,0 +1 @@ +jolestar diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/Makefile b/vendor/github.com/yunify/qingcloud-sdk-go/Makefile new file mode 100644 index 000000000..421db1d25 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/Makefile @@ -0,0 +1,144 @@ +SHELL := /bin/bash + +.PHONY: all check vet lint update generate build unit test release clean + +PREFIX=qingcloud-sdk-go +VERSION=$(shell cat version.go | grep "Version\ =" | sed -e s/^.*\ //g | sed -e s/\"//g) +DIRS_TO_CHECK=$(shell ls -d */ | grep -vE "vendor|test") +PKGS_TO_CHECK=$(shell go list ./... | grep -vE "vendor|test") +PKGS_TO_RELEASE=$(shell go list ./... | grep -vE "/vendor/|/test") +FILES_TO_RELEASE=$(shell find . -name "*.go" | grep -vE "/vendor/|/test|.*_test.go") +FILES_TO_RELEASE_WITH_VENDOR=$(shell find . -name "*.go" | grep -vE "/test|.*_test.go") +LINT_IGNORE_DOC="service\/.*\.go:.+(comment on exported|should have comment or be unexported)" +LINT_IGNORE_CONFLICT="service\/.*\.go:.+(type name will be used as)" +LINT_IGNORE_METHOD="GetGlobalUniqueId" + +GO_VERSION?=1.13 + +help: + @echo "Please use \`make \` where is one of" + @echo " all to check, build, test and release this SDK" + @echo " check to vet and lint the SDK" + @echo " generate to generate service code" + @echo " build to build the SDK" + @echo " unit to run all sort of unit tests except runtime" + @echo " unit-test to run unit test" + @echo " unit-benchmark to run unit test with benchmark" + @echo " unit-coverage to run unit test with coverage" + @echo " unit-race to run unit test with race" + @echo " unit-runtime to run test with go1.5, go1.6, go 1.7 in docker" + @echo " test to run service test" + @echo " release to build and release current version" + @echo " release-source to pack the source code" + @echo " clean to clean the coverage files" + +all: check build unit release + +check: vet lint + +vet: + @echo "go vet" + @for i in $DIRS_TO_CHECK; do go vet ./$i;done + @echo "ok" + +lint: + @echo "golint, skipping vendor packages" + @lint=$$(for pkg in ${PKGS_TO_CHECK}; do golint $${pkg}; done); \ + lint=$$(echo "$${lint}" | grep -vE -e ${LINT_IGNORE_DOC} -e ${LINT_IGNORE_CONFLICT} -e ${LINT_IGNORE_METHOD}); \ + if [[ -n $${lint} ]]; then echo "$${lint}"; exit 1; fi + @echo "ok" + +generate: snips ../qingcloud-api-specs/package.json + ./snips \ + -f=../qingcloud-api-specs/2013-08-30/swagger/api_v2.0.json \ + -t=./template \ + -o=./service + go fmt ./service/... + @echo "ok" + +snips: + curl -L https://github.com/yunify/snips/releases/download/v0.2.16/snips-v0.2.16-${shell go env GOOS}_amd64.tar.gz | tar zx + +../qingcloud-api-specs/package.json: + -go get -d github.com/yunify/qingcloud-api-specs + file %@ + +build: + @echo "build the SDK" + GOOS=linux GOARCH=amd64 go build ${PKGS_TO_CHECK} + GOOS=darwin GOARCH=amd64 go build ${PKGS_TO_CHECK} + GOOS=windows GOARCH=amd64 go build ${PKGS_TO_CHECK} + @echo "ok" + + +unit: unit-test unit-benchmark unit-coverage unit-race + +unit-test: + @echo "run unit test" + go test -v ${PKGS_TO_CHECK} + @echo "ok" + +unit-benchmark: + @echo "run unit test with benchmark" + go test -v -bench=. ${PKGS_TO_CHECK} + @echo "ok" + +unit-coverage: + @echo "run unit test with coverage" + for pkg in ${PKGS_TO_CHECK}; do \ + output="coverage$${pkg#github.com/yunify/qingcloud-sdk-go}"; \ + mkdir -p $${output}; \ + go test -v -cover -coverprofile="$${output}/profile.out" $${pkg}; \ + if [[ -e "$${output}/profile.out" ]]; then \ + go tool cover -html="$${output}/profile.out" -o "$${output}/profile.html"; \ + fi; \ + done + @echo "ok" + +unit-race: + @echo "run unit test with race" + go test -v -race -cpu=1,2,4 ${PKGS_TO_CHECK} + @echo "ok" + +unit-runtime: unit-runtime-go + +export define DOCKERFILE_WITH_GO_VERSION +FROM golang:GO_VERSION + +ADD . /go/src/github.com/yunify/qingcloud-sdk-go +WORKDIR /go/src/github.com/yunify/qingcloud-sdk-go + +CMD ["make", "build", "unit"] +endef + +unit-runtime-go: + @echo "run test in go ${GO_VERSION}" + echo "$${DOCKERFILE_WITH_GO_VERSION}" | sed 's/GO_VERSION/${GO_VERSION}/' > "dockerfile_go_${GO_VERSION}" + docker build -f "./dockerfile_go_${GO_VERSION}" -t "${PREFIX}:go-${GO_VERSION}" . + rm -f "./dockerfile_go_${GO_VERSION}" + docker run --name "${PREFIX}-go-${GO_VERSION}-unit" -t "${PREFIX}:go-${GO_VERSION}" + docker rm "${PREFIX}-go-${GO_VERSION}-unit" + docker rmi "${PREFIX}:go-${GO_VERSION}" + @echo "ok" + +test: + pushd "./test"; go test ; popd + @echo "ok" + +release: release-source release-source-with-vendor + +release-source: + @echo "pack the source code" + mkdir -p "release" + @zip -FS "release/${PREFIX}-source-v${VERSION}.zip" ${FILES_TO_RELEASE} + @echo "ok" + +release-source-with-vendor: + @echo "pack the source code" + mkdir -p "release" + @zip -FS "release/${PREFIX}-source-with-vendor-v${VERSION}.zip" ${FILES_TO_RELEASE_WITH_VENDOR} + @echo "ok" + +clean: + rm -rf $${PWD}/coverage + @echo "ok" diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/README.md b/vendor/github.com/yunify/qingcloud-sdk-go/README.md new file mode 100644 index 000000000..17b41e9d6 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/README.md @@ -0,0 +1,51 @@ +# QingCloud SDK for Go + +[![Build Status](https://travis-ci.org/yunify/qingcloud-sdk-go.svg?branch=master)](https://travis-ci.org/yunify/qingcloud-sdk-go) +[![Go Report Card](https://goreportcard.com/badge/github.com/yunify/qingcloud-sdk-go)](https://goreportcard.com/report/github.com/yunify/qingcloud-sdk-go) +[![API Reference](http://img.shields.io/badge/api-reference-green.svg)](http://docs.qingcloud.com) +[![License](http://img.shields.io/badge/license-apache%20v2-blue.svg)](https://github.com/yunify/qingcloud-sdk-go/blob/master/LICENSE) + +The official QingCloud SDK for the Go programming language. + +## Getting Started + +### Installation + +Refer to the [Installation Guide](docs/installation.md), and have this SDK installed. + +### Preparation + +Before your start, please go to [QingCloud Console](https://console.qingcloud.com/access_keys/) to create a pair of QingCloud API AccessKey. + +___API AccessKey Example:___ + +``` yaml +qy_access_key_id: 'ACCESS_KEY_ID_EXAMPLE' +qy_secret_access_key: 'SECRET_ACCESS_KEY_EXAMPLE' +``` + +### Usage + +Now you are ready to code. You can read the detailed guides in the list below to have a clear understanding. + +- [Configuration Guide](docs/configuration.md) +- [QingCloud Service Usage Guide](docs/qingcloud_service_usage.md) + +Checkout our [releases](https://github.com/yunify/qingcloud-sdk-go/releases) and [change logs](https://github.com/yunify/qingcloud-sdk-go/blob/master/CHANGELOG.md) for information about the latest features, bug fixes and new ideas. + +## Reference Documentations + +- [QingCloud Documentation Overview](https://docs.qingcloud.com) +- [QingCloud APIs](https://docs.qingcloud.com/api/index.html) + +## Contributing + +1. Fork it ( https://github.com/yunify/qingcloud-sdk-go/fork ) +2. Create your feature branch (`git checkout -b new-feature`) +3. Commit your changes (`git commit -asm 'Add some feature'`) +4. Push to the branch (`git push origin new-feature`) +5. Create a new Pull Request + +## LICENSE + +The Apache License (Version 2.0, January 2004). diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/config/config.go b/vendor/github.com/yunify/qingcloud-sdk-go/config/config.go index 36b38a1bc..bc01e345b 100644 --- a/vendor/github.com/yunify/qingcloud-sdk-go/config/config.go +++ b/vendor/github.com/yunify/qingcloud-sdk-go/config/config.go @@ -47,6 +47,14 @@ type Config struct { Zone string `yaml:"zone"` + CredentialProxyProtocol string `yaml:"credential_proxy_protocol"` + CredentialProxyHost string `yaml:"credential_proxy_host"` + CredentialProxyPort int `yaml:"credential_proxy_port"` + CredentialProxyURI string `yaml:"credential_proxy_uri"` + + Token string + Expiration int64 + Connection *http.Client } diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/config/config_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/config/config_test.go new file mode 100644 index 000000000..a1fe0fe9e --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/config/config_test.go @@ -0,0 +1,120 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 config + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yunify/qingcloud-sdk-go/logger" +) + +func TestConfig(t *testing.T) { + config := Config{ + AccessKeyID: "AccessKeyID", + SecretAccessKey: "SecretAccessKey", + Protocol: "http", + ConnectionRetries: 10, + LogLevel: "warn", + } + + assert.Equal(t, "AccessKeyID", config.AccessKeyID) + assert.Equal(t, "SecretAccessKey", config.SecretAccessKey) + assert.Equal(t, "http", config.Protocol) + assert.Equal(t, 10, config.ConnectionRetries) + assert.Equal(t, "warn", config.LogLevel) +} + +func TestConfig_LoadDefaultConfig(t *testing.T) { + config := Config{} + config.LoadDefaultConfig() + + assert.Equal(t, "", config.AccessKeyID) + assert.Equal(t, "", config.SecretAccessKey) + assert.Equal(t, "https", config.Protocol) + assert.Equal(t, "warning", logger.GetLevel()) +} + +func TestConfig_LoadUserConfig(t *testing.T) { + config := Config{} + config.LoadUserConfig() + + assert.Equal(t, "https", config.Protocol) +} + +func TestConfig_LoadConfigFromContent(t *testing.T) { + fileContent := ` +qy_access_key_id: 'access_key_id' +qy_secret_access_key: 'secret_access_key' + +log_level: 'debug' +` + + config := Config{} + config.LoadConfigFromContent([]byte(fileContent)) + + assert.Equal(t, "access_key_id", config.AccessKeyID) + assert.Equal(t, "secret_access_key", config.SecretAccessKey) + assert.Equal(t, "https", config.Protocol) + assert.Equal(t, "debug", logger.GetLevel()) +} + +func TestNewDefault(t *testing.T) { + config, err := NewDefault() + assert.Nil(t, err) + + assert.Equal(t, "", config.AccessKeyID) + assert.Equal(t, "", config.SecretAccessKey) + assert.Equal(t, "https", config.Protocol) + assert.Equal(t, 3, config.ConnectionRetries) +} + +func TestNew(t *testing.T) { + config, err := New("AccessKeyID", "SecretAccessKey") + assert.Nil(t, err) + + assert.Equal(t, "AccessKeyID", config.AccessKeyID) + assert.Equal(t, "SecretAccessKey", config.SecretAccessKey) + assert.Equal(t, "https", config.Protocol) +} + +func TestNewWithEndpoint(t *testing.T) { + config, err := NewWithEndpoint("AccessKeyID", "SecretAccessKey", "test.qingcloud.com:444/iaas?a=1#hhh") + assert.NotNil(t, err) + + config, err = NewWithEndpoint("AccessKeyID", "SecretAccessKey", "http:test.qingcloud.com:444/iaas?a=1#hhh") + assert.NotNil(t, err) + + config, err = NewWithEndpoint("AccessKeyID", "SecretAccessKey", "https://api.qingcloud.com/iaas") + assert.Nil(t, err) + assert.Equal(t, "AccessKeyID", config.AccessKeyID) + assert.Equal(t, "SecretAccessKey", config.SecretAccessKey) + assert.Equal(t, "https", config.Protocol) + assert.Equal(t, "api.qingcloud.com", config.Host) + assert.Equal(t, 443, config.Port) + assert.Equal(t, "/iaas", config.URI) + + config, err = NewWithEndpoint("AccessKeyID", "SecretAccessKey", "http://test.qingcloud.com:444/iaas") + assert.Nil(t, err) + assert.Equal(t, "AccessKeyID", config.AccessKeyID) + assert.Equal(t, "SecretAccessKey", config.SecretAccessKey) + assert.Equal(t, "http", config.Protocol) + assert.Equal(t, "test.qingcloud.com", config.Host) + assert.Equal(t, 444, config.Port) + assert.Equal(t, "/iaas", config.URI) + +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/docs/configuration.md b/vendor/github.com/yunify/qingcloud-sdk-go/docs/configuration.md new file mode 100644 index 000000000..8ced7c035 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/docs/configuration.md @@ -0,0 +1,84 @@ +# Configuration Guide + +## Summary + +This SDK uses a structure called "Config" to store and manage configuration, read ["config/config.go"](https://github.com/yunify/qingcloud-sdk-go/blob/master/config/config.go) file's comments of public functions for more information. + +Except for AccessKeyID and SecretAccessKey, you can also configure the API servers for private cloud usage scenario. All available configurable items are list in default configuration file. + +___Default Configuration File:___ + +``` yaml +# QingCloud services configuration + +qy_access_key_id: 'ACCESS_KEY_ID' +qy_secret_access_key: 'SECRET_ACCESS_KEY' + +host: 'api.qingcloud.com' +port: 443 +protocol: 'https' +uri: '/iaas' +connection_retries: 3 + +# Valid log levels are "debug", "info", "warn", "error", and "fatal". +log_level: 'warn' + +``` + +## Usage + +1. Just create a config structure instance with your API AccessKey, and initialize services that you need using Init() function of target service. + +2. Or if you do not want to expose your AccessKeyID and SecretAccessKey, you can also call our API without them in the following way: +- Go to our IAM service, create an instance role and attach it to your instance. +- Create the config structure instance without AccessKeyID and SecretAccessKey. + +Note that you can also configure your own credential proxy server (where you retrieve tokens) in configuration file like this: + +```yaml +credential_proxy_protocol: 'http' +credential_proxy_host: '169.254.169.254' +credential_proxy_port: 80 +credential_proxy_uri: '/latest/meta-data/security-credentials' +``` + +### Code Snippet + +Create default configuration + +``` go +defaultConfig, _ := config.NewDefault() +``` + +Create configuration from AccessKey + +``` go +configuration, _ := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY") + +anotherConfiguration := config.NewDefault() +anotherConfiguration.AccessKeyID = "ACCESS_KEY_ID" +anotherConfiguration.SecretAccessKey = "SECRET_ACCESS_KEY" +``` + +Load user configuration + +``` go +userConfig, _ := config.NewDefault().LoadUserConfig() +``` + +Load configuration from config file + +``` go +configFromFile, _ := config.NewDefault().LoadConfigFromFilepath("PATH/TO/FILE") +``` + +Change API server + +``` go +moreConfiguration, _ := config.NewDefault() + +moreConfiguration.Protocol = "https", +moreConfiguration.Host = "api.private.com", +moreConfiguration.Port = 4433, +moreConfiguration.URI = "/iaas", +``` diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/docs/installation.md b/vendor/github.com/yunify/qingcloud-sdk-go/docs/installation.md new file mode 100644 index 000000000..dc36632f9 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/docs/installation.md @@ -0,0 +1,36 @@ +# Installation Guide + +## Requirement + +This SDK requires Go 1.6 and higher vendor feature, the dependencies this project uses are included in the `vendor` directory. We use [govendor](https://github.com/kardianos/govendor) to manage project dependences. + +___Notice:___ _You can also use Go 1.5 with the `GO15VENDOREXPERIMENT=1`._ + +## Install from source code + +Use `go get` to download this SDK from GitHub: + +``` bash +$ go get -u github.com/yunify/qingcloud-sdk-go +``` + +You can also download a specified version of zipped source code in the repository [releases page](https://github.com/yunify/qingcloud-sdk-go/releases). The zipped source code only contains golang source code without unit test files. + +___For example:___ _[qingcloud-sdk-go-source-v0.7.1.zip](https://github.com/yunify/qingcloud-sdk-go/releases/download/v0.7.1/qingcloud-sdk-go-source-v0.7.1.zip)_ + +## Install from binary release + +After Go 1.7, there's a new feature called Binary-Only Package. It allows distributing packages in binary form without including the source code used for compiling the package. For more information about Binary-Only Package, please read [_GoLang Package Build_](https://golang.org/pkg/go/build/) to know how to use that. + +We provide Linux, macOS and Windows binary packages along with a header files. A header file only contains three lines of content, "//go:binary-only-package" is the first line, the second line is blank, and the second is the package name. There's one header file named "binary.go" for each golang package. + +You can download a specified version of zipped binary release in the repository [releases page](https://github.com/yunify/qingcloud-sdk-go/releases). + +___Notice:___ _We didn't provide 386 version binary packages, since there's almost no one using a 386 machine._ + +___Examples:___ + +- *[qingcloud-sdk-go-header-v0.7.1-go-1.7.zip](https://github.com/yunify/qingcloud-sdk-go/releases/download/v0.7.1/qingcloud-sdk-go-header-v0.7.1-go-1.7.zip)* +- *[qingcloud-sdk-go-binary-v0.7.1-linux_amd64-go-1.7.zip](https://github.com/yunify/qingcloud-sdk-go/releases/download/v0.7.1/qingcloud-sdk-go-binary-v0.7.1-linux_amd64-go-1.7.zip)* +- *[qingcloud-sdk-go-binary-v0.7.1-darwin_amd64-go-1.7.zip](https://github.com/yunify/qingcloud-sdk-go/releases/download/v0.7.1/qingcloud-sdk-go-binary-v0.7.1-darwin_amd64-go-1.7.zip)* +- *[qingcloud-sdk-go-binary-v0.7.1-windows_amd64-go-1.7.zip](https://github.com/yunify/qingcloud-sdk-go/releases/download/v0.7.1/qingcloud-sdk-go-binary-v0.7.1-windows_amd64-go-1.7.zip)* diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/docs/qingcloud_service_usage.md b/vendor/github.com/yunify/qingcloud-sdk-go/docs/qingcloud_service_usage.md new file mode 100644 index 000000000..e24b4d679 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/docs/qingcloud_service_usage.md @@ -0,0 +1,99 @@ +# QingCloud Service Usage Guide + +Import and initialize QingCloud service with a config, and you are ready to use the initialized service. + +Each API function take a Input struct and return an Output struct. The Input struct consists of request params, request headers and request elements, and the Output holds the HTTP status code, response headers, response elements and error (if error occurred). + +``` go +import ( + qc "github.com/yunify/qingcloud-sdk-go/service" +) +``` + +### Code Snippet + +Initialize the QingCloud service with a configuration + +``` go +qcService, _ := qc.Init(configuration) +``` + +Initialize the instance service in a zone + +``` go +pek3aInstance, _ := qcService.Instance("pek3a") +``` + +DescribeInstances + +``` go +iOutput, _ := pek3aInstance.DescribeInstances( + &qc.DescribeInstancesInput{ + Instances: qc.StringSlice([]string{"i-xxxxxxxx"}), + }, +) + +// Print the return code. +fmt.Println(qc.IntValue(iOutput.RetCode)) + +// Print the first instance ID. +fmt.Println(qc.StringValue(iOutput.InstanceSet[0].InstanceID)) +``` + +RunInstances + +``` go +iOutput, _ := pek3aInstance.RunInstances( + &qc.RunInstancesInput{ + ImageID: qc.String("centos7x64d"), + CPU: qc.Int(1), + Memory: qc.Int(1024), + LoginMode: qc.String("keypair"), + LoginKeyPair: qc.String("kp-xxxxxxxx"), + }, +) + +// Print the return code. +fmt.Println(qc.IntValue(iOutput.RetCode)) + +// Print the job ID. +fmt.Println(qc.StringValue(iOutput.JobID)) +``` + +Initialize the volume service in a zone + +``` go +pek3aVolume, _ := qcService.Volume("pek3a") +``` + +DescribeVolumes + +``` go +volOutput, _ := pek3aVolume.DescribeVolumes(&qc.DescribeVolumesInput{ + Volumes: qc.StringValue([]string{"vol-xxxxxxxx"}), +}) + +// Print the return code. +fmt.Println(qc.IntValue(volOutput.RetCode)) + +// Print the first volume name. +fmt.Println(qc.StringValue(volOutput.VolumeSet[0].VolumeName)) +``` + +CreateVolumes + +``` go +volOutput, _ := pek3aVolume.CreateVolumes( + &qc.CreateVolumesInput{ + Size: qc.Int(10), + Count: qc.Int(2), + }, +) + +// Print the return code. +fmt.Println(qc.IntValue(volOutput.RetCode)) + +// Print the job ID. +fmt.Println(qc.StringValue(volOutput.JobID)) +``` + diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/glide.yaml b/vendor/github.com/yunify/qingcloud-sdk-go/glide.yaml new file mode 100644 index 000000000..b04e5a0c3 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/glide.yaml @@ -0,0 +1,14 @@ +package: github.com/yunify/qingcloud-sdk-go +import: +- package: github.com/sirupsen/logrus + version: ~1.0.2 +- package: gopkg.in/yaml.v2 +testImport: +- package: github.com/DATA-DOG/godog + version: ~0.7.3 + subpackages: + - gherkin +- package: github.com/stretchr/testify + version: ~1.1.4 + subpackages: + - assert diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/go.mod b/vendor/github.com/yunify/qingcloud-sdk-go/go.mod new file mode 100644 index 000000000..68b03fa54 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/go.mod @@ -0,0 +1,16 @@ +module github.com/yunify/qingcloud-sdk-go + +go 1.13 + +require ( + github.com/DATA-DOG/godog v0.10.0 + github.com/sirupsen/logrus v1.6.0 + github.com/stretchr/testify v1.6.1 + gopkg.in/yaml.v2 v2.3.0 +) + +replace github.com/DATA-DOG/godog v0.10.0 => github.com/cucumber/godog v0.7.9 + +replace github.com/golang/lint v0.0.0-20201208152925-83fdc39ff7b5 => golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 + +replace golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 => github.com/golang/lint v0.0.0-20201208152925-83fdc39ff7b5 diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/go.sum b/vendor/github.com/yunify/qingcloud-sdk-go/go.sum new file mode 100644 index 000000000..0f810d4e1 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/go.sum @@ -0,0 +1,23 @@ +github.com/cucumber/godog v0.7.9 h1:xBFguv4NJkVgDTGE95g53uzxruxQMclSee/L6rI7NTc= +github.com/cucumber/godog v0.7.9/go.mod h1:2+kSV+QWoeX5l4THUNR1v5OnHuGM9QUD7nQILpqoklI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/request/builder.go b/vendor/github.com/yunify/qingcloud-sdk-go/request/builder.go index c14483bbb..9d9296d49 100644 --- a/vendor/github.com/yunify/qingcloud-sdk-go/request/builder.go +++ b/vendor/github.com/yunify/qingcloud-sdk-go/request/builder.go @@ -130,6 +130,10 @@ func (b *Builder) parseRequestParams() error { requestParams["action"] = b.operation.APIName + if b.operation.Config.URI == "/iam" { + requestParams["token"] = b.operation.Config.Token + } + if !b.input.Elem().IsValid() { return nil } diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/request/builder_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/request/builder_test.go new file mode 100644 index 000000000..95a05a9bc --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/request/builder_test.go @@ -0,0 +1,111 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 request + +import ( + "reflect" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yunify/qingcloud-sdk-go/config" + "github.com/yunify/qingcloud-sdk-go/request/data" +) + +type InstanceServiceProperties struct { + Zone *string `json:"zone" name:"zone"` // Required +} + +type DescribeInstancesInput struct { + ImageID []*string `json:"image_id" name:"image_id" location:"params"` + InstanceClass *int `json:"instance_class" name:"instance_class" location:"params" default:"0"` // Available values: 0, 1 + InstanceType []*string `json:"instance_type" name:"instance_type" location:"params"` + Instances []*string `json:"instances" name:"instances" location:"params"` + Limit *int `json:"limit" name:"limit" location:"params"` + Offset *int `json:"offset" name:"offset" location:"params"` + SearchWord *string `json:"search_word" name:"search_word" location:"params"` + Status []*string `json:"status" name:"status" location:"params"` // Available values: pending, running, stopped, suspended, terminated, ceased + Tags []*string `json:"tags" name:"tags" location:"params"` + Verbose *int `json:"verbose" name:"verbose" location:"params"` // Available values: 0, 1 +} + +func (i *DescribeInstancesInput) Validate() error { + return nil +} + +func String(v string) *string { + return &v +} + +func StringSlice(src []string) []*string { + dst := make([]*string, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +func Int(v int) *int { + return &v +} + +func TestBuilder(t *testing.T) { + + conf, err := config.NewDefault() + assert.Nil(t, err) + conf.Host = "api.qc.dev" + + builder := &Builder{} + operation := &data.Operation{ + Config: conf, + Properties: &InstanceServiceProperties{ + Zone: String("beta"), + }, + APIName: "DescribeInstances", + ServiceName: "Instance", + RequestMethod: "GET", + RequestURI: "/DescribeInstances", + StatusCodes: []int{ + 200, + }, + } + inputValue := reflect.ValueOf(&DescribeInstancesInput{ + ImageID: StringSlice([]string{"img-xxxxxxxx", "img-zzzzzzzz"}), + InstanceClass: Int(0), + InstanceType: StringSlice([]string{"type1", "type2"}), + Instances: StringSlice([]string{"i-xxxxxxxx", "i-zzzzzzzz"}), + SearchWord: String("search_word"), + Status: StringSlice([]string{"running"}), + Tags: StringSlice([]string{"tag1", "tag2"}), + Verbose: Int(1), + }) + httpRequest, err := builder.BuildHTTPRequest(operation, &inputValue) + assert.Nil(t, err) + + assert.True(t, strings.Contains(httpRequest.URL.String(), "https://api.qc.dev:443/iaas?")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "image_id.1=img-xxxxxxxx")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "instance_class=0")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "instance_type.1=type1")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "instances.2=i-zzzzzzzz")) + assert.True(t, !strings.Contains(httpRequest.URL.String(), "limit")) + assert.True(t, !strings.Contains(httpRequest.URL.String(), "offset")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "search_word=search_word")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "status.1=running")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "tags.1=tag1")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "verbose=1")) + assert.True(t, strings.Contains(httpRequest.URL.String(), "zone=beta")) +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/request/request.go b/vendor/github.com/yunify/qingcloud-sdk-go/request/request.go index b6f171d08..2008662f6 100644 --- a/vendor/github.com/yunify/qingcloud-sdk-go/request/request.go +++ b/vendor/github.com/yunify/qingcloud-sdk-go/request/request.go @@ -19,6 +19,7 @@ package request import ( "errors" "fmt" + "io/ioutil" "net/http" "reflect" "time" @@ -38,6 +39,29 @@ type Request struct { HTTPResponse *http.Response } +// DefaultCredentialProxyHost is default credential proxy host +const DefaultCredentialProxyHost = "169.254.169.254" + +// DefaultCredentialProxyPort is default credential proxy port +const DefaultCredentialProxyPort = 80 + +// DefaultCredentialProxyProtocol is default credential proxy protocol +const DefaultCredentialProxyProtocol = "http" + +// DefaultCredentialProxyURI is default credential proxy URI +const DefaultCredentialProxyURI = "/latest/meta-data/security-credentials" + +// TokenOutput is the structure of token when retrieving it +type TokenOutput struct { + Jti string `json:"jti"` + Token string `json:"id_token"` + AccessKey string `json:"access_key"` + SecretAccess string `json:"secret_key"` + Expiration int64 `json:"expiration"` + Action string `json:"action,omitempty"` + RetCode string `json:"ret_code"` +} + // New create a Request from given Operation, Input and Output. // It returns a Request. func New(o *data.Operation, i data.Input, x interface{}) (*Request, error) { @@ -89,6 +113,20 @@ func (r *Request) Send() error { } func (r *Request) check() error { + if r.Operation.Config.AccessKeyID == "" && r.Operation.Config.SecretAccessKey == "" || r.Operation.Config.URI == "/iam" && r.isTokenExpired() { + t := TokenOutput{} + err := t.GetToken(r.getCredentialProxyURL()) + + if err != nil { + return err + } + r.Operation.Config.AccessKeyID = t.AccessKey + r.Operation.Config.SecretAccessKey = t.SecretAccess + r.Operation.Config.URI = "/iam" + r.Operation.Config.Token = t.Token + r.Operation.Config.Expiration = t.Expiration + } + if r.Operation.Config.AccessKeyID == "" { return errors.New("access key not provided") } @@ -169,3 +207,66 @@ func (r *Request) unpack() error { return nil } + +// GetToken is used to get token from credential proxy server +func (t *TokenOutput) GetToken(credentialProxyURL string) error { + response, err := http.Get(credentialProxyURL) + if err != nil { + return err + } + + content, err := ioutil.ReadAll(response.Body) + + if err != nil { + return err + } + + _, err = utils.JSONDecode(content, t) + + return err +} + +func (r *Request) isTokenExpired() bool { + if r.Operation.Config.Token == "" { + return true + } + + now := time.Now().UTC().Unix() + + return now >= r.Operation.Config.Expiration +} + +func (r *Request) getCredentialProxyURL() string { + var credentialProxyProtocol string + var credentialProxyHost string + var credentialProxyPort int + var credentialProxyURI string + + if r.Operation.Config.CredentialProxyProtocol != "" { + credentialProxyProtocol = r.Operation.Config.CredentialProxyProtocol + } else { + credentialProxyProtocol = DefaultCredentialProxyProtocol + } + + if r.Operation.Config.CredentialProxyHost != "" { + credentialProxyHost = r.Operation.Config.CredentialProxyHost + } else { + credentialProxyHost = DefaultCredentialProxyHost + } + + if r.Operation.Config.CredentialProxyPort != 0 { + credentialProxyPort = r.Operation.Config.CredentialProxyPort + } else { + credentialProxyPort = DefaultCredentialProxyPort + } + + if r.Operation.Config.CredentialProxyURI != "" { + credentialProxyURI = r.Operation.Config.CredentialProxyURI + } else { + credentialProxyURI = DefaultCredentialProxyURI + } + + credentialProxyURL := fmt.Sprintf("%s://%s:%d%s", credentialProxyProtocol, credentialProxyHost, credentialProxyPort, credentialProxyURI) + + return credentialProxyURL +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/request/signer_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/request/signer_test.go new file mode 100644 index 000000000..aa8b166d6 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/request/signer_test.go @@ -0,0 +1,67 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 request + +import ( + "net/http" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/yunify/qingcloud-sdk-go/utils" +) + +func TestSigner0(t *testing.T) { + url := "https://api.qc.dev/iaas?instance.0=i-xxxxxxxx&action=DescribeInstance&verbose=1" + httpRequest, err := http.NewRequest("GET", url, nil) + httpRequest.Header.Set("Date", utils.TimeToString(time.Time{}, "RFC 822")) + assert.Nil(t, err) + + s := Signer{ + AccessKeyID: "ENV_ACCESS_KEY_ID", + SecretAccessKey: "ENV_SECRET_ACCESS_KEY", + } + err = s.WriteSignature(httpRequest) + assert.Nil(t, err) + assert.True(t, strings.Contains(httpRequest.URL.String(), "https://api.qc.dev/iaas?")) + assert.True(t, strings.Contains( + httpRequest.URL.String(), "time_stamp=0001-01-01T00%3A00%3A00Z")) + assert.True(t, strings.Contains( + httpRequest.URL.String(), "signature=ZHa2iQ8PeyP1ktMF9C%2BDjOQBl537ti9RnYZ1Qqr6KRg%3D")) +} + +func TestSigner1(t *testing.T) { + url := "https://api.qc.dev/iaas/?action=RunInstances&count=1&image_id=centos64x86a&instance_name=demo&instance_type=small_b&login_mode=passwd&login_passwd=QingCloud20130712&signature_method=HmacSHA256&signature_version=1&time_stamp=2013-08-27T14%3A30%3A10Z&version=1&vxnets.1=vxnet-0&zone=pek1" + httpRequest, err := http.NewRequest("GET", url, nil) + timeValue, err := utils.StringToTime("2013-08-27T14:30:10Z", "ISO 8601") + assert.Nil(t, err) + httpRequest.Header.Set("Date", utils.TimeToString(timeValue, "RFC 822")) + assert.Nil(t, err) + + s := Signer{ + AccessKeyID: "QYACCESSKEYIDEXAMPLE", + SecretAccessKey: "SECRETACCESSKEY", + } + err = s.WriteSignature(httpRequest) + assert.Nil(t, err) + assert.True(t, strings.Contains(httpRequest.URL.String(), "https://api.qc.dev/iaas/?")) + assert.True(t, strings.Contains( + httpRequest.URL.String(), "time_stamp=2013-08-27T14%3A30%3A10Z")) + assert.True(t, strings.Contains( + httpRequest.URL.String(), "signature=32bseYy39DOlatuewpeuW5vpmW51sD1A%2FJdGynqSpP8%3D")) +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/request/unpacker_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/request/unpacker_test.go new file mode 100644 index 000000000..4848a9dd2 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/request/unpacker_test.go @@ -0,0 +1,198 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 request + +import ( + "bytes" + "io/ioutil" + "net/http" + "reflect" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/yunify/qingcloud-sdk-go/request/data" + "github.com/yunify/qingcloud-sdk-go/request/errors" +) + +func StringValue(v *string) string { + if v != nil { + return *v + } + return "" +} + +func IntValue(v *int) int { + if v != nil { + return *v + } + return 0 +} + +func TimeValue(v *time.Time) time.Time { + if v != nil { + return *v + } + return time.Time{} +} + +func TestUnpackerUnpackHTTPRequest(t *testing.T) { + type Instance struct { + Device *string `json:"device" name:"device"` + InstanceID *string `json:"instance_id" name:"instance_id"` + InstanceName *string `json:"instance_name" name:"instance_name"` + } + + type Volume struct { + CreateTime *time.Time `json:"create_time" name:"create_time" format:"ISO 8601"` + Description *string `json:"description" name:"description"` + Instance *Instance `json:"instance" name:"instance"` + Size *int `json:"size" name:"size"` + Status *string `json:"status" name:"status"` + StatusTime *time.Time `json:"status_time" name:"status_time" format:"ISO 8601"` + SubCode *int `json:"sub_code" name:"sub_code"` + TransitionStatus *string `json:"transition_status" name:"transition_status"` + VolumeID *string `json:"volume_id" name:"volume_id"` + VolumeName *string `json:"volume_name" name:"volume_name"` + } + + type DescribeVolumesOutput struct { + StatusCode int `location:"statusCode"` + Error *errors.QingCloudError + + Action *string `json:"action" name:"action"` + RetCode *int `json:"ret_code" name:"ret_code"` + TotalCount *int `json:"total_count" name:"total_count"` + VolumeSet []*Volume `json:"volume_set" name:"volume_set"` + Message *string `json:"message" name:"message"` + } + + httpResponse := &http.Response{Header: http.Header{}} + httpResponse.StatusCode = 200 + httpResponse.Header.Set("Content-Type", "application/json") + responseString := `{ + "action": "DescribeVolumesResponse", + "total_count": 1024, + "volume_set": [ + { + "status": "in-use", + "description": null, + "volume_name": "vol name", + "sub_code": 0, + "transition_status": "", + "instance": { + "instance_id": "i-xxxxxxxx", + "instance_name": "", + "device": "/dev/sdb" + }, + "create_time": "2013-08-30T05:13:25Z", + "volume_id": "vol-xxxxxxxx", + "status_time": "2013-08-30T05:13:32Z", + "size": 10 + } + ], + "ret_code": 0 + }` + responseString = strings.Replace(responseString, ": ", ":", -1) + httpResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte(responseString))) + + output := &DescribeVolumesOutput{} + outputValue := reflect.ValueOf(output) + unpacker := Unpacker{} + err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue) + assert.Nil(t, err) + assert.Equal(t, "i-xxxxxxxx", StringValue(output.VolumeSet[0].Instance.InstanceID)) + assert.Equal(t, "vol-xxxxxxxx", StringValue(output.VolumeSet[0].VolumeID)) + assert.Equal(t, "vol name", StringValue(output.VolumeSet[0].VolumeName)) + assert.Equal(t, 1024, IntValue(output.TotalCount)) + statusTime := time.Date(2013, 8, 30, 5, 13, 32, 0, time.UTC) + assert.Equal(t, statusTime, TimeValue(output.VolumeSet[0].StatusTime)) +} + +func TestUnpacker_UnpackHTTPRequestWithError(t *testing.T) { + type DescribeInstanceTypesOutput struct { + RetCode *int `json:"ret_code" name:"ret_code"` + Message *string `json:"message" name:"message"` + } + + httpResponse := &http.Response{Header: http.Header{}} + httpResponse.StatusCode = 200 + httpResponse.Header.Set("Content-Type", "application/json") + responseString := `{ + "message":"PermissionDenied, instance [i-xxxxxxxx] is not running, can not be stopped", + "ret_code":1400 + }` + httpResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte(responseString))) + + output := &DescribeInstanceTypesOutput{} + outputValue := reflect.ValueOf(output) + unpacker := Unpacker{} + err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue) + assert.NotNil(t, err) + switch e := err.(type) { + case errors.QingCloudError: + assert.Equal(t, e.RetCode, 1400) + } +} + +func TestUnpacker_UnpackHTTPRequestWithWrongType2(t *testing.T) { + type DescribeInstanceTypesOutput struct { + RetCode *int `json:"ret_code" name:"ret_code"` + Message *string `json:"message" name:"message"` + } + + httpResponse := &http.Response{Header: http.Header{}} + httpResponse.StatusCode = 200 + httpResponse.Header.Set("Content-Type", "application/json") + responseString := `{ + "message": null, + "ret_code":1400 + }` + httpResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte(responseString))) + + output := &DescribeInstanceTypesOutput{} + outputValue := reflect.ValueOf(output) + unpacker := Unpacker{} + err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue) + if !assert.NotNil(t, err) { + t.FailNow() + } + println("err", err.Error()) +} + +func TestUnpacker_UnpackHTTPRequestWithWrongHTTPStatus(t *testing.T) { + type DescribeInstanceTypesOutput struct { + RetCode *int `json:"ret_code" name:"ret_code"` + Message *string `json:"message" name:"message"` + } + + httpResponse := &http.Response{Header: http.Header{}} + httpResponse.StatusCode = 500 + httpResponse.Header.Set("Content-Type", "application/json") + responseString := "{}" + httpResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte(responseString))) + + output := &DescribeInstanceTypesOutput{} + outputValue := reflect.ValueOf(output) + unpacker := Unpacker{} + err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue) + if !assert.NotNil(t, err) { + t.FailNow() + } + println("err", err.Error()) +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/service/convert_types_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/service/convert_types_test.go new file mode 100644 index 000000000..5660c9e40 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/service/convert_types_test.go @@ -0,0 +1,311 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 service + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var testCasesStringSlice = [][]string{ + {"a", "b", "c", "d", "e"}, + {"a", "b", "", "", "e"}, +} + +func TestStringSlice(t *testing.T) { + for idx, in := range testCasesStringSlice { + if in == nil { + continue + } + out := StringSlice(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) + } + + out2 := StringValueSlice(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) + } +} + +var testCasesStringValueSlice = [][]*string{ + {String("a"), String("b"), nil, String("c")}, +} + +func TestStringValueSlice(t *testing.T) { + for idx, in := range testCasesStringValueSlice { + if in == nil { + continue + } + out := StringValueSlice(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + if in[i] == nil { + assert.Empty(t, out[i], "Unexpected value at idx %d", idx) + } else { + assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) + } + } + + out2 := StringSlice(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + for i := range out2 { + if in[i] == nil { + assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) + } else { + assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) + } + } + } +} + +var testCasesStringMap = []map[string]string{ + {"a": "1", "b": "2", "c": "3"}, +} + +func TestStringMap(t *testing.T) { + for idx, in := range testCasesStringMap { + if in == nil { + continue + } + out := StringMap(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) + } + + out2 := StringValueMap(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) + } +} + +var testCasesBoolSlice = [][]bool{ + {true, true, false, false}, +} + +func TestBoolSlice(t *testing.T) { + for idx, in := range testCasesBoolSlice { + if in == nil { + continue + } + out := BoolSlice(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) + } + + out2 := BoolValueSlice(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) + } +} + +var testCasesBoolValueSlice = [][]*bool{} + +func TestBoolValueSlice(t *testing.T) { + for idx, in := range testCasesBoolValueSlice { + if in == nil { + continue + } + out := BoolValueSlice(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + if in[i] == nil { + assert.Empty(t, out[i], "Unexpected value at idx %d", idx) + } else { + assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) + } + } + + out2 := BoolSlice(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + for i := range out2 { + if in[i] == nil { + assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) + } else { + assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) + } + } + } +} + +var testCasesBoolMap = []map[string]bool{ + {"a": true, "b": false, "c": true}, +} + +func TestBoolMap(t *testing.T) { + for idx, in := range testCasesBoolMap { + if in == nil { + continue + } + out := BoolMap(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) + } + + out2 := BoolValueMap(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) + } +} + +var testCasesIntSlice = [][]int{ + {1, 2, 3, 4}, +} + +func TestIntSlice(t *testing.T) { + for idx, in := range testCasesIntSlice { + if in == nil { + continue + } + out := IntSlice(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) + } + + out2 := IntValueSlice(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) + } +} + +var testCasesIntValueSlice = [][]*int{} + +func TestIntValueSlice(t *testing.T) { + for idx, in := range testCasesIntValueSlice { + if in == nil { + continue + } + out := IntValueSlice(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + if in[i] == nil { + assert.Empty(t, out[i], "Unexpected value at idx %d", idx) + } else { + assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) + } + } + + out2 := IntSlice(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + for i := range out2 { + if in[i] == nil { + assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) + } else { + assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) + } + } + } +} + +var testCasesIntMap = []map[string]int{ + {"a": 3, "b": 2, "c": 1}, +} + +func TestIntMap(t *testing.T) { + for idx, in := range testCasesIntMap { + if in == nil { + continue + } + out := IntMap(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) + } + + out2 := IntValueMap(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) + } +} + +var testCasesTimeSlice = [][]time.Time{ + {time.Now(), time.Now().AddDate(100, 0, 0)}, +} + +func TestTimeSlice(t *testing.T) { + for idx, in := range testCasesTimeSlice { + if in == nil { + continue + } + out := TimeSlice(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) + } + + out2 := TimeValueSlice(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) + } +} + +var testCasesTimeValueSlice = [][]*time.Time{} + +func TestTimeValueSlice(t *testing.T) { + for idx, in := range testCasesTimeValueSlice { + if in == nil { + continue + } + out := TimeValueSlice(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + if in[i] == nil { + assert.Empty(t, out[i], "Unexpected value at idx %d", idx) + } else { + assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) + } + } + + out2 := TimeSlice(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + for i := range out2 { + if in[i] == nil { + assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) + } else { + assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) + } + } + } +} + +var testCasesTimeMap = []map[string]time.Time{ + {"a": time.Now().AddDate(-100, 0, 0), "b": time.Now()}, +} + +func TestTimeMap(t *testing.T) { + for idx, in := range testCasesTimeMap { + if in == nil { + continue + } + out := TimeMap(in) + assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) + for i := range out { + assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) + } + + out2 := TimeValueMap(out) + assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) + assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) + } +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/service/image.go b/vendor/github.com/yunify/qingcloud-sdk-go/service/image.go index 47ca250c1..b0229d0e8 100644 --- a/vendor/github.com/yunify/qingcloud-sdk-go/service/image.go +++ b/vendor/github.com/yunify/qingcloud-sdk-go/service/image.go @@ -238,6 +238,7 @@ type DescribeImagesInput struct { Provider *string `json:"provider" name:"provider" location:"params"` SearchWord *string `json:"search_word" name:"search_word" location:"params"` Status []*string `json:"status" name:"status" location:"params"` + Tags []*string `json:"tags" name:"tags" location:"params"` // Verbose's available values: 0 Verbose *int `json:"verbose" name:"verbose" default:"0" location:"params"` // Visibility's available values: public, private diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/service/types.go b/vendor/github.com/yunify/qingcloud-sdk-go/service/types.go index c6dd979d8..297626d08 100644 --- a/vendor/github.com/yunify/qingcloud-sdk-go/service/types.go +++ b/vendor/github.com/yunify/qingcloud-sdk-go/service/types.go @@ -1202,6 +1202,7 @@ func (v *InstanceVxNet) Validate() error { type Job struct { CreateTime *time.Time `json:"create_time" name:"create_time" format:"ISO 8601"` + Directive *string `json:"directive" name:"directive"` JobAction *string `json:"job_action" name:"job_action"` JobID *string `json:"job_id" name:"job_id"` Owner *string `json:"owner" name:"owner"` @@ -3260,8 +3261,9 @@ type VxNet struct { VpcRouterID *string `json:"vpc_router_id" name:"vpc_router_id"` VxNetID *string `json:"vxnet_id" name:"vxnet_id"` VxNetName *string `json:"vxnet_name" name:"vxnet_name"` - // VxNetType's available values: 0, 1 - VxNetType *int `json:"vxnet_type" name:"vxnet_type"` + // VxNetType's available values: 0, 1, 2 + VxNetType *int `json:"vxnet_type" name:"vxnet_type"` + ZoneID *string `json:"zone_id" name:"zone_id"` } func (v *VxNet) Validate() error { @@ -3281,7 +3283,7 @@ func (v *VxNet) Validate() error { } if v.VxNetType != nil { - vxnetTypeValidValues := []string{"0", "1"} + vxnetTypeValidValues := []string{"0", "1", "2"} vxnetTypeParameterValue := fmt.Sprint(*v.VxNetType) vxnetTypeIsValid := false diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/service/vxnet.go b/vendor/github.com/yunify/qingcloud-sdk-go/service/vxnet.go index 7fa2a3e00..fb4038698 100644 --- a/vendor/github.com/yunify/qingcloud-sdk-go/service/vxnet.go +++ b/vendor/github.com/yunify/qingcloud-sdk-go/service/vxnet.go @@ -259,9 +259,10 @@ type DescribeVxNetsInput struct { Tags []*string `json:"tags" name:"tags" location:"params"` // Verbose's available values: 0, 1 Verbose *int `json:"verbose" name:"verbose" default:"0" location:"params"` - // VxNetType's available values: 0, 1 + // VxNetType's available values: 0, 1, 2 VxNetType *int `json:"vxnet_type" name:"vxnet_type" location:"params"` VxNets []*string `json:"vxnets" name:"vxnets" location:"params"` + Zone *string `json:"zone" name:"zone" location:"params"` } func (v *DescribeVxNetsInput) Validate() error { @@ -287,7 +288,7 @@ func (v *DescribeVxNetsInput) Validate() error { } if v.VxNetType != nil { - vxnetTypeValidValues := []string{"0", "1"} + vxnetTypeValidValues := []string{"0", "1", "2"} vxnetTypeParameterValue := fmt.Sprint(*v.VxNetType) vxnetTypeIsValid := false diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/template/manifest.json b/vendor/github.com/yunify/qingcloud-sdk-go/template/manifest.json new file mode 100644 index 000000000..9d2f86f86 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/template/manifest.json @@ -0,0 +1,8 @@ +{ + "output": { + "file_naming": { + "style": "snake_case", + "extension": ".go" + } + } +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/template/service.tmpl b/vendor/github.com/yunify/qingcloud-sdk-go/template/service.tmpl new file mode 100644 index 000000000..36ceb1cd1 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/template/service.tmpl @@ -0,0 +1,48 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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. +// +------------------------------------------------------------------------- + +{{$service := .Data.Service}} + +// Package service provides {{$service.Name}} Service API (API Version {{$service.APIVersion}}) +package service + +import ( + "github.com/yunify/qingcloud-sdk-go/config" + "github.com/yunify/qingcloud-sdk-go/request" + "github.com/yunify/qingcloud-sdk-go/request/data" + "github.com/yunify/qingcloud-sdk-go/logger" +) + +{{if $service.Description}}// {{$service.Name | camelCase}}Service: {{$service.Description}}{{end}} +type {{$service.Name | camelCase}}Service struct { + Config *config.Config + Properties *{{$service.Name | camelCase}}ServiceProperties +} + +type {{$service.Name | camelCase}}ServiceProperties struct { + {{template "RenderProperties" passThrough $service.Properties ""}} +} + +func Init(c *config.Config) (*{{$service.Name | camelCase}}Service, error) { + properties := &{{$service.Name | camelCase}}ServiceProperties{} + logger.SetLevel(c.LogLevel) + return &{{$service.Name | camelCase}}Service{Config: c, Properties: properties}, nil +} + +{{range $_, $operation := $service.Operations}} + {{$belongs := printf "%sService" ($service.Name | camelCase)}} + {{template "RenderOperation" passThrough $belongs $operation}} +{{end}} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/template/shared.tmpl b/vendor/github.com/yunify/qingcloud-sdk-go/template/shared.tmpl new file mode 100644 index 000000000..c22747384 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/template/shared.tmpl @@ -0,0 +1,240 @@ +{{define "Type"}} + {{- $typeName := index . 0 -}} + {{- $disablePointer := index . 1 -}} + + {{- if eq $typeName "string" -}} + {{- if not $disablePointer -}}*{{- end -}}string + {{- else if eq $typeName "boolean" -}} + {{- if not $disablePointer -}}*{{- end -}}bool + {{- else if eq $typeName "integer" -}} + {{- if not $disablePointer -}}*{{- end -}}int + {{- else if eq $typeName "timestamp" -}} + {{- if not $disablePointer -}}*{{- end -}}time.Time + {{- else if eq $typeName "binary" -}} + io.Reader + {{- else if eq $typeName "array" -}} + interface{} + {{- else if eq $typeName "object" -}} + interface{} + {{- else if eq $typeName "map" -}} + interface{} + {{- else if eq $typeName "any" -}} + interface{} + {{- else if eq $typeName "" -}} + interface{} + {{- else -}} + *{{$typeName | camelCase}} + {{- end -}} +{{end}} + +{{define "PropertyType"}} + {{- $property := index . 0 -}} + {{- $disablePointer := index . 1 -}} + + {{- if eq $property.Type "object" -}} + {{template "Type" passThrough $property.ExtraType $disablePointer}} + {{- else if eq $property.Type "array" -}} + []{{template "Type" passThrough $property.ExtraType $disablePointer}} + {{- else if eq $property.Type "map" -}} + map[string]{{template "Type" passThrough $property.ExtraType $disablePointer}} + {{- else if eq $property.Type "any" -}} + {{template "Type" passThrough $property.Type $disablePointer}} + {{- else -}} + {{template "Type" passThrough $property.Type $disablePointer}} + {{- end -}} +{{end}} + +{{define "PropertyTags"}} + {{- $property := . -}} + {{- printf `json:"%s"` ($property.Name | normalized) -}} + {{- printf ` name:"%s"` ($property.Name | normalized) -}} + {{- if $property.Format}} + {{- printf ` format:"%s"` $property.Format -}} + {{- end -}} + {{- if $property.Default -}} + {{- printf ` default:"%s"` $property.Default -}} + {{- end -}} +{{end}} + +{{define "PropertyExtraTags"}} + {{- $PropertyExtraTags := . -}} + {{- if $PropertyExtraTags -}} + {{- printf " %s" $PropertyExtraTags -}} + {{- end -}} +{{end}} + +{{define "RenderProperties"}} + {{- $customizedType := index . 0 -}} + {{- $PropertyExtraTags := index . 1 -}} + + {{range $_, $property := $customizedType.Properties -}} + {{if $property.Description -}} + // {{$property.Description}} + {{end -}} + {{if $property.Enum -}} + // {{$property.ID | camelCase}}'s available values: {{$property.Enum | commaConnected}} + {{end -}} + {{$property.ID | camelCase | upperFirst}}{{" " -}} + {{template "PropertyType" passThrough $property false}}{{" " -}} + `{{template "PropertyTags" $property}}{{template "PropertyExtraTags" $PropertyExtraTags}}`{{" " -}} + {{if $property.IsRequired -}} + // Required + {{- end}} + {{end -}} +{{end}} + +{{define "RenderOperation"}} + {{$belongs := index . 0}} + {{$operation := index . 1}} + + {{$opID := $operation.ID | camelCase}} + + {{if $operation.Description -}} + // {{$opID}}: {{$operation.Description}} + {{- end}} + {{if $operation.DocumentationURL -}} + // Documentation URL: {{$operation.DocumentationURL}} + {{- end}} + func (s *{{$belongs}}) {{$opID}}(i *{{$opID}}Input) (*{{$opID}}Output, error) { + if i == nil { + i = &{{$opID}}Input{} + } + o := &data.Operation{ + Config: s.Config, + Properties: s.Properties, + APIName: "{{$operation.Name}}", + RequestMethod: "{{$operation.Request.Method}}", + } + + x := &{{$opID}}Output{} + r, err := request.New(o, i, x) + if err != nil { + return nil, err + } + + err = r.Send() + if err != nil { + return nil, err + } + + return x, err + } + + type {{$opID}}Input struct { + {{if $operation.Request.Query.Properties | len -}} + {{$data := $operation.Request.Query}} + {{template "RenderProperties" passThrough $data `location:"params"`}} + {{- end -}} + } + + func (v *{{$opID}}Input) Validate() error { + {{template "ValidateCustomizedType" $operation.Request.Query}} + {{template "ValidateCustomizedType" $operation.Request.Headers}} + {{template "ValidateCustomizedType" $operation.Request.Elements}} + + return nil + } + + type {{$opID}}Output struct { + Message *string `json:"message" name:"message"` + {{- range $k, $reponse := $operation.Responses -}} + {{- if $reponse.Elements.Properties | len -}} + {{$data := $reponse.Elements}} + {{template "RenderProperties" passThrough $data `location:"elements"` $operation.Name}} + {{- end -}} + {{- end -}} + } +{{end}} + +{{define "SubServiceInitParams"}} + {{- $customizedType := index . 0 -}} + {{- $disablePointer := index . 1 -}} + + {{- range $_, $property := $customizedType.Properties -}} + {{$property.ID | camelCase | lowerFirstWord}}{{" " -}} + {{template "PropertyType" passThrough $property $disablePointer}}, + {{- end -}} +{{end}} + +{{define "ValidateCustomizedType"}} + {{$customizedType := .}} + + {{range $_, $property := $customizedType.Properties}} + {{$isNormalType := or (eq $property.Type "string") (eq $property.Type "integer")}} + {{$isContentLength := eq $property.ID "Content-Length"}} + {{if and $isNormalType (not $isContentLength) }} + {{if $property.IsRequired }} + if v.{{$property.ID | camelCase}} == nil { + return errors.ParameterRequiredError{ + ParameterName: "{{$property.ID | camelCase}}", + ParentName: "{{$customizedType.ID | camelCase}}", + } + } + {{end}} + {{$parameterName := $property.ID | camelCase | lowerFirstWord}} + {{if gt ($property.Enum | len) 0}} + if v.{{$property.ID | camelCase}} != nil { + {{$parameterName}}ValidValues := []string{ + {{- $property.Enum | commaConnectedWithQuote -}} + } + {{$parameterName}}ParameterValue := fmt.Sprint(*v.{{$property.ID | camelCase}}) + + {{$parameterName}}IsValid := false + for _, value := range {{$parameterName}}ValidValues { + if value == {{$parameterName}}ParameterValue { + {{$parameterName}}IsValid = true + } + } + + if !{{$parameterName}}IsValid { + return errors.ParameterValueNotAllowedError{ + ParameterName: "{{$property.ID | camelCase}}", + ParameterValue: {{$parameterName}}ParameterValue, + AllowedValues: {{$parameterName}}ValidValues, + } + } + } + {{end}} + {{end}} + + {{if and (eq $property.Type "object") (ne $property.ExtraType "") }} + if v.{{$property.ID | camelCase}} != nil { + if err := v.{{$property.ID | camelCase}}.Validate(); err != nil { + return err + } + } + {{if $property.IsRequired }} + if v.{{$property.ID | camelCase}} == nil { + return errors.ParameterRequiredError{ + ParameterName: "{{$property.ID | camelCase}}", + ParentName: "{{$customizedType.ID | camelCase}}", + } + } + {{end}} + {{end}} + + {{if eq $property.Type "array"}} + {{if $property.IsRequired}} + if len(v.{{$property.ID | camelCase}}) == 0 { + return errors.ParameterRequiredError{ + ParameterName: "{{$property.ID | camelCase}}", + ParentName: "{{$customizedType.ID | camelCase}}", + } + } + {{end}} + {{$isNotString := ne $property.ExtraType "string"}} + {{$isNotInteger := ne $property.ExtraType "integer"}} + {{$isNotTimestamp := ne $property.ExtraType "timestamp"}} + {{$isNotInterface := ne $property.ExtraType "object"}} + {{if and $isNotString $isNotInteger $isNotTimestamp $isNotInterface}} + if len(v.{{$property.ID | camelCase}}) > 0 { + for _, property := range v.{{$property.ID | camelCase}} { + if err := property.Validate(); err != nil { + return err + } + } + } + {{end}} + {{end}} + {{end}} +{{end}} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/template/sub_service.tmpl b/vendor/github.com/yunify/qingcloud-sdk-go/template/sub_service.tmpl new file mode 100644 index 000000000..b9c7a2496 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/template/sub_service.tmpl @@ -0,0 +1,61 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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. +// +------------------------------------------------------------------------- + +{{$service := .Data.Service}} +{{$subService := index .Data.SubServices .CurrentSubServiceID}} + +package service + +import ( + "fmt" + "time" + + "github.com/yunify/qingcloud-sdk-go/config" + "github.com/yunify/qingcloud-sdk-go/request" + "github.com/yunify/qingcloud-sdk-go/request/data" + "github.com/yunify/qingcloud-sdk-go/request/errors" +) + +var _ fmt.State +var _ time.Time + +type {{$subService.ID | camelCase}}Service struct { + Config *config.Config + Properties *{{$subService.ID | camelCase}}ServiceProperties +} + +type {{$subService.ID | camelCase}}ServiceProperties struct { + {{template "RenderProperties" passThrough $service.Properties "" -}} + + {{template "RenderProperties" passThrough $subService.Properties "" -}} +} + +func (s *{{$service.Name | camelCase}}Service) {{$subService.ID | camelCase}}( + {{- template "SubServiceInitParams" passThrough $subService.Properties true -}} + ) (*{{$subService.ID | camelCase}}Service, error) { + properties := &{{$subService.ID | camelCase}}ServiceProperties{ + {{range $_, $property := $subService.Properties.Properties -}} + {{$property.ID | upperFirst}}: &{{$property.ID}}, + {{end}} + } + + return &{{$subService.ID | camelCase}}Service{Config: s.Config, Properties: properties}, nil +} + +{{range $_, $operation := $subService.Operations}} + {{$belongs := printf "%sService" ($subService.Name | camelCase)}} + {{template "RenderOperation" passThrough $belongs $operation}} +{{end}} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/template/types.tmpl b/vendor/github.com/yunify/qingcloud-sdk-go/template/types.tmpl new file mode 100644 index 000000000..7007149b5 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/template/types.tmpl @@ -0,0 +1,39 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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. +// +------------------------------------------------------------------------- + +{{$service := .Data.Service}} +{{$customizedTypes := .Data.CustomizedTypes}} + +package service + +import ( + "fmt" + "time" + + "github.com/yunify/qingcloud-sdk-go/request/errors" +) + +{{range $_, $customizedType := $customizedTypes}} + type {{$customizedType.ID | camelCase}} struct { + {{template "RenderProperties" passThrough $customizedType ""}} + } + + func (v *{{$customizedType.ID | camelCase}}) Validate() error { + {{template "ValidateCustomizedType" $customizedType}} + + return nil + } +{{end}} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/.gitignore b/vendor/github.com/yunify/qingcloud-sdk-go/test/.gitignore new file mode 100644 index 000000000..4e3825eda --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/.gitignore @@ -0,0 +1,3 @@ +# Ignore local config +config.yaml +test_config.yaml diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/config.yaml.example b/vendor/github.com/yunify/qingcloud-sdk-go/test/config.yaml.example new file mode 100644 index 000000000..7e0bcf588 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/config.yaml.example @@ -0,0 +1,13 @@ +# QingStor services configuration + +qy_access_key_id: 'ACCESS_KEY_ID' +qy_secret_access_key: 'SECRET_ACCESS_KEY' + +host: 'api.qingcloud.com' +port: 443 +protocol: 'https' +uri: '/iaas' +connection_retries: 3 + +# Valid log levels are "debug", "info", "warn", "error", and "fatal". +log_level: 'debug' diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/features/qingcloud.feature b/vendor/github.com/yunify/qingcloud-sdk-go/test/features/qingcloud.feature new file mode 100644 index 000000000..cf992e043 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/features/qingcloud.feature @@ -0,0 +1,46 @@ +# Read the Gherkin grammar here: https://cucumber.io/docs/reference + +@service +Feature: the QingCloud service + The QingCloud service should be available + As a QingCloud user + I can list all zones + + Scenario: need to use QingCloud service + When initialize QingCloud service + Then the QingCloud service is initialized + + Scenario: need to use instance service + When initialize instance service + Then the instance service is initialized + + Scenario: need to use job service + When initialize job service + Then the job service is initialized + + # DescribeZones + Scenario: want to know all QingCloud zones + When describe zones + Then describe zones should get 1 zone at least + Then describe zones should have the zone I'm using + + # RunInstances + Scenario: need instance + Given instance configuration: + | image_id | instance_type | count | login_mode | login_passwd | + | centos7x64d | c1m1 | 1 | passwd | Hello2333 | + When run instances + Then run instances should get a job ID + Then run instances will be finished + + # TerminateInstances + Scenario: need to remove instance + When terminate instances + Then terminate instances should get a job ID + Then terminate instances will be finished + + # DescribeJobs + Scenario: want to know all jobs + When describe jobs + Then describe jobs should get 2 job at least + Then describe jobs should have the jobs I just created diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/instance_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/test/instance_test.go new file mode 100644 index 000000000..5b457fe96 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/instance_test.go @@ -0,0 +1,153 @@ +package main + +import ( + "errors" + "fmt" + "github.com/DATA-DOG/godog" + "github.com/DATA-DOG/godog/gherkin" + qcErrors "github.com/yunify/qingcloud-sdk-go/request/errors" + qc "github.com/yunify/qingcloud-sdk-go/service" + "strconv" + "time" +) + +var instanceService *qc.InstanceService +var runInstanceInput *qc.RunInstancesInput +var runInstanceOutput *qc.RunInstancesOutput + +func InstanceFeatureContext(s *godog.Suite) { + s.Step(`^initialize instance service$`, initializeInstanceService) + s.Step(`^the instance service is initialized$`, theInstanceServiceIsInitialized) + + s.Step(`^instance configuration:$`, instanceConfiguration) + s.Step(`^run instances$`, runInstances) + + s.Step(`^run instances should get a job ID$`, runInstancesShouldGetAJobID) + s.Step(`^run instances will be finished$`, runInstancesWillBeFinished) + + s.Step(`^terminate instances$`, terminateInstances) + s.Step(`^terminate instances should get a job ID$`, terminateInstancesShouldGetAJobID) + s.Step(`^terminate instances will be finished$`, terminateInstancesWillBeFinished) +} + +func initializeInstanceService() error { + instanceService, err = qcService.Instance(tc.Zone) + return err +} + +func theInstanceServiceIsInitialized() error { + if instanceService == nil { + return errors.New("Instance sub service is not initialized") + } + return nil +} + +// -------------------------------------------------------------------------- + +func instanceConfiguration(configuration *gherkin.DataTable) error { + count, err := strconv.Atoi(configuration.Rows[1].Cells[2].Value) + if err != nil { + return err + } + + runInstanceInput = &qc.RunInstancesInput{ + ImageID: qc.String(configuration.Rows[1].Cells[0].Value), + InstanceType: qc.String(configuration.Rows[1].Cells[1].Value), + Count: qc.Int(count), + LoginMode: qc.String(configuration.Rows[1].Cells[3].Value), + LoginPasswd: qc.String(configuration.Rows[1].Cells[4].Value), + } + return nil +} + +func runInstances() error { + runInstanceOutput, err = instanceService.RunInstances(runInstanceInput) + return err +} + +// -------------------------------------------------------------------------- + +func runInstancesShouldGetAJobID() error { + if runInstanceOutput.JobID != nil { + return nil + } + return errors.New("RunInstances don't get a job ID") +} + +func runInstancesWillBeFinished() error { + retries := 0 + for retries < tc.MaxRetries { + describeJobOutput, err := jobService.DescribeJobs( + &qc.DescribeJobsInput{ + Jobs: []*string{runInstanceOutput.JobID}, + }, + ) + if err != nil { + return err + } + if qc.StringValue(describeJobOutput.JobSet[0].Status) == "successful" { + return nil + } + retries++ + time.Sleep(time.Second * time.Duration(tc.RetryWaitTime)) + } + return nil +} + +// -------------------------------------------------------------------------- + +var terminateInstanceOutput *qc.TerminateInstancesOutput + +func terminateInstances() error { + retries := 0 + for retries < tc.MaxRetries { + terminateInstanceOutput, err = instanceService.TerminateInstances( + &qc.TerminateInstancesInput{ + Instances: runInstanceOutput.Instances, + }, + ) + if err != nil { + switch e := err.(type) { + case *qcErrors.QingCloudError: + fmt.Println(e) + if e.RetCode != 1400 { + return e + } + default: + return err + } + } else { + return nil + } + retries++ + time.Sleep(time.Second * time.Duration(tc.RetryWaitTime)) + } + return nil +} + +func terminateInstancesShouldGetAJobID() error { + if terminateInstanceOutput.JobID != nil { + return nil + } + return errors.New("TerminateInstances doesn't get a job ID") +} + +func terminateInstancesWillBeFinished() error { + retries := 0 + for retries < tc.MaxRetries { + describeJobOutput, err := jobService.DescribeJobs( + &qc.DescribeJobsInput{ + Jobs: []*string{terminateInstanceOutput.JobID}, + }, + ) + if err != nil { + return err + } + if qc.StringValue(describeJobOutput.JobSet[0].Status) == "successful" { + return nil + } + retries++ + time.Sleep(time.Second * time.Duration(tc.RetryWaitTime)) + } + return nil +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/job_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/test/job_test.go new file mode 100644 index 000000000..a02cf7e7a --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/job_test.go @@ -0,0 +1,65 @@ +package main + +import ( + "errors" + "fmt" + "github.com/DATA-DOG/godog" + qc "github.com/yunify/qingcloud-sdk-go/service" +) + +func JobFeatureContext(s *godog.Suite) { + s.Step(`^initialize job service$`, initializeJobService) + s.Step(`^the job service is initialized$`, theJobServiceIsInitialized) + + s.Step(`^describe jobs$`, describeJobs) + s.Step(`^describe jobs should get (\d+) job at least$`, describeJobsShouldGetJobAtLeast) + s.Step(`^describe jobs should have the jobs I just created$`, describeJobsShouldHaveTheJobsIJustCreated) +} + +var jobService *qc.JobService +var describeJobOutput *qc.DescribeJobsOutput + +// -------------------------------------------------------------------------- + +func initializeJobService() error { + jobService, err = qcService.Job(tc.Zone) + return err +} + +func theJobServiceIsInitialized() error { + if jobService == nil { + return errors.New("Job sub service is not initialized") + } + return nil +} + +// -------------------------------------------------------------------------- + +func describeJobs() error { + describeJobOutput, err = jobService.DescribeJobs(nil) + return err +} + +func describeJobsShouldGetJobAtLeast(count int) error { + if len(describeJobOutput.JobSet) >= count { + return nil + } + return fmt.Errorf("DescribeJobs doesn't get \"%d\" job(s)", count) +} + +func describeJobsShouldHaveTheJobsIJustCreated() error { + okCount := 0 + for _, job := range describeJobOutput.JobSet { + if qc.StringValue(job.JobID) == qc.StringValue(runInstanceOutput.JobID) { + okCount++ + } + if qc.StringValue(job.JobID) == qc.StringValue(terminateInstanceOutput.JobID) { + okCount++ + } + } + + if okCount == 2 { + return nil + } + return errors.New("DescribeJobs doesn't get the jobs I just created") +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/main_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/test/main_test.go new file mode 100644 index 000000000..662fd4e95 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/main_test.go @@ -0,0 +1,96 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 main + +import ( + "io/ioutil" + "os" + "testing" + + "gopkg.in/yaml.v2" + + "github.com/DATA-DOG/godog" + + "github.com/yunify/qingcloud-sdk-go/config" + qc "github.com/yunify/qingcloud-sdk-go/service" +) + +func TestMain(m *testing.M) { + setUp() + + context := func(s *godog.Suite) { + QingCloudServiceFeatureContext(s) + InstanceFeatureContext(s) + JobFeatureContext(s) + } + options := godog.Options{ + Format: "pretty", + Paths: []string{"./features"}, + Tags: "", + } + status := godog.RunWithOptions("*", context, options) + if st := m.Run(); st > status { + status = st + } + os.Exit(status) +} + +func setUp() { + loadTestConfig() + loadConfig() + initQingCloudService() +} + +var err error +var tc *testConfig +var c *config.Config +var qcService *qc.QingCloudService + +type testConfig struct { + Zone string `json:"zone" yaml:"zone"` + + RetryWaitTime int `json:"retry_wait_time" yaml:"retry_wait_time"` + MaxRetries int `json:"max_retries" yaml:"max_retries"` +} + +func loadTestConfig() { + if tc == nil { + configYAML, err := ioutil.ReadFile("./test_config.yaml") + checkErrorForExit(err) + + tc = &testConfig{} + err = yaml.Unmarshal(configYAML, tc) + checkErrorForExit(err) + } +} + +func loadConfig() { + if c == nil { + c, err = config.NewDefault() + checkErrorForExit(err) + + err = c.LoadConfigFromFilepath("./config.yaml") + checkErrorForExit(err) + } +} + +func initQingCloudService() { + if qcService == nil { + qcService, err = qc.Init(c) + checkErrorForExit(err) + } +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/service_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/test/service_test.go new file mode 100644 index 000000000..6c784e196 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/service_test.go @@ -0,0 +1,73 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 main + +import ( + "errors" + "fmt" + "github.com/DATA-DOG/godog" + qc "github.com/yunify/qingcloud-sdk-go/service" +) + +// QingCloudServiceFeatureContext provides feature context for QingCloudService. +func QingCloudServiceFeatureContext(s *godog.Suite) { + s.Step(`^initialize QingCloud service$`, initializeQingCloudService) + s.Step(`^the QingCloud service is initialized$`, theQingCloudServiceIsInitialized) + + s.Step(`^describe zones$`, describeZones) + s.Step(`^describe zones should get (\d+) zone at least$`, describeZonesShouldGetZoneAtLeast) + s.Step(`^describe zones should have the zone I\'m using$`, describeZonesShouldHaveTheZoneIamUsing) +} + +// -------------------------------------------------------------------------- + +func initializeQingCloudService() error { + return nil +} + +func theQingCloudServiceIsInitialized() error { + if qcService == nil { + return errors.New("QingCloud service is not initialized") + } + return nil +} + +// -------------------------------------------------------------------------- + +var describeZonesOutput *qc.DescribeZonesOutput + +func describeZones() error { + describeZonesOutput, err = qcService.DescribeZones(nil) + return err +} + +func describeZonesShouldGetZoneAtLeast(count int) error { + if len(describeZonesOutput.ZoneSet) >= count { + return nil + } + return fmt.Errorf("DescribeZones only got \"%d\" zone(s)", count) +} + +func describeZonesShouldHaveTheZoneIamUsing() error { + for _, zone := range describeZonesOutput.ZoneSet { + if qc.StringValue(zone.ZoneID) == tc.Zone { + return nil + } + } + + return fmt.Errorf("DescribeZones dosen't have zone \"%s\"", tc.Zone) +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/test_config.yaml.example b/vendor/github.com/yunify/qingcloud-sdk-go/test/test_config.yaml.example new file mode 100644 index 000000000..18cb5c8c0 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/test_config.yaml.example @@ -0,0 +1,6 @@ +# Test configurations + +zone: 'pek3a' + +retry_wait_time: 3 # seconds +max_retries: 60 diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/test/utils.go b/vendor/github.com/yunify/qingcloud-sdk-go/test/utils.go new file mode 100644 index 000000000..d6e05b17c --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/test/utils.go @@ -0,0 +1,33 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 main + +import ( + "fmt" + "os" +) + +func checkErrorForExit(err error, code ...int) { + if err != nil { + exitCode := 1 + if len(code) > 0 { + exitCode = code[0] + } + fmt.Println(err.Error(), exitCode) + os.Exit(exitCode) + } +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/utils/json_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/utils/json_test.go new file mode 100644 index 000000000..28a86b48c --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/utils/json_test.go @@ -0,0 +1,79 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestJSONDecode_Unknown(t *testing.T) { + jsonString := `{ + "key1" : "This is a string.", + "key2" : 10.50, + "key3": [null, {"nestedKey1": "Another string"}] + }` + + anyData, err := JSONDecode([]byte(jsonString)) + assert.Nil(t, err) + data := anyData.(map[string]interface{}) + assert.Equal(t, 10.50, data["key2"]) + + var anotherData interface{} + _, err = JSONDecode([]byte(jsonString), &anotherData) + assert.Nil(t, err) + data = anyData.(map[string]interface{}) + assert.Equal(t, 10.50, data["key2"]) +} + +func TestJSONDecode_Known(t *testing.T) { + type SampleJSON struct { + Name string `json:"name"` + Description string `json:"description"` + } + sampleJSONString := `{"name": "NAME"}` + + sample := SampleJSON{Name: "NaMe", Description: "DeScRiPtIoN"} + anyDataPointer, err := JSONDecode([]byte(sampleJSONString), &sample) + assert.Nil(t, err) + data := anyDataPointer.(*SampleJSON) + assert.Equal(t, "NAME", sample.Name) + assert.Equal(t, "DeScRiPtIoN", sample.Description) + assert.Equal(t, "NAME", (*data).Name) + assert.Equal(t, "DeScRiPtIoN", (*data).Description) +} + +func TestJSONEncode(t *testing.T) { + type SampleJSON struct { + Name string `json:"name"` + Description string `json:"description"` + } + sample := SampleJSON{Name: "NaMe", Description: "DeScRiPtIoN"} + + jsonBytes, err := JSONEncode(sample, true) + assert.Nil(t, err) + assert.Equal(t, `{"name":"NaMe","description":"DeScRiPtIoN"}`, string(jsonBytes)) +} + +func TestJSONFormatToReadable(t *testing.T) { + sampleJSONString := `{"name": "NAME"}` + + jsonBytes, err := JSONFormatToReadable([]byte(sampleJSONString)) + assert.Nil(t, err) + assert.Equal(t, "{\n \"name\": \"NAME\"\n}", string(jsonBytes)) +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/utils/time_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/utils/time_test.go new file mode 100644 index 000000000..0985bb1ea --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/utils/time_test.go @@ -0,0 +1,57 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 utils + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestTimeToString(t *testing.T) { + tz, err := time.LoadLocation("Asia/Shanghai") + assert.Nil(t, err) + + someTime := time.Date(2016, 9, 1, 15, 30, 0, 0, tz) + assert.Equal(t, "Thu, 01 Sep 2016 07:30:00 GMT", TimeToString(someTime, "RFC 822")) + assert.Equal(t, "2016-09-01T07:30:00Z", TimeToString(someTime, "ISO 8601")) +} + +func TestStringToTime(t *testing.T) { + tz, err := time.LoadLocation("Asia/Shanghai") + assert.Nil(t, err) + someTime := time.Date(2016, 9, 1, 15, 30, 0, 0, tz) + + parsedTime, err := StringToTime("Thu, 01 Sep 2016 07:30:00 GMT", "RFC 822") + assert.Nil(t, err) + assert.Equal(t, someTime.UTC(), parsedTime) + + parsedTime, err = StringToTime("2016-09-01T07:30:00Z", "ISO 8601") + assert.Nil(t, err) + assert.Equal(t, someTime.UTC(), parsedTime) + + parsedTime, err = StringToTime("2016-09-01T07:30:00.000Z", "ISO 8601") + assert.Nil(t, err) + assert.Equal(t, someTime.UTC(), parsedTime) +} + +func TestStringToUnixString(t *testing.T) { + assert.Equal(t, 1472715000, StringToUnixInt("Thu, 01 Sep 2016 07:30:00 GMT", "RFC 822")) + assert.Equal(t, 1472715000, StringToUnixInt("2016-09-01T07:30:00Z", "ISO 8601")) + assert.Equal(t, 1472715000, StringToUnixInt("2016-09-01T07:30:00.000Z", "ISO 8601")) +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/utils/wait_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/utils/wait_test.go new file mode 100644 index 000000000..03f0e0a49 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/utils/wait_test.go @@ -0,0 +1,49 @@ +package utils + +import ( + "errors" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestWaitForSpecificOrError(t *testing.T) { + + waitInterval := 100 * time.Millisecond + timeout := 10*waitInterval + waitInterval/2 + times := 0 + err := WaitForSpecificOrError(func() (bool, error) { + times++ + println("times", times) + if times == 3 { + return true, nil + } + return false, nil + }, timeout, waitInterval) + assert.NoError(t, err) + assert.Equal(t, 3, times) + + times = 0 + err = WaitForSpecificOrError(func() (bool, error) { + times++ + println("times", times) + if times == 3 { + return false, errors.New("error") + } + return false, nil + }, timeout, waitInterval) + assert.Error(t, err) + assert.Equal(t, 3, times) + + times = 0 + err = WaitForSpecificOrError(func() (bool, error) { + times++ + println("times", times) + return false, nil + }, timeout, waitInterval) + assert.Error(t, err) + tErr, ok := err.(*TimeoutError) + assert.True(t, ok) + assert.Equal(t, timeout, tErr.timeout) + assert.Equal(t, 10, times) +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/utils/yaml_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/utils/yaml_test.go new file mode 100644 index 000000000..aa5b9f3d3 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/utils/yaml_test.go @@ -0,0 +1,75 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestYAMLDecode_Unknown(t *testing.T) { + yamlString := ` +key1: "This is a string." # Single Line Comment +key2: 10.50 +key3: + - null + - nestedKey1: Anothor string +` + + anyData, err := YAMLDecode([]byte(yamlString)) + assert.Nil(t, err) + data := anyData.(map[interface{}]interface{}) + assert.Equal(t, 10.50, data["key2"]) +} + +func TestYAMLDecode_Known(t *testing.T) { + type SampleYAML struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + } + sampleYAMLString := `name: "NAME"` + + sample := SampleYAML{Name: "NaMe", Description: "DeScRiPtIoN"} + anyDataPointer, err := YAMLDecode([]byte(sampleYAMLString), &sample) + assert.Nil(t, err) + data := anyDataPointer.(*SampleYAML) + assert.Equal(t, "NAME", sample.Name) + assert.Equal(t, "DeScRiPtIoN", sample.Description) + assert.Equal(t, "NAME", (*data).Name) + assert.Equal(t, "DeScRiPtIoN", (*data).Description) +} + +func TestYAMLDecode_Empty(t *testing.T) { + yamlString := "" + + anyData, err := YAMLDecode([]byte(yamlString)) + assert.Nil(t, err) + assert.Nil(t, anyData) +} + +func TestYAMLEncode(t *testing.T) { + type SampleYAML struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + } + sample := SampleYAML{Name: "NaMe", Description: "DeScRiPtIoN"} + + yamlBytes, err := YAMLEncode(sample) + assert.Nil(t, err) + assert.Equal(t, "name: NaMe\ndescription: DeScRiPtIoN\n", string(yamlBytes)) +} diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/version.go b/vendor/github.com/yunify/qingcloud-sdk-go/version.go new file mode 100644 index 000000000..b18a22720 --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/version.go @@ -0,0 +1,23 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 sdk is the official QingCloud SDK for the Go programming language. +// +// https://github.com/yunify/qingcloud-sdk-go +package sdk + +// Version number. +const Version = "2.0.0-alpha.10" diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/version_test.go b/vendor/github.com/yunify/qingcloud-sdk-go/version_test.go new file mode 100644 index 000000000..fedc2bd5a --- /dev/null +++ b/vendor/github.com/yunify/qingcloud-sdk-go/version_test.go @@ -0,0 +1,28 @@ +// +------------------------------------------------------------------------- +// | Copyright (C) 2016 Yunify, Inc. +// +------------------------------------------------------------------------- +// | Licensed under the Apache License, Version 2.0 (the "License"); +// | you may not use this work except in compliance with the License. +// | You may obtain a copy of the License in the LICENSE file, or 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 sdk + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestVersion(t *testing.T) { + assert.Equal(t, "string", reflect.ValueOf(Version).Type().String()) +} From c7ac9a0a02e53eebea9fb27c8a4c5ea4f5e25de9 Mon Sep 17 00:00:00 2001 From: Zhanwei Wang Date: Wed, 14 Jul 2021 16:30:05 +0800 Subject: [PATCH 02/10] Remove instance and volume type validation --- .../qingcloud-sdk-go/service/instance.go | 20 ------------------- .../yunify/qingcloud-sdk-go/service/volume.go | 20 ------------------- 2 files changed, 40 deletions(-) diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/service/instance.go b/vendor/github.com/yunify/qingcloud-sdk-go/service/instance.go index 458056d10..5cfa46b9f 100644 --- a/vendor/github.com/yunify/qingcloud-sdk-go/service/instance.go +++ b/vendor/github.com/yunify/qingcloud-sdk-go/service/instance.go @@ -670,26 +670,6 @@ func (v *RunInstancesInput) Validate() error { } } - if v.InstanceClass != nil { - instanceClassValidValues := []string{"0", "1", "2", "3", "4", "5", "6", "100", "101", "200", "201", "300", "301"} - instanceClassParameterValue := fmt.Sprint(*v.InstanceClass) - - instanceClassIsValid := false - for _, value := range instanceClassValidValues { - if value == instanceClassParameterValue { - instanceClassIsValid = true - } - } - - if !instanceClassIsValid { - return errors.ParameterValueNotAllowedError{ - ParameterName: "InstanceClass", - ParameterValue: instanceClassParameterValue, - AllowedValues: instanceClassValidValues, - } - } - } - if v.LoginMode == nil { return errors.ParameterRequiredError{ ParameterName: "LoginMode", diff --git a/vendor/github.com/yunify/qingcloud-sdk-go/service/volume.go b/vendor/github.com/yunify/qingcloud-sdk-go/service/volume.go index 9ca0f3d67..93715606e 100644 --- a/vendor/github.com/yunify/qingcloud-sdk-go/service/volume.go +++ b/vendor/github.com/yunify/qingcloud-sdk-go/service/volume.go @@ -149,26 +149,6 @@ func (v *CloneVolumesInput) Validate() error { } } - if v.VolumeType != nil { - volumeTypeValidValues := []string{"0", "1", "2", "3", "4", "5", "6", "10", "100", "200"} - volumeTypeParameterValue := fmt.Sprint(*v.VolumeType) - - volumeTypeIsValid := false - for _, value := range volumeTypeValidValues { - if value == volumeTypeParameterValue { - volumeTypeIsValid = true - } - } - - if !volumeTypeIsValid { - return errors.ParameterValueNotAllowedError{ - ParameterName: "VolumeType", - ParameterValue: volumeTypeParameterValue, - AllowedValues: volumeTypeValidValues, - } - } - } - return nil } From 41b36568885108c8abd0de9767919321bc468f50 Mon Sep 17 00:00:00 2001 From: Zhanwei Wang Date: Wed, 14 Jul 2021 16:32:37 +0800 Subject: [PATCH 03/10] Remove instance and volume type validation --- qingcloud/resource_qingcloud_instance.go | 1 - qingcloud/resource_qingcloud_volume.go | 1 - 2 files changed, 2 deletions(-) diff --git a/qingcloud/resource_qingcloud_instance.go b/qingcloud/resource_qingcloud_instance.go index 251b0f3c8..8370d83b3 100644 --- a/qingcloud/resource_qingcloud_instance.go +++ b/qingcloud/resource_qingcloud_instance.go @@ -69,7 +69,6 @@ func resourceQingcloudInstance() *schema.Resource { Type: schema.TypeInt, ForceNew: true, Optional: true, - ValidateFunc: withinArrayInt(0, 1, 2, 3, 4, 5, 6, 100, 101, 200, 201, 300, 301), Default: 0, }, resourceInstanceManagedVxnetID: { diff --git a/qingcloud/resource_qingcloud_volume.go b/qingcloud/resource_qingcloud_volume.go index 9c15d73ff..3c09df74b 100644 --- a/qingcloud/resource_qingcloud_volume.go +++ b/qingcloud/resource_qingcloud_volume.go @@ -41,7 +41,6 @@ func resourceQingcloudVolume() *schema.Resource { Optional: true, Default: 0, ForceNew: true, - ValidateFunc: withinArrayInt(0, 1, 2, 3), }, resourceTagIds: tagIdsSchema(), resourceTagNames: tagNamesSchema(), From be9d9ec0fdf9ca23bd884d221112cbcd8b415a38 Mon Sep 17 00:00:00 2001 From: Zhanwei Wang Date: Wed, 14 Jul 2021 16:34:21 +0800 Subject: [PATCH 04/10] Wait for private ip ready --- qingcloud/resource_qingcloud_instance.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qingcloud/resource_qingcloud_instance.go b/qingcloud/resource_qingcloud_instance.go index 8370d83b3..d159ef108 100644 --- a/qingcloud/resource_qingcloud_instance.go +++ b/qingcloud/resource_qingcloud_instance.go @@ -197,6 +197,10 @@ func resourceQingcloudInstanceRead(d *schema.ResourceData, meta interface{}) err var err error simpleRetry(func() error { output, err = clt.DescribeInstances(input) + if err == nil && qc.StringValue(output.InstanceSet[0].VxNets[0].PrivateIP) == "" { + return fmt.Errorf("no private ip found for instance %s", + *output.InstanceSet[0].InstanceID) + } return isServerBusy(err) }) if err != nil { From 8ce00939142057d4acf9ee0d652c98b4408b4121 Mon Sep 17 00:00:00 2001 From: Zhanwei Wang Date: Wed, 14 Jul 2021 16:44:00 +0800 Subject: [PATCH 05/10] Update github action for release --- .github/workflows/release.yml | 42 ++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6050c4f99..4ccef2a0a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,36 +1,46 @@ -name: goreleaser - +# This GitHub action can publish assets for release when a tag is created. +# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). +# +# This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your +# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` +# secret. If you would rather own your own GPG handling, please fork this action +# or use an alternative one for key handling. +# +# You will need to pass the `--batch` flag to `gpg` in your signing step +# in `goreleaser` to indicate this is being used in a non-interactive mode. +# +name: release on: push: tags: - 'v*' - jobs: goreleaser: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Go + - name: Unshallow + run: git fetch --prune --unshallow + - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.14 - - - name: Import GPG key + go-version: 1.16 + - name: Describe plugin + id: plugin_describe + run: echo "::set-output name=api_version::$(go run . describe | jq -r '.api_version')" + - name: Import GPG key id: import_gpg - uses: crazy-max/ghaction-import-gpg@v2 + uses: hashicorp/ghaction-import-gpg@v2.1.0 env: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - - - name: Run GoReleaser + PASSPHRASE: ${{ secrets.PASSPHRASE }} + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: version: latest args: release --rm-dist env: - GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + API_VERSION: ${{ steps.plugin_describe.outputs.api_version }} \ No newline at end of file From 5dc1ab65bf79b4fee8b7660c9ba6e143b5e0373d Mon Sep 17 00:00:00 2001 From: Zhanwei Wang Date: Wed, 14 Jul 2021 18:21:49 +0800 Subject: [PATCH 06/10] Wait for private ip ready --- qingcloud/resource_qingcloud_instance.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qingcloud/resource_qingcloud_instance.go b/qingcloud/resource_qingcloud_instance.go index d159ef108..61d6157b6 100644 --- a/qingcloud/resource_qingcloud_instance.go +++ b/qingcloud/resource_qingcloud_instance.go @@ -197,7 +197,8 @@ func resourceQingcloudInstanceRead(d *schema.ResourceData, meta interface{}) err var err error simpleRetry(func() error { output, err = clt.DescribeInstances(input) - if err == nil && qc.StringValue(output.InstanceSet[0].VxNets[0].PrivateIP) == "" { + if err == nil && (len(output.InstanceSet[0].VxNets) == 0 || + qc.StringValue(output.InstanceSet[0].VxNets[0].PrivateIP) == "") { return fmt.Errorf("no private ip found for instance %s", *output.InstanceSet[0].InstanceID) } From 92056bc92bc885aaa1a0bef32bebf0e4b606aaee Mon Sep 17 00:00:00 2001 From: wangjiaxi90 <37741159+wangjiaxi90@users.noreply.github.com> Date: Tue, 7 Dec 2021 17:30:39 +0800 Subject: [PATCH 07/10] Fix Bug (#1) * Remove the validateFunc of instance_class * Add a jugment to get private ip * Fix Bug When the instance is deleted. Delete func should not return error and Read/Update func should recreate the instance. * Single function responsibility Remove create instance in read function --- qingcloud/resource_qingcloud_instance.go | 25 +++++++++++++++---- qingcloud/resource_qingcloud_instance_help.go | 24 ++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/qingcloud/resource_qingcloud_instance.go b/qingcloud/resource_qingcloud_instance.go index 61d6157b6..022412378 100644 --- a/qingcloud/resource_qingcloud_instance.go +++ b/qingcloud/resource_qingcloud_instance.go @@ -2,7 +2,6 @@ package qingcloud import ( "fmt" - "github.com/hashicorp/terraform/helper/schema" qc "github.com/yunify/qingcloud-sdk-go/service" ) @@ -66,10 +65,10 @@ func resourceQingcloudInstance() *schema.Resource { Default: 1024, }, resourceInstanceClass: { - Type: schema.TypeInt, - ForceNew: true, - Optional: true, - Default: 0, + Type: schema.TypeInt, + ForceNew: true, + Optional: true, + Default: 0, }, resourceInstanceManagedVxnetID: { Type: schema.TypeString, @@ -257,6 +256,15 @@ func resourceQingcloudInstanceRead(d *schema.ResourceData, meta interface{}) err } func resourceQingcloudInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + if !d.IsNewResource() { + isDelete, err := isInstanceDeletedWrapper(d.Id(), meta.(*QingCloudClient).instance) + if err != nil { + return err + } + if isDelete { + return resourceQingcloudInstanceCreate(d, meta) + } + } d.Partial(true) if err := waitInstanceLease(d, meta); err != nil { return err @@ -310,6 +318,13 @@ func resourceQingcloudInstanceDelete(d *schema.ResourceData, meta interface{}) e return err } clt := meta.(*QingCloudClient).instance + if isDelete, err := isInstanceDeletedWrapper(d.Id(), clt); err != nil || isDelete { + if err != nil { + return err + } + d.SetId("") + return nil + } input := new(qc.TerminateInstancesInput) input.Instances = []*string{qc.String(d.Id())} var err error diff --git a/qingcloud/resource_qingcloud_instance_help.go b/qingcloud/resource_qingcloud_instance_help.go index 2c299e433..2e42c00d5 100644 --- a/qingcloud/resource_qingcloud_instance_help.go +++ b/qingcloud/resource_qingcloud_instance_help.go @@ -440,3 +440,27 @@ func isInstanceDeleted(instanceSet []*qc.Instance) bool { } return false } + +func describeInstances(id string, clt *qc.InstanceService) (*qc.DescribeInstancesOutput, error) { + inputDescribe := new(qc.DescribeInstancesInput) + inputDescribe.Instances = []*string{qc.String(id)} + inputDescribe.Verbose = qc.Int(1) + var output *qc.DescribeInstancesOutput + var errDescribe error + simpleRetry(func() error { + output, errDescribe = clt.DescribeInstances(inputDescribe) + return isServerBusy(errDescribe) + }) + if errDescribe != nil { + return nil, errDescribe + } + return output, nil +} + +func isInstanceDeletedWrapper(id string, clt *qc.InstanceService) (bool, error) { + output, err := describeInstances(id, clt) + if err != nil { + return false, err + } + return isInstanceDeleted(output.InstanceSet), nil +} From 125a3ace160417167e4a57fd085c3f735080f2ae Mon Sep 17 00:00:00 2001 From: Zhanwei Wang Date: Thu, 17 Mar 2022 10:48:57 +0800 Subject: [PATCH 08/10] Do not create instance when update --- qingcloud/resource_qingcloud_instance.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/qingcloud/resource_qingcloud_instance.go b/qingcloud/resource_qingcloud_instance.go index 022412378..b24e96a17 100644 --- a/qingcloud/resource_qingcloud_instance.go +++ b/qingcloud/resource_qingcloud_instance.go @@ -256,15 +256,6 @@ func resourceQingcloudInstanceRead(d *schema.ResourceData, meta interface{}) err } func resourceQingcloudInstanceUpdate(d *schema.ResourceData, meta interface{}) error { - if !d.IsNewResource() { - isDelete, err := isInstanceDeletedWrapper(d.Id(), meta.(*QingCloudClient).instance) - if err != nil { - return err - } - if isDelete { - return resourceQingcloudInstanceCreate(d, meta) - } - } d.Partial(true) if err := waitInstanceLease(d, meta); err != nil { return err From d4f9bd25d65a9d7df766e75a461c7266ea2d0cff Mon Sep 17 00:00:00 2001 From: xuxiangnan Date: Mon, 13 Feb 2023 18:20:58 +0800 Subject: [PATCH 09/10] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=99=9A=E6=9C=BA?= =?UTF-8?q?=E5=92=8C=E7=A3=81=E7=9B=98=E7=BB=91=E5=AE=9Aresource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qingcloud/common.go | 15 +++ qingcloud/constants.go | 3 + qingcloud/errors.go | 53 ++++++++ qingcloud/provider.go | 29 ++--- ...ce_qingcloud_instance_volume_attachment.go | 118 ++++++++++++++++++ ...ngcloud_instance_volume_attachment_test.go | 34 +++++ 6 files changed, 238 insertions(+), 14 deletions(-) create mode 100644 qingcloud/common.go create mode 100644 qingcloud/resource_qingcloud_instance_volume_attachment.go create mode 100644 qingcloud/resource_qingcloud_instance_volume_attachment_test.go diff --git a/qingcloud/common.go b/qingcloud/common.go new file mode 100644 index 000000000..252bf7da7 --- /dev/null +++ b/qingcloud/common.go @@ -0,0 +1,15 @@ +package qingcloud + +import ( + "fmt" + "strings" +) + +func ParseResourceId(id string, length int) (parts []string, err error) { + parts = strings.Split(id, ":") + + if len(parts) != length { + err = WrapError(fmt.Errorf("Invalid Resource Id %s. Expected parts' length %d, got %d", id, length, len(parts))) + } + return parts, err +} diff --git a/qingcloud/constants.go b/qingcloud/constants.go index 94bcc3bf1..244a408f0 100644 --- a/qingcloud/constants.go +++ b/qingcloud/constants.go @@ -1,5 +1,7 @@ package qingcloud +type Status string + const ( qingcloudResourceTypeInstance = "instance" qingcloudResourceTypeVolume = "volume" @@ -11,6 +13,7 @@ const ( qingcloudResourceTypeLoadBalancer = "loadbalancer" StatusActive = "active" + InUse = Status("in-use") DEFAULT_ZONE = "pek3a" DEFAULT_ENDPOINT = "https://api.qingcloud.com:443/iaas" diff --git a/qingcloud/errors.go b/qingcloud/errors.go index b5f541530..d1e968626 100644 --- a/qingcloud/errors.go +++ b/qingcloud/errors.go @@ -1,3 +1,56 @@ package qingcloud +import ( + "fmt" + "log" + "runtime" + "strings" +) + const SERVERBUSY = 5100 + +func WrapError(cause error) error { + if cause == nil { + return nil + } + _, filepath, line, ok := runtime.Caller(1) + if !ok { + log.Printf("\u001B[31m[ERROR]\u001B[0m runtime.Caller error in WrapError.") + return WrapComplexError(cause, nil, "", -1) + } + parts := strings.Split(filepath, "/") + if len(parts) > 3 { + filepath = strings.Join(parts[len(parts)-3:], "/") + } + return WrapComplexError(cause, nil, filepath, line) +} + +func WrapComplexError(cause, err error, filepath string, fileline int) error { + return &ComplexError{ + Cause: cause, + Err: err, + Path: filepath, + Line: fileline, + } +} + +type ComplexError struct { + Cause error + Err error + Path string + Line int +} + +func (e ComplexError) Error() string { + if e.Cause == nil { + e.Cause = Error("") + } + if e.Err == nil { + return fmt.Sprintf("\u001B[31m[ERROR]\u001B[0m %s:%d:\n%s", e.Path, e.Line, e.Cause.Error()) + } + return fmt.Sprintf("\u001B[31m[ERROR]\u001B[0m %s:%d: %s:\n%s", e.Path, e.Line, e.Err.Error(), e.Cause.Error()) +} + +func Error(msg string, args ...interface{}) error { + return fmt.Errorf(msg, args...) +} diff --git a/qingcloud/provider.go b/qingcloud/provider.go index 313df095d..313c5576a 100644 --- a/qingcloud/provider.go +++ b/qingcloud/provider.go @@ -37,20 +37,21 @@ func Provider() terraform.ResourceProvider { "qingcloud_vpn_cert": dataSourceQingcloudVpnCert(), }, ResourcesMap: map[string]*schema.Resource{ - "qingcloud_eip": resourceQingcloudEip(), - "qingcloud_keypair": resourceQingcloudKeypair(), - "qingcloud_security_group": resourceQingcloudSecurityGroup(), - "qingcloud_security_group_rule": resourceQingcloudSecurityGroupRule(), - "qingcloud_vxnet": resourceQingcloudVxnet(), - "qingcloud_vpc": resourceQingcloudVpc(), - "qingcloud_instance": resourceQingcloudInstance(), - "qingcloud_volume": resourceQingcloudVolume(), - "qingcloud_tag": resourceQingcloudTag(), - "qingcloud_vpc_static": resourceQingcloudVpcStatic(), - "qingcloud_loadbalancer": resourceQingcloudLoadBalancer(), - "qingcloud_loadbalancer_listener": resourceQingcloudLoadBalancerListener(), - "qingcloud_loadbalancer_backend": resourceQingcloudLoadBalancerBackend(), - "qingcloud_server_certificate": resourceQingcloudServerCertificate(), + "qingcloud_eip": resourceQingcloudEip(), + "qingcloud_keypair": resourceQingcloudKeypair(), + "qingcloud_security_group": resourceQingcloudSecurityGroup(), + "qingcloud_security_group_rule": resourceQingcloudSecurityGroupRule(), + "qingcloud_vxnet": resourceQingcloudVxnet(), + "qingcloud_vpc": resourceQingcloudVpc(), + "qingcloud_instance": resourceQingcloudInstance(), + "qingcloud_instance_volume_attachment": resourceQingcloudInstanceVolumeAttachment(), + "qingcloud_volume": resourceQingcloudVolume(), + "qingcloud_tag": resourceQingcloudTag(), + "qingcloud_vpc_static": resourceQingcloudVpcStatic(), + "qingcloud_loadbalancer": resourceQingcloudLoadBalancer(), + "qingcloud_loadbalancer_listener": resourceQingcloudLoadBalancerListener(), + "qingcloud_loadbalancer_backend": resourceQingcloudLoadBalancerBackend(), + "qingcloud_server_certificate": resourceQingcloudServerCertificate(), }, ConfigureFunc: providerConfigure, } diff --git a/qingcloud/resource_qingcloud_instance_volume_attachment.go b/qingcloud/resource_qingcloud_instance_volume_attachment.go new file mode 100644 index 000000000..3b961ec6c --- /dev/null +++ b/qingcloud/resource_qingcloud_instance_volume_attachment.go @@ -0,0 +1,118 @@ +package qingcloud + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + qc "github.com/yunify/qingcloud-sdk-go/service" + "log" + "time" +) + +const ( + resourceVolumes = "volume_id" + resourceInstanceId = "instance_id" +) + +func resourceQingcloudInstanceVolumeAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceQingcloudInstanceVolumeAttachmentCreate, + Read: resourceQingcloudInstanceVolumeAttachmentRead, + Update: resourceQingcloudInstanceVolumeAttachmentUpdate, + Delete: resourceQingcloudInstanceVolumeAttachmentDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(5 * time.Minute), + Delete: schema.DefaultTimeout(5 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + resourceVolumes: { + Type: schema.TypeString, + Required: true, + Description: "绑定的磁盘id", + }, + resourceInstanceId: { + Type: schema.TypeString, + Required: true, + Description: "绑定的机器id", + }, + }, + } +} + +func resourceQingcloudInstanceVolumeAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + clt := meta.(*QingCloudClient).volume + input := new(qc.AttachVolumesInput) + volumeId := []string{d.Get(resourceVolumes).(string)} + input.Volumes = qc.StringSlice(volumeId) + input.Instance = qc.String(d.Get(resourceInstanceId).(string)) + var err error + simpleRetry(func() error { + _, err = clt.AttachVolumes(input) + return WrapError(isServerBusy(err)) + }) + if err != nil { + return WrapError(err) + } + d.SetId(fmt.Sprint(volumeId[0], ":", *input.Instance)) + if _, err := VolumeTransitionStateRefresh(clt, volumeId[0]); err != nil { + return WrapError(err) + } + return resourceQingcloudInstanceVolumeAttachmentRead(d, meta) +} + +func resourceQingcloudInstanceVolumeAttachmentRead(d *schema.ResourceData, meta interface{}) error { + clt := meta.(*QingCloudClient).volume + parts, err := ParseResourceId(d.Id(), 2) + if err != nil { + return WrapError(err) + } + input := new(qc.DescribeVolumesInput) + input.Volumes = []*string{qc.String(parts[0])} + var output *qc.DescribeVolumesOutput + simpleRetry(func() error { + output, err = clt.DescribeVolumes(input) + return WrapError(isServerBusy(err)) + }) + if err != nil { + return WrapError(err) + } + if len(output.VolumeSet) == 0 { + d.SetId("") + return nil + } + volume := output.VolumeSet[0] + if *volume.Status != string(InUse) && *volume.Instances[0].InstanceID != parts[1] { + return WrapError(fmt.Errorf("the specified %s %s is not found", "VolumeAttach", d.Id())) + } + d.Set("volume_id", parts[0]) + d.Set("instance_id", parts[1]) + return nil +} + +func resourceQingcloudInstanceVolumeAttachmentUpdate(d *schema.ResourceData, meta interface{}) error { + log.Println(fmt.Sprintf("[WARNING] The resouce has not update operation.")) + return resourceQingcloudInstanceVolumeAttachmentRead(d, meta) +} + +func resourceQingcloudInstanceVolumeAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + clt := meta.(*QingCloudClient).volume + input := new(qc.DetachVolumesInput) + volumeId := []string{d.Get(resourceVolumes).(string)} + input.Volumes = qc.StringSlice(volumeId) + input.Instance = qc.String(d.Get(resourceInstanceId).(string)) + var err error + simpleRetry(func() error { + _, err = clt.DetachVolumes(input) + return WrapError(isServerBusy(err)) + }) + if err != nil { + return WrapError(err) + } + d.SetId("") + if _, err := VolumeTransitionStateRefresh(clt, volumeId[0]); err != nil { + return WrapError(err) + } + return nil +} diff --git a/qingcloud/resource_qingcloud_instance_volume_attachment_test.go b/qingcloud/resource_qingcloud_instance_volume_attachment_test.go new file mode 100644 index 000000000..427d9fae0 --- /dev/null +++ b/qingcloud/resource_qingcloud_instance_volume_attachment_test.go @@ -0,0 +1,34 @@ +package qingcloud + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccQingcloudInstanceVolumeAttachment_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "qingcloud_instance_volume_attachment.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testInstanceVolumeAttachmentConfig), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("qingcloud_instance_volume_attachment.foo", "volume_id", "vos-60xcbw8r"), + resource.TestCheckResourceAttr("qingcloud_instance_volume_attachment.foo", "instance_id", "i-o1gm1smr"), + ), + }, + }, + }) +} + +const testInstanceVolumeAttachmentConfig = ` +resource "qingcloud_instance_volume_attachment" "foo"{ + volume_id = "vos-60xcbw8r" + instance_id = "i-o1gm1smr" +} +` From be8ee90b792c3b2854aed24984abb22f151cae55 Mon Sep 17 00:00:00 2001 From: xuxiangnan Date: Wed, 15 Feb 2023 10:24:41 +0800 Subject: [PATCH 10/10] fix volume attach and detach --- ...ce_qingcloud_instance_volume_attachment.go | 8 +-- ...ngcloud_instance_volume_attachment_test.go | 8 +-- qingcloud/transition_status.go | 62 +++++++++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/qingcloud/resource_qingcloud_instance_volume_attachment.go b/qingcloud/resource_qingcloud_instance_volume_attachment.go index 3b961ec6c..a29b99273 100644 --- a/qingcloud/resource_qingcloud_instance_volume_attachment.go +++ b/qingcloud/resource_qingcloud_instance_volume_attachment.go @@ -55,10 +55,10 @@ func resourceQingcloudInstanceVolumeAttachmentCreate(d *schema.ResourceData, met if err != nil { return WrapError(err) } - d.SetId(fmt.Sprint(volumeId[0], ":", *input.Instance)) - if _, err := VolumeTransitionStateRefresh(clt, volumeId[0]); err != nil { + if _, err := VolumeAttachTransitionStateRefresh(clt, volumeId[0]); err != nil { return WrapError(err) } + d.SetId(volumeId[0] + ":" + *input.Instance) return resourceQingcloudInstanceVolumeAttachmentRead(d, meta) } @@ -110,9 +110,9 @@ func resourceQingcloudInstanceVolumeAttachmentDelete(d *schema.ResourceData, met if err != nil { return WrapError(err) } - d.SetId("") - if _, err := VolumeTransitionStateRefresh(clt, volumeId[0]); err != nil { + if _, err := VolumeDetachTransitionStateRefresh(clt, volumeId[0]); err != nil { return WrapError(err) } + d.SetId("") return nil } diff --git a/qingcloud/resource_qingcloud_instance_volume_attachment_test.go b/qingcloud/resource_qingcloud_instance_volume_attachment_test.go index 427d9fae0..9096aae16 100644 --- a/qingcloud/resource_qingcloud_instance_volume_attachment_test.go +++ b/qingcloud/resource_qingcloud_instance_volume_attachment_test.go @@ -18,8 +18,8 @@ func TestAccQingcloudInstanceVolumeAttachment_basic(t *testing.T) { { Config: fmt.Sprintf(testInstanceVolumeAttachmentConfig), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("qingcloud_instance_volume_attachment.foo", "volume_id", "vos-60xcbw8r"), - resource.TestCheckResourceAttr("qingcloud_instance_volume_attachment.foo", "instance_id", "i-o1gm1smr"), + resource.TestCheckResourceAttr("qingcloud_instance_volume_attachment.foo", "volume_id", "vos-6byu0d7a"), + resource.TestCheckResourceAttr("qingcloud_instance_volume_attachment.foo", "instance_id", "i-tyotg828"), ), }, }, @@ -28,7 +28,7 @@ func TestAccQingcloudInstanceVolumeAttachment_basic(t *testing.T) { const testInstanceVolumeAttachmentConfig = ` resource "qingcloud_instance_volume_attachment" "foo"{ - volume_id = "vos-60xcbw8r" - instance_id = "i-o1gm1smr" + volume_id = "vos-6byu0d7a" + instance_id = "i-tyotg828" } ` diff --git a/qingcloud/transition_status.go b/qingcloud/transition_status.go index a5d5bed71..253254613 100644 --- a/qingcloud/transition_status.go +++ b/qingcloud/transition_status.go @@ -70,6 +70,68 @@ func VolumeTransitionStateRefresh(clt *qc.VolumeService, id string) (interface{} return stateConf.WaitForState() } +func VolumeAttachTransitionStateRefresh(clt *qc.VolumeService, id string) (interface{}, error) { + refreshFunc := func() (interface{}, string, error) { + input := new(qc.DescribeVolumesInput) + input.Volumes = []*string{qc.String(id)} + var output *qc.DescribeVolumesOutput + var err error + simpleRetry(func() error { + output, err = clt.DescribeVolumes(input) + return isServerBusy(err) + }) + if err != nil { + return nil, "", err + } + if len(output.VolumeSet) != 1 { + return output, "attaching", nil + } + volume := output.VolumeSet[0] + return volume, qc.StringValue(volume.Status), nil + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"pending", "available", "suspended", "deleted", "ceased"}, + Target: []string{"in-use"}, + Refresh: refreshFunc, + Timeout: waitJobTimeOutDefault * time.Second, + Delay: waitJobIntervalDefault * time.Second, + MinTimeout: waitJobIntervalDefault * time.Second, + } + return stateConf.WaitForState() +} + +func VolumeDetachTransitionStateRefresh(clt *qc.VolumeService, id string) (interface{}, error) { + refreshFunc := func() (interface{}, string, error) { + input := new(qc.DescribeVolumesInput) + input.Volumes = []*string{qc.String(id)} + var output *qc.DescribeVolumesOutput + var err error + simpleRetry(func() error { + output, err = clt.DescribeVolumes(input) + return isServerBusy(err) + }) + if err != nil { + return nil, "", err + } + if len(output.VolumeSet) != 1 { + return output, "detaching", nil + } + volume := output.VolumeSet[0] + return volume, qc.StringValue(volume.Status), nil + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"pending", "in-use", "suspended", "deleted", "ceased"}, + Target: []string{"available"}, + Refresh: refreshFunc, + Timeout: waitJobTimeOutDefault * time.Second, + Delay: waitJobIntervalDefault * time.Second, + MinTimeout: waitJobIntervalDefault * time.Second, + } + return stateConf.WaitForState() +} + func VolumeDeleteTransitionStateRefresh(clt *qc.VolumeService, id string) (interface{}, error) { refreshFunc := func() (interface{}, string, error) { input := new(qc.DescribeVolumesInput)