mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 13:29:11 +01:00
Running glide update -v to pulldown all the required depedencies
This commit is contained in:
31
vendor/github.com/Azure/go-autorest/.gitignore
generated
vendored
Normal file
31
vendor/github.com/Azure/go-autorest/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# The standard Go .gitignore file follows. (Sourced from: github.com/github/gitignore/master/Go.gitignore)
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
.DS_Store
|
||||
.idea/
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# go-autorest specific
|
||||
vendor/
|
||||
autorest/azure/example/example
|
||||
25
vendor/github.com/Azure/go-autorest/.travis.yml
generated
vendored
Normal file
25
vendor/github.com/Azure/go-autorest/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.9
|
||||
- 1.8
|
||||
- 1.7
|
||||
- 1.6
|
||||
|
||||
install:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
- go get -u github.com/Masterminds/glide
|
||||
- go get -u github.com/stretchr/testify
|
||||
- go get -u github.com/GoASTScanner/gas
|
||||
- glide install
|
||||
|
||||
script:
|
||||
- grep -L -r --include *.go --exclude-dir vendor -P "Copyright (\d{4}|\(c\)) Microsoft" ./ | tee /dev/stderr | test -z "$(< /dev/stdin)"
|
||||
- test -z "$(gofmt -s -l -w ./autorest/. | tee /dev/stderr)"
|
||||
- test -z "$(golint ./autorest/... | tee /dev/stderr)"
|
||||
- go vet ./autorest/...
|
||||
- test -z "$(gas ./autorest/... | tee /dev/stderr | grep Error)"
|
||||
- go build -v ./autorest/...
|
||||
- go test -v ./autorest/...
|
||||
210
vendor/github.com/Azure/go-autorest/CHANGELOG.md
generated
vendored
Normal file
210
vendor/github.com/Azure/go-autorest/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v9.1.0
|
||||
|
||||
### New Features
|
||||
|
||||
- In cases where there is a non-empty error from the service, attempt to unmarshal it instead of uniformly calling it an "Unknown" error.
|
||||
- Support for loading Azure CLI Authentication files.
|
||||
- Automatically register your subscription with the Azure Resource Provider if it hadn't been previously.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- RetriableRequest can now tolerate a ReadSeekable body being read but not reset.
|
||||
- Adding missing Apache Headers
|
||||
|
||||
## v9.0.0
|
||||
|
||||
> **IMPORTANT:** This release was intially labeled incorrectly as `v8.4.0`. From the time it was released, it should have been marked `v9.0.0` because it contains breaking changes to the MSI packages. We appologize for any inconvenience this causes.
|
||||
|
||||
Adding MSI Endpoint Support and CLI token rehydration.
|
||||
|
||||
## v8.3.1
|
||||
|
||||
Pick up bug fix in adal for MSI support.
|
||||
|
||||
## v8.3.0
|
||||
|
||||
Updates to Error string formats for clarity. Also, adding a copy of the http.Response to errors for an improved debugging experience.
|
||||
|
||||
## v8.2.0
|
||||
|
||||
### New Features
|
||||
|
||||
- Add support for bearer authentication callbacks
|
||||
- Support 429 response codes that include "Retry-After" header
|
||||
- Support validation constraint "Pattern" for map keys
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Make RetriableRequest work with multiple versions of Go
|
||||
|
||||
## v8.1.1
|
||||
Updates the RetriableRequest to take advantage of GetBody() added in Go 1.8.
|
||||
|
||||
## v8.1.0
|
||||
Adds RetriableRequest type for more efficient handling of retrying HTTP requests.
|
||||
|
||||
## v8.0.0
|
||||
|
||||
ADAL refactored into its own package.
|
||||
Support for UNIX time.
|
||||
|
||||
## v7.3.1
|
||||
- Version Testing now removed from production bits that are shipped with the library.
|
||||
|
||||
## v7.3.0
|
||||
- Exposing new `RespondDecorator`, `ByDiscardingBody`. This allows operations
|
||||
to acknowledge that they do not need either the entire or a trailing portion
|
||||
of accepts response body. In doing so, Go's http library can reuse HTTP
|
||||
connections more readily.
|
||||
- Adding `PrepareDecorator` to target custom BaseURLs.
|
||||
- Adding ACR suffix to public cloud environment.
|
||||
- Updating Glide dependencies.
|
||||
|
||||
## v7.2.5
|
||||
- Fixed the Active Directory endpoint for the China cloud.
|
||||
- Removes UTF-8 BOM if present in response payload.
|
||||
- Added telemetry.
|
||||
|
||||
## v7.2.3
|
||||
- Fixing bug in calls to `DelayForBackoff` that caused doubling of delay
|
||||
duration.
|
||||
|
||||
## v7.2.2
|
||||
- autorest/azure: added ASM and ARM VM DNS suffixes.
|
||||
|
||||
## v7.2.1
|
||||
- fixed parsing of UTC times that are not RFC3339 conformant.
|
||||
|
||||
## v7.2.0
|
||||
- autorest/validation: Reformat validation error for better error message.
|
||||
|
||||
## v7.1.0
|
||||
- preparer: Added support for multipart formdata - WithMultiPartFormdata()
|
||||
- preparer: Added support for sending file in request body - WithFile
|
||||
- client: Added RetryDuration parameter.
|
||||
- autorest/validation: new package for validation code for Azure Go SDK.
|
||||
|
||||
## v7.0.7
|
||||
- Add trailing / to endpoint
|
||||
- azure: add EnvironmentFromName
|
||||
|
||||
## v7.0.6
|
||||
- Add retry logic for 408, 500, 502, 503 and 504 status codes.
|
||||
- Change url path and query encoding logic.
|
||||
- Fix DelayForBackoff for proper exponential delay.
|
||||
- Add CookieJar in Client.
|
||||
|
||||
## v7.0.5
|
||||
- Add check to start polling only when status is in [200,201,202].
|
||||
- Refactoring for unchecked errors.
|
||||
- azure/persist changes.
|
||||
- Fix 'file in use' issue in renewing token in deviceflow.
|
||||
- Store header RetryAfter for subsequent requests in polling.
|
||||
- Add attribute details in service error.
|
||||
|
||||
## v7.0.4
|
||||
- Better error messages for long running operation failures
|
||||
|
||||
## v7.0.3
|
||||
- Corrected DoPollForAsynchronous to properly handle the initial response
|
||||
|
||||
## v7.0.2
|
||||
- Corrected DoPollForAsynchronous to continue using the polling method first discovered
|
||||
|
||||
## v7.0.1
|
||||
- Fixed empty JSON input error in ByUnmarshallingJSON
|
||||
- Fixed polling support for GET calls
|
||||
- Changed format name from TimeRfc1123 to TimeRFC1123
|
||||
|
||||
## v7.0.0
|
||||
- Added ByCopying responder with supporting TeeReadCloser
|
||||
- Rewrote Azure asynchronous handling
|
||||
- Reverted to only unmarshalling JSON
|
||||
- Corrected handling of RFC3339 time strings and added support for Rfc1123 time format
|
||||
|
||||
The `json.Decoder` does not catch bad data as thoroughly as `json.Unmarshal`. Since
|
||||
`encoding/json` successfully deserializes all core types, and extended types normally provide
|
||||
their custom JSON serialization handlers, the code has been reverted back to using
|
||||
`json.Unmarshal`. The original change to use `json.Decode` was made to reduce duplicate
|
||||
code; there is no loss of function, and there is a gain in accuracy, by reverting.
|
||||
|
||||
Additionally, Azure services indicate requests to be polled by multiple means. The existing code
|
||||
only checked for one of those (that is, the presence of the `Azure-AsyncOperation` header).
|
||||
The new code correctly covers all cases and aligns with the other Azure SDKs.
|
||||
|
||||
## v6.1.0
|
||||
- Introduced `date.ByUnmarshallingJSONDate` and `date.ByUnmarshallingJSONTime` to enable JSON encoded values.
|
||||
|
||||
## v6.0.0
|
||||
- Completely reworked the handling of polled and asynchronous requests
|
||||
- Removed unnecessary routines
|
||||
- Reworked `mocks.Sender` to replay a series of `http.Response` objects
|
||||
- Added `PrepareDecorators` for primitive types (e.g., bool, int32)
|
||||
|
||||
Handling polled and asynchronous requests is no longer part of `Client#Send`. Instead new
|
||||
`SendDecorators` implement different styles of polled behavior. See`autorest.DoPollForStatusCodes`
|
||||
and `azure.DoPollForAsynchronous` for examples.
|
||||
|
||||
## v5.0.0
|
||||
- Added new RespondDecorators unmarshalling primitive types
|
||||
- Corrected application of inspection and authorization PrependDecorators
|
||||
|
||||
## v4.0.0
|
||||
- Added support for Azure long-running operations.
|
||||
- Added cancelation support to all decorators and functions that may delay.
|
||||
- Breaking: `DelayForBackoff` now accepts a channel, which may be nil.
|
||||
|
||||
## v3.1.0
|
||||
- Add support for OAuth Device Flow authorization.
|
||||
- Add support for ServicePrincipalTokens that are backed by an existing token, rather than other secret material.
|
||||
- Add helpers for persisting and restoring Tokens.
|
||||
- Increased code coverage in the github.com/Azure/autorest/azure package
|
||||
|
||||
## v3.0.0
|
||||
- Breaking: `NewErrorWithError` no longer takes `statusCode int`.
|
||||
- Breaking: `NewErrorWithStatusCode` is replaced with `NewErrorWithResponse`.
|
||||
- Breaking: `Client#Send()` no longer takes `codes ...int` argument.
|
||||
- Add: XML unmarshaling support with `ByUnmarshallingXML()`
|
||||
- Stopped vending dependencies locally and switched to [Glide](https://github.com/Masterminds/glide).
|
||||
Applications using this library should either use Glide or vendor dependencies locally some other way.
|
||||
- Add: `azure.WithErrorUnlessStatusCode()` decorator to handle Azure errors.
|
||||
- Fix: use `net/http.DefaultClient` as base client.
|
||||
- Fix: Missing inspection for polling responses added.
|
||||
- Add: CopyAndDecode helpers.
|
||||
- Improved `./autorest/to` with `[]string` helpers.
|
||||
- Removed golint suppressions in .travis.yml.
|
||||
|
||||
## v2.1.0
|
||||
|
||||
- Added `StatusCode` to `Error` for more easily obtaining the HTTP Reponse StatusCode (if any)
|
||||
|
||||
## v2.0.0
|
||||
|
||||
- Changed `to.StringMapPtr` method signature to return a pointer
|
||||
- Changed `ServicePrincipalCertificateSecret` and `NewServicePrincipalTokenFromCertificate` to support generic certificate and private keys
|
||||
|
||||
## v1.0.0
|
||||
|
||||
- Added Logging inspectors to trace http.Request / Response
|
||||
- Added support for User-Agent header
|
||||
- Changed WithHeader PrepareDecorator to use set vs. add
|
||||
- Added JSON to error when unmarshalling fails
|
||||
- Added Client#Send method
|
||||
- Corrected case of "Azure" in package paths
|
||||
- Added "to" helpers, Azure helpers, and improved ease-of-use
|
||||
- Corrected golint issues
|
||||
|
||||
## v1.0.1
|
||||
|
||||
- Added CHANGELOG.md
|
||||
|
||||
## v1.1.0
|
||||
|
||||
- Added mechanism to retrieve a ServicePrincipalToken using a certificate-signed JWT
|
||||
- Added an example of creating a certificate-based ServicePrincipal and retrieving an OAuth token using the certificate
|
||||
|
||||
## v1.1.1
|
||||
|
||||
- Introduce godeps and vendor dependencies introduced in v1.1.1
|
||||
23
vendor/github.com/Azure/go-autorest/GNUmakefile
generated
vendored
Normal file
23
vendor/github.com/Azure/go-autorest/GNUmakefile
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
DIR?=./autorest/
|
||||
|
||||
default: build
|
||||
|
||||
build: fmt
|
||||
go install $(DIR)
|
||||
|
||||
test:
|
||||
go test $(DIR) || exit 1
|
||||
|
||||
vet:
|
||||
@echo "go vet ."
|
||||
@go vet $(DIR)... ; if [ $$? -eq 1 ]; then \
|
||||
echo ""; \
|
||||
echo "Vet found suspicious constructs. Please check the reported constructs"; \
|
||||
echo "and fix them if necessary before submitting the code for review."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
fmt:
|
||||
gofmt -w $(DIR)
|
||||
|
||||
.PHONY: build test vet fmt
|
||||
191
vendor/github.com/Azure/go-autorest/LICENSE
generated
vendored
Normal file
191
vendor/github.com/Azure/go-autorest/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2015 Microsoft Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
132
vendor/github.com/Azure/go-autorest/README.md
generated
vendored
Normal file
132
vendor/github.com/Azure/go-autorest/README.md
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
# go-autorest
|
||||
|
||||
[](https://godoc.org/github.com/Azure/go-autorest/autorest) [](https://travis-ci.org/Azure/go-autorest) [](https://goreportcard.com/report/Azure/go-autorest)
|
||||
|
||||
## Usage
|
||||
Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines
|
||||
and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/)
|
||||
generated Go code.
|
||||
|
||||
The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending,
|
||||
and Responding. A typical pattern is:
|
||||
|
||||
```go
|
||||
req, err := Prepare(&http.Request{},
|
||||
token.WithAuthorization())
|
||||
|
||||
resp, err := Send(req,
|
||||
WithLogging(logger),
|
||||
DoErrorIfStatusCode(http.StatusInternalServerError),
|
||||
DoCloseIfError(),
|
||||
DoRetryForAttempts(5, time.Second))
|
||||
|
||||
err = Respond(resp,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
```
|
||||
|
||||
Each phase relies on decorators to modify and / or manage processing. Decorators may first modify
|
||||
and then pass the data along, pass the data first and then modify the result, or wrap themselves
|
||||
around passing the data (such as a logger might do). Decorators run in the order provided. For
|
||||
example, the following:
|
||||
|
||||
```go
|
||||
req, err := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
WithPath("a"),
|
||||
WithPath("b"),
|
||||
WithPath("c"))
|
||||
```
|
||||
|
||||
will set the URL to:
|
||||
|
||||
```
|
||||
https://microsoft.com/a/b/c
|
||||
```
|
||||
|
||||
Preparers and Responders may be shared and re-used (assuming the underlying decorators support
|
||||
sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders
|
||||
shared among multiple go-routines, and a single Sender shared among multiple sending go-routines,
|
||||
all bound together by means of input / output channels.
|
||||
|
||||
Decorators hold their passed state within a closure (such as the path components in the example
|
||||
above). Be careful to share Preparers and Responders only in a context where such held state
|
||||
applies. For example, it may not make sense to share a Preparer that applies a query string from a
|
||||
fixed set of values. Similarly, sharing a Responder that reads the response body into a passed
|
||||
struct (e.g., `ByUnmarshallingJson`) is likely incorrect.
|
||||
|
||||
Errors raised by autorest objects and methods will conform to the `autorest.Error` interface.
|
||||
|
||||
See the included examples for more detail. For details on the suggested use of this package by
|
||||
generated clients, see the Client described below.
|
||||
|
||||
## Helpers
|
||||
|
||||
### Handling Swagger Dates
|
||||
|
||||
The Swagger specification (https://swagger.io) that drives AutoRest
|
||||
(https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The
|
||||
github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure correct
|
||||
parsing and formatting.
|
||||
|
||||
### Handling Empty Values
|
||||
|
||||
In JSON, missing values have different semantics than empty values. This is especially true for
|
||||
services using the HTTP PATCH verb. The JSON submitted with a PATCH request generally contains
|
||||
only those values to modify. Missing values are to be left unchanged. Developers, then, require a
|
||||
means to both specify an empty value and to leave the value out of the submitted JSON.
|
||||
|
||||
The Go JSON package (`encoding/json`) supports the `omitempty` tag. When specified, it omits
|
||||
empty values from the rendered JSON. Since Go defines default values for all base types (such as ""
|
||||
for string and 0 for int) and provides no means to mark a value as actually empty, the JSON package
|
||||
treats default values as meaning empty, omitting them from the rendered JSON. This means that, using
|
||||
the Go base types encoded through the default JSON package, it is not possible to create JSON to
|
||||
clear a value at the server.
|
||||
|
||||
The workaround within the Go community is to use pointers to base types in lieu of base types within
|
||||
structures that map to JSON. For example, instead of a value of type `string`, the workaround uses
|
||||
`*string`. While this enables distinguishing empty values from those to be unchanged, creating
|
||||
pointers to a base type (notably constant, in-line values) requires additional variables. This, for
|
||||
example,
|
||||
|
||||
```go
|
||||
s := struct {
|
||||
S *string
|
||||
}{ S: &"foo" }
|
||||
```
|
||||
fails, while, this
|
||||
|
||||
```go
|
||||
v := "foo"
|
||||
s := struct {
|
||||
S *string
|
||||
}{ S: &v }
|
||||
```
|
||||
succeeds.
|
||||
|
||||
To ease using pointers, the subpackage `to` contains helpers that convert to and from pointers for
|
||||
Go base types which have Swagger analogs. It also provides a helper that converts between
|
||||
`map[string]string` and `map[string]*string`, enabling the JSON to specify that the value
|
||||
associated with a key should be cleared. With the helpers, the previous example becomes
|
||||
|
||||
```go
|
||||
s := struct {
|
||||
S *string
|
||||
}{ S: to.StringPtr("foo") }
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/Azure/go-autorest/autorest
|
||||
go get github.com/Azure/go-autorest/autorest/azure
|
||||
go get github.com/Azure/go-autorest/autorest/date
|
||||
go get github.com/Azure/go-autorest/autorest/to
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
See LICENSE file.
|
||||
|
||||
-----
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
253
vendor/github.com/Azure/go-autorest/autorest/adal/README.md
generated
vendored
Normal file
253
vendor/github.com/Azure/go-autorest/autorest/adal/README.md
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
# Azure Active Directory library for Go
|
||||
|
||||
This project provides a stand alone Azure Active Directory library for Go. The code was extracted
|
||||
from [go-autorest](https://github.com/Azure/go-autorest/) project, which is used as a base for
|
||||
[azure-sdk-for-go](https://github.com/Azure/azure-sdk-for-go).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
go get -u github.com/Azure/go-autorest/autorest/adal
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) follow these [guidelines](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli).
|
||||
|
||||
### Register an Azure AD Application with secret
|
||||
|
||||
|
||||
1. Register a new application with a `secret` credential
|
||||
|
||||
```
|
||||
az ad app create \
|
||||
--display-name example-app \
|
||||
--homepage https://example-app/home \
|
||||
--identifier-uris https://example-app/app \
|
||||
--password secret
|
||||
```
|
||||
|
||||
2. Create a service principal using the `Application ID` from previous step
|
||||
|
||||
```
|
||||
az ad sp create --id "Application ID"
|
||||
```
|
||||
|
||||
* Replace `Application ID` with `appId` from step 1.
|
||||
|
||||
### Register an Azure AD Application with certificate
|
||||
|
||||
1. Create a private key
|
||||
|
||||
```
|
||||
openssl genrsa -out "example-app.key" 2048
|
||||
```
|
||||
|
||||
2. Create the certificate
|
||||
|
||||
```
|
||||
openssl req -new -key "example-app.key" -subj "/CN=example-app" -out "example-app.csr"
|
||||
openssl x509 -req -in "example-app.csr" -signkey "example-app.key" -out "example-app.crt" -days 10000
|
||||
```
|
||||
|
||||
3. Create the PKCS12 version of the certificate containing also the private key
|
||||
|
||||
```
|
||||
openssl pkcs12 -export -out "example-app.pfx" -inkey "example-app.key" -in "example-app.crt" -passout pass:
|
||||
|
||||
```
|
||||
|
||||
4. Register a new application with the certificate content form `example-app.crt`
|
||||
|
||||
```
|
||||
certificateContents="$(tail -n+2 "example-app.crt" | head -n-1)"
|
||||
|
||||
az ad app create \
|
||||
--display-name example-app \
|
||||
--homepage https://example-app/home \
|
||||
--identifier-uris https://example-app/app \
|
||||
--key-usage Verify --end-date 2018-01-01 \
|
||||
--key-value "${certificateContents}"
|
||||
```
|
||||
|
||||
5. Create a service principal using the `Application ID` from previous step
|
||||
|
||||
```
|
||||
az ad sp create --id "APPLICATION_ID"
|
||||
```
|
||||
|
||||
* Replace `APPLICATION_ID` with `appId` from step 4.
|
||||
|
||||
|
||||
### Grant the necessary permissions
|
||||
|
||||
Azure relies on a Role-Based Access Control (RBAC) model to manage the access to resources at a fine-grained
|
||||
level. There is a set of [pre-defined roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles)
|
||||
which can be assigned to a service principal of an Azure AD application depending of your needs.
|
||||
|
||||
```
|
||||
az role assignment create --assigner "SERVICE_PRINCIPAL_ID" --role "ROLE_NAME"
|
||||
```
|
||||
|
||||
* Replace the `SERVICE_PRINCIPAL_ID` with the `appId` from previous step.
|
||||
* Replace the `ROLE_NAME` with a role name of your choice.
|
||||
|
||||
It is also possible to define custom role definitions.
|
||||
|
||||
```
|
||||
az role definition create --role-definition role-definition.json
|
||||
```
|
||||
|
||||
* Check [custom roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file.
|
||||
|
||||
|
||||
### Acquire Access Token
|
||||
|
||||
The common configuration used by all flows:
|
||||
|
||||
```Go
|
||||
const activeDirectoryEndpoint = "https://login.microsoftonline.com/"
|
||||
tenantID := "TENANT_ID"
|
||||
oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID)
|
||||
|
||||
applicationID := "APPLICATION_ID"
|
||||
|
||||
callback := func(token adal.Token) error {
|
||||
// This is called after the token is acquired
|
||||
}
|
||||
|
||||
// The resource for which the token is acquired
|
||||
resource := "https://management.core.windows.net/"
|
||||
```
|
||||
|
||||
* Replace the `TENANT_ID` with your tenant ID.
|
||||
* Replace the `APPLICATION_ID` with the value from previous section.
|
||||
|
||||
#### Client Credentials
|
||||
|
||||
```Go
|
||||
applicationSecret := "APPLICATION_SECRET"
|
||||
|
||||
spt, err := adal.NewServicePrincipalToken(
|
||||
oauthConfig,
|
||||
appliationID,
|
||||
applicationSecret,
|
||||
resource,
|
||||
callbacks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Acquire a new access token
|
||||
err = spt.Refresh()
|
||||
if (err == nil) {
|
||||
token := spt.Token
|
||||
}
|
||||
```
|
||||
|
||||
* Replace the `APPLICATION_SECRET` with the `password` value from previous section.
|
||||
|
||||
#### Client Certificate
|
||||
|
||||
```Go
|
||||
certificatePath := "./example-app.pfx"
|
||||
|
||||
certData, err := ioutil.ReadFile(certificatePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err)
|
||||
}
|
||||
|
||||
// Get the certificate and private key from pfx file
|
||||
certificate, rsaPrivateKey, err := decodePkcs12(certData, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromCertificate(
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
certificate,
|
||||
rsaPrivateKey,
|
||||
resource,
|
||||
callbacks...)
|
||||
|
||||
// Acquire a new access token
|
||||
err = spt.Refresh()
|
||||
if (err == nil) {
|
||||
token := spt.Token
|
||||
}
|
||||
```
|
||||
|
||||
* Update the certificate path to point to the example-app.pfx file which was created in previous section.
|
||||
|
||||
|
||||
#### Device Code
|
||||
|
||||
```Go
|
||||
oauthClient := &http.Client{}
|
||||
|
||||
// Acquire the device code
|
||||
deviceCode, err := adal.InitiateDeviceAuth(
|
||||
oauthClient,
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to start device auth flow: %s", err)
|
||||
}
|
||||
|
||||
// Display the authentication message
|
||||
fmt.Println(*deviceCode.Message)
|
||||
|
||||
// Wait here until the user is authenticated
|
||||
token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to finish device auth flow: %s", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
resource,
|
||||
*token,
|
||||
callbacks...)
|
||||
|
||||
if (err == nil) {
|
||||
token := spt.Token
|
||||
}
|
||||
```
|
||||
|
||||
### Command Line Tool
|
||||
|
||||
A command line tool is available in `cmd/adal.go` that can acquire a token for a given resource. It supports all flows mentioned above.
|
||||
|
||||
```
|
||||
adal -h
|
||||
|
||||
Usage of ./adal:
|
||||
-applicationId string
|
||||
application id
|
||||
-certificatePath string
|
||||
path to pk12/PFC application certificate
|
||||
-mode string
|
||||
authentication mode (device, secret, cert, refresh) (default "device")
|
||||
-resource string
|
||||
resource for which the token is requested
|
||||
-secret string
|
||||
application secret
|
||||
-tenantId string
|
||||
tenant id
|
||||
-tokenCachePath string
|
||||
location of oath token cache (default "/home/cgc/.adal/accessToken.json")
|
||||
```
|
||||
|
||||
Example acquire a token for `https://management.core.windows.net/` using device code flow:
|
||||
|
||||
```
|
||||
adal -mode device \
|
||||
-applicationId "APPLICATION_ID" \
|
||||
-tenantId "TENANT_ID" \
|
||||
-resource https://management.core.windows.net/
|
||||
|
||||
```
|
||||
298
vendor/github.com/Azure/go-autorest/autorest/adal/cmd/adal.go
generated
vendored
Normal file
298
vendor/github.com/Azure/go-autorest/autorest/adal/cmd/adal.go
generated
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
package main
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os/user"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
)
|
||||
|
||||
const (
|
||||
deviceMode = "device"
|
||||
clientSecretMode = "secret"
|
||||
clientCertMode = "cert"
|
||||
refreshMode = "refresh"
|
||||
|
||||
activeDirectoryEndpoint = "https://login.microsoftonline.com/"
|
||||
)
|
||||
|
||||
type option struct {
|
||||
name string
|
||||
value string
|
||||
}
|
||||
|
||||
var (
|
||||
mode string
|
||||
resource string
|
||||
|
||||
tenantID string
|
||||
applicationID string
|
||||
|
||||
applicationSecret string
|
||||
certificatePath string
|
||||
|
||||
tokenCachePath string
|
||||
)
|
||||
|
||||
func checkMandatoryOptions(mode string, options ...option) {
|
||||
for _, option := range options {
|
||||
if strings.TrimSpace(option.value) == "" {
|
||||
log.Fatalf("Authentication mode '%s' requires mandatory option '%s'.", mode, option.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func defaultTokenCachePath() string {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defaultTokenPath := usr.HomeDir + "/.adal/accessToken.json"
|
||||
return defaultTokenPath
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&mode, "mode", "device", "authentication mode (device, secret, cert, refresh)")
|
||||
flag.StringVar(&resource, "resource", "", "resource for which the token is requested")
|
||||
flag.StringVar(&tenantID, "tenantId", "", "tenant id")
|
||||
flag.StringVar(&applicationID, "applicationId", "", "application id")
|
||||
flag.StringVar(&applicationSecret, "secret", "", "application secret")
|
||||
flag.StringVar(&certificatePath, "certificatePath", "", "path to pk12/PFC application certificate")
|
||||
flag.StringVar(&tokenCachePath, "tokenCachePath", defaultTokenCachePath(), "location of oath token cache")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
switch mode = strings.TrimSpace(mode); mode {
|
||||
case clientSecretMode:
|
||||
checkMandatoryOptions(clientSecretMode,
|
||||
option{name: "resource", value: resource},
|
||||
option{name: "tenantId", value: tenantID},
|
||||
option{name: "applicationId", value: applicationID},
|
||||
option{name: "secret", value: applicationSecret},
|
||||
)
|
||||
case clientCertMode:
|
||||
checkMandatoryOptions(clientCertMode,
|
||||
option{name: "resource", value: resource},
|
||||
option{name: "tenantId", value: tenantID},
|
||||
option{name: "applicationId", value: applicationID},
|
||||
option{name: "certificatePath", value: certificatePath},
|
||||
)
|
||||
case deviceMode:
|
||||
checkMandatoryOptions(deviceMode,
|
||||
option{name: "resource", value: resource},
|
||||
option{name: "tenantId", value: tenantID},
|
||||
option{name: "applicationId", value: applicationID},
|
||||
)
|
||||
case refreshMode:
|
||||
checkMandatoryOptions(refreshMode,
|
||||
option{name: "resource", value: resource},
|
||||
option{name: "tenantId", value: tenantID},
|
||||
option{name: "applicationId", value: applicationID},
|
||||
)
|
||||
default:
|
||||
log.Fatalln("Authentication modes 'secret, 'cert', 'device' or 'refresh' are supported.")
|
||||
}
|
||||
}
|
||||
|
||||
func acquireTokenClientSecretFlow(oauthConfig adal.OAuthConfig,
|
||||
appliationID string,
|
||||
applicationSecret string,
|
||||
resource string,
|
||||
callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
|
||||
|
||||
spt, err := adal.NewServicePrincipalToken(
|
||||
oauthConfig,
|
||||
appliationID,
|
||||
applicationSecret,
|
||||
resource,
|
||||
callbacks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return spt, spt.Refresh()
|
||||
}
|
||||
|
||||
func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||
privateKey, certificate, err := pkcs12.Decode(pkcs, password)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey)
|
||||
if !isRsaKey {
|
||||
return nil, nil, fmt.Errorf("PKCS#12 certificate must contain an RSA private key")
|
||||
}
|
||||
|
||||
return certificate, rsaPrivateKey, nil
|
||||
}
|
||||
|
||||
func acquireTokenClientCertFlow(oauthConfig adal.OAuthConfig,
|
||||
applicationID string,
|
||||
applicationCertPath string,
|
||||
resource string,
|
||||
callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
|
||||
|
||||
certData, err := ioutil.ReadFile(certificatePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err)
|
||||
}
|
||||
|
||||
certificate, rsaPrivateKey, err := decodePkcs12(certData, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromCertificate(
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
certificate,
|
||||
rsaPrivateKey,
|
||||
resource,
|
||||
callbacks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return spt, spt.Refresh()
|
||||
}
|
||||
|
||||
func acquireTokenDeviceCodeFlow(oauthConfig adal.OAuthConfig,
|
||||
applicationID string,
|
||||
resource string,
|
||||
callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
|
||||
|
||||
oauthClient := &http.Client{}
|
||||
deviceCode, err := adal.InitiateDeviceAuth(
|
||||
oauthClient,
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to start device auth flow: %s", err)
|
||||
}
|
||||
|
||||
fmt.Println(*deviceCode.Message)
|
||||
|
||||
token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to finish device auth flow: %s", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
resource,
|
||||
*token,
|
||||
callbacks...)
|
||||
return spt, err
|
||||
}
|
||||
|
||||
func refreshToken(oauthConfig adal.OAuthConfig,
|
||||
applicationID string,
|
||||
resource string,
|
||||
tokenCachePath string,
|
||||
callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
|
||||
|
||||
token, err := adal.LoadToken(tokenCachePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load token from cache: %v", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
resource,
|
||||
*token,
|
||||
callbacks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return spt, spt.Refresh()
|
||||
}
|
||||
|
||||
func saveToken(spt adal.Token) error {
|
||||
if tokenCachePath != "" {
|
||||
err := adal.SaveToken(tokenCachePath, 0600, spt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Acquired token was saved in '%s' file\n", tokenCachePath)
|
||||
return nil
|
||||
|
||||
}
|
||||
return fmt.Errorf("empty path for token cache")
|
||||
}
|
||||
|
||||
func main() {
|
||||
oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
callback := func(token adal.Token) error {
|
||||
return saveToken(token)
|
||||
}
|
||||
|
||||
log.Printf("Authenticating with mode '%s'\n", mode)
|
||||
switch mode {
|
||||
case clientSecretMode:
|
||||
_, err = acquireTokenClientSecretFlow(
|
||||
*oauthConfig,
|
||||
applicationID,
|
||||
applicationSecret,
|
||||
resource,
|
||||
callback)
|
||||
case clientCertMode:
|
||||
_, err = acquireTokenClientCertFlow(
|
||||
*oauthConfig,
|
||||
applicationID,
|
||||
certificatePath,
|
||||
resource,
|
||||
callback)
|
||||
case deviceMode:
|
||||
var spt *adal.ServicePrincipalToken
|
||||
spt, err = acquireTokenDeviceCodeFlow(
|
||||
*oauthConfig,
|
||||
applicationID,
|
||||
resource,
|
||||
callback)
|
||||
if err == nil {
|
||||
err = saveToken(spt.Token)
|
||||
}
|
||||
case refreshMode:
|
||||
_, err = refreshToken(
|
||||
*oauthConfig,
|
||||
applicationID,
|
||||
resource,
|
||||
tokenCachePath,
|
||||
callback)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to acquire a token for resource %s. Error: %v", resource, err)
|
||||
}
|
||||
}
|
||||
65
vendor/github.com/Azure/go-autorest/autorest/adal/config.go
generated
vendored
Normal file
65
vendor/github.com/Azure/go-autorest/autorest/adal/config.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
activeDirectoryAPIVersion = "1.0"
|
||||
)
|
||||
|
||||
// OAuthConfig represents the endpoints needed
|
||||
// in OAuth operations
|
||||
type OAuthConfig struct {
|
||||
AuthorityEndpoint url.URL
|
||||
AuthorizeEndpoint url.URL
|
||||
TokenEndpoint url.URL
|
||||
DeviceCodeEndpoint url.URL
|
||||
}
|
||||
|
||||
// NewOAuthConfig returns an OAuthConfig with tenant specific urls
|
||||
func NewOAuthConfig(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) {
|
||||
const activeDirectoryEndpointTemplate = "%s/oauth2/%s?api-version=%s"
|
||||
u, err := url.Parse(activeDirectoryEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorityURL, err := u.Parse(tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "authorize", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "token", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deviceCodeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "devicecode", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &OAuthConfig{
|
||||
AuthorityEndpoint: *authorityURL,
|
||||
AuthorizeEndpoint: *authorizeURL,
|
||||
TokenEndpoint: *tokenURL,
|
||||
DeviceCodeEndpoint: *deviceCodeURL,
|
||||
}, nil
|
||||
}
|
||||
44
vendor/github.com/Azure/go-autorest/autorest/adal/config_test.go
generated
vendored
Normal file
44
vendor/github.com/Azure/go-autorest/autorest/adal/config_test.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewOAuthConfig(t *testing.T) {
|
||||
const testActiveDirectoryEndpoint = "https://login.test.com"
|
||||
const testTenantID = "tenant-id-test"
|
||||
|
||||
config, err := NewOAuthConfig(testActiveDirectoryEndpoint, testTenantID)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest/adal: Unexpected error while creating oauth configuration for tenant: %v.", err)
|
||||
}
|
||||
|
||||
expected := "https://login.test.com/tenant-id-test/oauth2/authorize?api-version=1.0"
|
||||
if config.AuthorizeEndpoint.String() != expected {
|
||||
t.Fatalf("autorest/adal: Incorrect authorize url for Tenant from Environment. expected(%s). actual(%v).", expected, config.AuthorizeEndpoint)
|
||||
}
|
||||
|
||||
expected = "https://login.test.com/tenant-id-test/oauth2/token?api-version=1.0"
|
||||
if config.TokenEndpoint.String() != expected {
|
||||
t.Fatalf("autorest/adal: Incorrect authorize url for Tenant from Environment. expected(%s). actual(%v).", expected, config.TokenEndpoint)
|
||||
}
|
||||
|
||||
expected = "https://login.test.com/tenant-id-test/oauth2/devicecode?api-version=1.0"
|
||||
if config.DeviceCodeEndpoint.String() != expected {
|
||||
t.Fatalf("autorest/adal Incorrect devicecode url for Tenant from Environment. expected(%s). actual(%v).", expected, config.DeviceCodeEndpoint)
|
||||
}
|
||||
}
|
||||
242
vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go
generated
vendored
Normal file
242
vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/*
|
||||
This file is largely based on rjw57/oauth2device's code, with the follow differences:
|
||||
* scope -> resource, and only allow a single one
|
||||
* receive "Message" in the DeviceCode struct and show it to users as the prompt
|
||||
* azure-xplat-cli has the following behavior that this emulates:
|
||||
- does not send client_secret during the token exchange
|
||||
- sends resource again in the token exchange request
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
logPrefix = "autorest/adal/devicetoken:"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDeviceGeneric represents an unknown error from the token endpoint when using device flow
|
||||
ErrDeviceGeneric = fmt.Errorf("%s Error while retrieving OAuth token: Unknown Error", logPrefix)
|
||||
|
||||
// ErrDeviceAccessDenied represents an access denied error from the token endpoint when using device flow
|
||||
ErrDeviceAccessDenied = fmt.Errorf("%s Error while retrieving OAuth token: Access Denied", logPrefix)
|
||||
|
||||
// ErrDeviceAuthorizationPending represents the server waiting on the user to complete the device flow
|
||||
ErrDeviceAuthorizationPending = fmt.Errorf("%s Error while retrieving OAuth token: Authorization Pending", logPrefix)
|
||||
|
||||
// ErrDeviceCodeExpired represents the server timing out and expiring the code during device flow
|
||||
ErrDeviceCodeExpired = fmt.Errorf("%s Error while retrieving OAuth token: Code Expired", logPrefix)
|
||||
|
||||
// ErrDeviceSlowDown represents the service telling us we're polling too often during device flow
|
||||
ErrDeviceSlowDown = fmt.Errorf("%s Error while retrieving OAuth token: Slow Down", logPrefix)
|
||||
|
||||
// ErrDeviceCodeEmpty represents an empty device code from the device endpoint while using device flow
|
||||
ErrDeviceCodeEmpty = fmt.Errorf("%s Error while retrieving device code: Device Code Empty", logPrefix)
|
||||
|
||||
// ErrOAuthTokenEmpty represents an empty OAuth token from the token endpoint when using device flow
|
||||
ErrOAuthTokenEmpty = fmt.Errorf("%s Error while retrieving OAuth token: Token Empty", logPrefix)
|
||||
|
||||
errCodeSendingFails = "Error occurred while sending request for Device Authorization Code"
|
||||
errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint"
|
||||
errTokenSendingFails = "Error occurred while sending request with device code for a token"
|
||||
errTokenHandlingFails = "Error occurred while handling response from the Token Endpoint (during device flow)"
|
||||
errStatusNotOK = "Error HTTP status != 200"
|
||||
)
|
||||
|
||||
// DeviceCode is the object returned by the device auth endpoint
|
||||
// It contains information to instruct the user to complete the auth flow
|
||||
type DeviceCode struct {
|
||||
DeviceCode *string `json:"device_code,omitempty"`
|
||||
UserCode *string `json:"user_code,omitempty"`
|
||||
VerificationURL *string `json:"verification_url,omitempty"`
|
||||
ExpiresIn *int64 `json:"expires_in,string,omitempty"`
|
||||
Interval *int64 `json:"interval,string,omitempty"`
|
||||
|
||||
Message *string `json:"message"` // Azure specific
|
||||
Resource string // store the following, stored when initiating, used when exchanging
|
||||
OAuthConfig OAuthConfig
|
||||
ClientID string
|
||||
}
|
||||
|
||||
// TokenError is the object returned by the token exchange endpoint
|
||||
// when something is amiss
|
||||
type TokenError struct {
|
||||
Error *string `json:"error,omitempty"`
|
||||
ErrorCodes []int `json:"error_codes,omitempty"`
|
||||
ErrorDescription *string `json:"error_description,omitempty"`
|
||||
Timestamp *string `json:"timestamp,omitempty"`
|
||||
TraceID *string `json:"trace_id,omitempty"`
|
||||
}
|
||||
|
||||
// DeviceToken is the object return by the token exchange endpoint
|
||||
// It can either look like a Token or an ErrorToken, so put both here
|
||||
// and check for presence of "Error" to know if we are in error state
|
||||
type deviceToken struct {
|
||||
Token
|
||||
TokenError
|
||||
}
|
||||
|
||||
// InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode
|
||||
// that can be used with CheckForUserCompletion or WaitForUserCompletion.
|
||||
func InitiateDeviceAuth(sender Sender, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) {
|
||||
v := url.Values{
|
||||
"client_id": []string{clientID},
|
||||
"resource": []string{resource},
|
||||
}
|
||||
|
||||
s := v.Encode()
|
||||
body := ioutil.NopCloser(strings.NewReader(s))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, oauthConfig.DeviceCodeEndpoint.String(), body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error())
|
||||
}
|
||||
|
||||
req.ContentLength = int64(len(s))
|
||||
req.Header.Set(contentType, mimeTypeFormPost)
|
||||
resp, err := sender.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
rb, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, errStatusNotOK)
|
||||
}
|
||||
|
||||
if len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return nil, ErrDeviceCodeEmpty
|
||||
}
|
||||
|
||||
var code DeviceCode
|
||||
err = json.Unmarshal(rb, &code)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
code.ClientID = clientID
|
||||
code.Resource = resource
|
||||
code.OAuthConfig = oauthConfig
|
||||
|
||||
return &code, nil
|
||||
}
|
||||
|
||||
// CheckForUserCompletion takes a DeviceCode and checks with the Azure AD OAuth endpoint
|
||||
// to see if the device flow has: been completed, timed out, or otherwise failed
|
||||
func CheckForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
|
||||
v := url.Values{
|
||||
"client_id": []string{code.ClientID},
|
||||
"code": []string{*code.DeviceCode},
|
||||
"grant_type": []string{OAuthGrantTypeDeviceCode},
|
||||
"resource": []string{code.Resource},
|
||||
}
|
||||
|
||||
s := v.Encode()
|
||||
body := ioutil.NopCloser(strings.NewReader(s))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, code.OAuthConfig.TokenEndpoint.String(), body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error())
|
||||
}
|
||||
|
||||
req.ContentLength = int64(len(s))
|
||||
req.Header.Set(contentType, mimeTypeFormPost)
|
||||
resp, err := sender.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
rb, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK && len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, errStatusNotOK)
|
||||
}
|
||||
if len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return nil, ErrOAuthTokenEmpty
|
||||
}
|
||||
|
||||
var token deviceToken
|
||||
err = json.Unmarshal(rb, &token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if token.Error == nil {
|
||||
return &token.Token, nil
|
||||
}
|
||||
|
||||
switch *token.Error {
|
||||
case "authorization_pending":
|
||||
return nil, ErrDeviceAuthorizationPending
|
||||
case "slow_down":
|
||||
return nil, ErrDeviceSlowDown
|
||||
case "access_denied":
|
||||
return nil, ErrDeviceAccessDenied
|
||||
case "code_expired":
|
||||
return nil, ErrDeviceCodeExpired
|
||||
default:
|
||||
return nil, ErrDeviceGeneric
|
||||
}
|
||||
}
|
||||
|
||||
// WaitForUserCompletion calls CheckForUserCompletion repeatedly until a token is granted or an error state occurs.
|
||||
// This prevents the user from looping and checking against 'ErrDeviceAuthorizationPending'.
|
||||
func WaitForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
|
||||
intervalDuration := time.Duration(*code.Interval) * time.Second
|
||||
waitDuration := intervalDuration
|
||||
|
||||
for {
|
||||
token, err := CheckForUserCompletion(sender, code)
|
||||
|
||||
if err == nil {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
switch err {
|
||||
case ErrDeviceSlowDown:
|
||||
waitDuration += waitDuration
|
||||
case ErrDeviceAuthorizationPending:
|
||||
// noop
|
||||
default: // everything else is "fatal" to us
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if waitDuration > (intervalDuration * 3) {
|
||||
return nil, fmt.Errorf("%s Error waiting for user to complete device flow. Server told us to slow_down too much", logPrefix)
|
||||
}
|
||||
|
||||
time.Sleep(waitDuration)
|
||||
}
|
||||
}
|
||||
330
vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken_test.go
generated
vendored
Normal file
330
vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken_test.go
generated
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
const (
|
||||
TestResource = "SomeResource"
|
||||
TestClientID = "SomeClientID"
|
||||
TestTenantID = "SomeTenantID"
|
||||
TestActiveDirectoryEndpoint = "https://login.test.com/"
|
||||
)
|
||||
|
||||
var (
|
||||
testOAuthConfig, _ = NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID)
|
||||
TestOAuthConfig = *testOAuthConfig
|
||||
)
|
||||
|
||||
const MockDeviceCodeResponse = `
|
||||
{
|
||||
"device_code": "10000-40-1234567890",
|
||||
"user_code": "ABCDEF",
|
||||
"verification_url": "http://aka.ms/deviceauth",
|
||||
"expires_in": "900",
|
||||
"interval": "0"
|
||||
}
|
||||
`
|
||||
|
||||
const MockDeviceTokenResponse = `{
|
||||
"access_token": "accessToken",
|
||||
"refresh_token": "refreshToken",
|
||||
"expires_in": "1000",
|
||||
"expires_on": "2000",
|
||||
"not_before": "3000",
|
||||
"resource": "resource",
|
||||
"token_type": "type"
|
||||
}
|
||||
`
|
||||
|
||||
func TestDeviceCodeIncludesResource(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
sender.AppendResponse(mocks.NewResponseWithContent(MockDeviceCodeResponse))
|
||||
|
||||
code, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
|
||||
if err != nil {
|
||||
t.Fatalf("adal: unexpected error initiating device auth")
|
||||
}
|
||||
|
||||
if code.Resource != TestResource {
|
||||
t.Fatalf("adal: InitiateDeviceAuth failed to stash the resource in the DeviceCode struct")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceCodeReturnsErrorIfSendingFails(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
sender.SetError(fmt.Errorf("this is an error"))
|
||||
|
||||
_, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
|
||||
if err == nil || !strings.Contains(err.Error(), errCodeSendingFails) {
|
||||
t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeSendingFails, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceCodeReturnsErrorIfBadRequest(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody("doesn't matter")
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
|
||||
|
||||
_, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
|
||||
if err == nil || !strings.Contains(err.Error(), errCodeHandlingFails) {
|
||||
t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceCodeReturnsErrorIfCannotDeserializeDeviceCode(t *testing.T) {
|
||||
gibberishJSON := strings.Replace(MockDeviceCodeResponse, "expires_in", "\":, :gibberish", -1)
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody(gibberishJSON)
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
|
||||
|
||||
_, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
|
||||
if err == nil || !strings.Contains(err.Error(), errCodeHandlingFails) {
|
||||
t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceCodeReturnsErrorIfEmptyDeviceCode(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody("")
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
|
||||
|
||||
_, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
|
||||
if err != ErrDeviceCodeEmpty {
|
||||
t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", ErrDeviceCodeEmpty, err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func deviceCode() *DeviceCode {
|
||||
var deviceCode DeviceCode
|
||||
_ = json.Unmarshal([]byte(MockDeviceCodeResponse), &deviceCode)
|
||||
deviceCode.Resource = TestResource
|
||||
deviceCode.ClientID = TestClientID
|
||||
return &deviceCode
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturns(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody(MockDeviceTokenResponse)
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err != nil {
|
||||
t.Fatalf("adal: got error unexpectedly")
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorIfSendingFails(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
sender.SetError(fmt.Errorf("this is an error"))
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err == nil || !strings.Contains(err.Error(), errTokenSendingFails) {
|
||||
t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenSendingFails, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorIfServerError(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody("")
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusInternalServerError, "Internal Server Error"))
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err == nil || !strings.Contains(err.Error(), errTokenHandlingFails) {
|
||||
t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorIfCannotDeserializeDeviceToken(t *testing.T) {
|
||||
gibberishJSON := strings.Replace(MockDeviceTokenResponse, "expires_in", ";:\"gibberish", -1)
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody(gibberishJSON)
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err == nil || !strings.Contains(err.Error(), errTokenHandlingFails) {
|
||||
t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func errorDeviceTokenResponse(message string) string {
|
||||
return `{ "error": "` + message + `" }`
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorIfAuthorizationPending(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody(errorDeviceTokenResponse("authorization_pending"))
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
|
||||
|
||||
_, err := CheckForUserCompletion(sender, deviceCode())
|
||||
if err != ErrDeviceAuthorizationPending {
|
||||
t.Fatalf("!!!")
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorIfSlowDown(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody(errorDeviceTokenResponse("slow_down"))
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
|
||||
|
||||
_, err := CheckForUserCompletion(sender, deviceCode())
|
||||
if err != ErrDeviceSlowDown {
|
||||
t.Fatalf("!!!")
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
type deviceTokenSender struct {
|
||||
errorString string
|
||||
attempts int
|
||||
}
|
||||
|
||||
func newDeviceTokenSender(deviceErrorString string) *deviceTokenSender {
|
||||
return &deviceTokenSender{errorString: deviceErrorString, attempts: 0}
|
||||
}
|
||||
|
||||
func (s *deviceTokenSender) Do(req *http.Request) (*http.Response, error) {
|
||||
var resp *http.Response
|
||||
if s.attempts < 1 {
|
||||
s.attempts++
|
||||
resp = mocks.NewResponseWithContent(errorDeviceTokenResponse(s.errorString))
|
||||
} else {
|
||||
resp = mocks.NewResponseWithContent(MockDeviceTokenResponse)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// since the above only exercise CheckForUserCompletion, we repeat the test here,
|
||||
// but with the intent of showing that WaitForUserCompletion loops properly.
|
||||
func TestDeviceTokenSucceedsWithIntermediateAuthPending(t *testing.T) {
|
||||
sender := newDeviceTokenSender("authorization_pending")
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
// same as above but with SlowDown now
|
||||
func TestDeviceTokenSucceedsWithIntermediateSlowDown(t *testing.T) {
|
||||
sender := newDeviceTokenSender("slow_down")
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorIfAccessDenied(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody(errorDeviceTokenResponse("access_denied"))
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err != ErrDeviceAccessDenied {
|
||||
t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceAccessDenied.Error(), err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorIfCodeExpired(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody(errorDeviceTokenResponse("code_expired"))
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err != ErrDeviceCodeExpired {
|
||||
t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceCodeExpired.Error(), err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorForUnknownError(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody(errorDeviceTokenResponse("unknown_error"))
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err == nil {
|
||||
t.Fatalf("failed to get error")
|
||||
}
|
||||
if err != ErrDeviceGeneric {
|
||||
t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceGeneric.Error(), err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceTokenReturnsErrorIfTokenEmptyAndStatusOK(t *testing.T) {
|
||||
sender := mocks.NewSender()
|
||||
body := mocks.NewBody("")
|
||||
sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
|
||||
|
||||
_, err := WaitForUserCompletion(sender, deviceCode())
|
||||
if err != ErrOAuthTokenEmpty {
|
||||
t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrOAuthTokenEmpty.Error(), err.Error())
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("response body was left open!")
|
||||
}
|
||||
}
|
||||
20
vendor/github.com/Azure/go-autorest/autorest/adal/msi.go
generated
vendored
Normal file
20
vendor/github.com/Azure/go-autorest/autorest/adal/msi.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// +build !windows
|
||||
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// msiPath is the path to the MSI Extension settings file (to discover the endpoint)
|
||||
var msiPath = "/var/lib/waagent/ManagedIdentity-Settings"
|
||||
25
vendor/github.com/Azure/go-autorest/autorest/adal/msi_windows.go
generated
vendored
Normal file
25
vendor/github.com/Azure/go-autorest/autorest/adal/msi_windows.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// +build windows
|
||||
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// msiPath is the path to the MSI Extension settings file (to discover the endpoint)
|
||||
var msiPath = strings.Join([]string{os.Getenv("SystemDrive"), "WindowsAzure/Config/ManagedIdentity-Settings"}, "/")
|
||||
73
vendor/github.com/Azure/go-autorest/autorest/adal/persist.go
generated
vendored
Normal file
73
vendor/github.com/Azure/go-autorest/autorest/adal/persist.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// LoadToken restores a Token object from a file located at 'path'.
|
||||
func LoadToken(path string) (*Token, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var token Token
|
||||
|
||||
dec := json.NewDecoder(file)
|
||||
if err = dec.Decode(&token); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode contents of file (%s) into Token representation: %v", path, err)
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
// SaveToken persists an oauth token at the given location on disk.
|
||||
// It moves the new file into place so it can safely be used to replace an existing file
|
||||
// that maybe accessed by multiple processes.
|
||||
func SaveToken(path string, mode os.FileMode, token Token) error {
|
||||
dir := filepath.Dir(path)
|
||||
err := os.MkdirAll(dir, os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory (%s) to store token in: %v", dir, err)
|
||||
}
|
||||
|
||||
newFile, err := ioutil.TempFile(dir, "token")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create the temp file to write the token: %v", err)
|
||||
}
|
||||
tempPath := newFile.Name()
|
||||
|
||||
if err := json.NewEncoder(newFile).Encode(token); err != nil {
|
||||
return fmt.Errorf("failed to encode token to file (%s) while saving token: %v", tempPath, err)
|
||||
}
|
||||
if err := newFile.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close temp file %s: %v", tempPath, err)
|
||||
}
|
||||
|
||||
// Atomic replace to avoid multi-writer file corruptions
|
||||
if err := os.Rename(tempPath, path); err != nil {
|
||||
return fmt.Errorf("failed to move temporary token to desired output location. src=%s dst=%s: %v", tempPath, path, err)
|
||||
}
|
||||
if err := os.Chmod(path, mode); err != nil {
|
||||
return fmt.Errorf("failed to chmod the token file %s: %v", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
171
vendor/github.com/Azure/go-autorest/autorest/adal/persist_test.go
generated
vendored
Normal file
171
vendor/github.com/Azure/go-autorest/autorest/adal/persist_test.go
generated
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const MockTokenJSON string = `{
|
||||
"access_token": "accessToken",
|
||||
"refresh_token": "refreshToken",
|
||||
"expires_in": "1000",
|
||||
"expires_on": "2000",
|
||||
"not_before": "3000",
|
||||
"resource": "resource",
|
||||
"token_type": "type"
|
||||
}`
|
||||
|
||||
var TestToken = Token{
|
||||
AccessToken: "accessToken",
|
||||
RefreshToken: "refreshToken",
|
||||
ExpiresIn: "1000",
|
||||
ExpiresOn: "2000",
|
||||
NotBefore: "3000",
|
||||
Resource: "resource",
|
||||
Type: "type",
|
||||
}
|
||||
|
||||
func writeTestTokenFile(t *testing.T, suffix string, contents string) *os.File {
|
||||
f, err := ioutil.TempFile(os.TempDir(), suffix)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: unexpected error when creating temp file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write([]byte(contents))
|
||||
if err != nil {
|
||||
t.Fatalf("azure: unexpected error when writing temp test file: %v", err)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func TestLoadToken(t *testing.T) {
|
||||
f := writeTestTokenFile(t, "testloadtoken", MockTokenJSON)
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
expectedToken := TestToken
|
||||
actualToken, err := LoadToken(f.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("azure: unexpected error loading token from file: %v", err)
|
||||
}
|
||||
|
||||
if *actualToken != expectedToken {
|
||||
t.Fatalf("azure: failed to decode properly expected(%v) actual(%v)", expectedToken, *actualToken)
|
||||
}
|
||||
|
||||
// test that LoadToken closes the file properly
|
||||
err = SaveToken(f.Name(), 0600, *actualToken)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: could not save token after LoadToken: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadTokenFailsBadPath(t *testing.T) {
|
||||
_, err := LoadToken("/tmp/this_file_should_never_exist_really")
|
||||
expectedSubstring := "failed to open file"
|
||||
if err == nil || !strings.Contains(err.Error(), expectedSubstring) {
|
||||
t.Fatalf("azure: failed to get correct error expected(%s) actual(%s)", expectedSubstring, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadTokenFailsBadJson(t *testing.T) {
|
||||
gibberishJSON := strings.Replace(MockTokenJSON, "expires_on", ";:\"gibberish", -1)
|
||||
f := writeTestTokenFile(t, "testloadtokenfailsbadjson", gibberishJSON)
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
_, err := LoadToken(f.Name())
|
||||
expectedSubstring := "failed to decode contents of file"
|
||||
if err == nil || !strings.Contains(err.Error(), expectedSubstring) {
|
||||
t.Fatalf("azure: failed to get correct error expected(%s) actual(%s)", expectedSubstring, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func token() *Token {
|
||||
var token Token
|
||||
json.Unmarshal([]byte(MockTokenJSON), &token)
|
||||
return &token
|
||||
}
|
||||
|
||||
func TestSaveToken(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "testloadtoken")
|
||||
if err != nil {
|
||||
t.Fatalf("azure: unexpected error when creating temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
f.Close()
|
||||
|
||||
mode := os.ModePerm & 0642
|
||||
err = SaveToken(f.Name(), mode, *token())
|
||||
if err != nil {
|
||||
t.Fatalf("azure: unexpected error saving token to file: %v", err)
|
||||
}
|
||||
fi, err := os.Stat(f.Name()) // open a new stat as held ones are not fresh
|
||||
if err != nil {
|
||||
t.Fatalf("azure: stat failed: %v", err)
|
||||
}
|
||||
if runtime.GOOS != "windows" { // permissions don't work on Windows
|
||||
if perm := fi.Mode().Perm(); perm != mode {
|
||||
t.Fatalf("azure: wrong file perm. got:%s; expected:%s file :%s", perm, mode, f.Name())
|
||||
}
|
||||
}
|
||||
|
||||
var actualToken Token
|
||||
var expectedToken Token
|
||||
|
||||
json.Unmarshal([]byte(MockTokenJSON), expectedToken)
|
||||
|
||||
contents, err := ioutil.ReadFile(f.Name())
|
||||
if err != nil {
|
||||
t.Fatal("!!")
|
||||
}
|
||||
json.Unmarshal(contents, actualToken)
|
||||
|
||||
if !reflect.DeepEqual(actualToken, expectedToken) {
|
||||
t.Fatal("azure: token was not serialized correctly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveTokenFailsNoPermission(t *testing.T) {
|
||||
pathWhereWeShouldntHavePermission := "/usr/thiswontwork/atall"
|
||||
if runtime.GOOS == "windows" {
|
||||
pathWhereWeShouldntHavePermission = path.Join(os.Getenv("windir"), "system32\\mytokendir\\mytoken")
|
||||
}
|
||||
err := SaveToken(pathWhereWeShouldntHavePermission, 0644, *token())
|
||||
expectedSubstring := "failed to create directory"
|
||||
if err == nil || !strings.Contains(err.Error(), expectedSubstring) {
|
||||
t.Fatalf("azure: failed to get correct error expected(%s) actual(%v)", expectedSubstring, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveTokenFailsCantCreate(t *testing.T) {
|
||||
tokenPath := "/thiswontwork"
|
||||
if runtime.GOOS == "windows" {
|
||||
tokenPath = path.Join(os.Getenv("windir"), "system32")
|
||||
}
|
||||
err := SaveToken(tokenPath, 0644, *token())
|
||||
expectedSubstring := "failed to create the temp file to write the token"
|
||||
if err == nil || !strings.Contains(err.Error(), expectedSubstring) {
|
||||
t.Fatalf("azure: failed to get correct error expected(%s) actual(%v)", expectedSubstring, err)
|
||||
}
|
||||
}
|
||||
60
vendor/github.com/Azure/go-autorest/autorest/adal/sender.go
generated
vendored
Normal file
60
vendor/github.com/Azure/go-autorest/autorest/adal/sender.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
contentType = "Content-Type"
|
||||
mimeTypeFormPost = "application/x-www-form-urlencoded"
|
||||
)
|
||||
|
||||
// Sender is the interface that wraps the Do method to send HTTP requests.
|
||||
//
|
||||
// The standard http.Client conforms to this interface.
|
||||
type Sender interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// SenderFunc is a method that implements the Sender interface.
|
||||
type SenderFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
// Do implements the Sender interface on SenderFunc.
|
||||
func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) {
|
||||
return sf(r)
|
||||
}
|
||||
|
||||
// SendDecorator takes and possibily decorates, by wrapping, a Sender. Decorators may affect the
|
||||
// http.Request and pass it along or, first, pass the http.Request along then react to the
|
||||
// http.Response result.
|
||||
type SendDecorator func(Sender) Sender
|
||||
|
||||
// CreateSender creates, decorates, and returns, as a Sender, the default http.Client.
|
||||
func CreateSender(decorators ...SendDecorator) Sender {
|
||||
return DecorateSender(&http.Client{}, decorators...)
|
||||
}
|
||||
|
||||
// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to
|
||||
// the Sender. Decorators are applied in the order received, but their affect upon the request
|
||||
// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a
|
||||
// post-decorator (pass the http.Request along and react to the results in http.Response).
|
||||
func DecorateSender(s Sender, decorators ...SendDecorator) Sender {
|
||||
for _, decorate := range decorators {
|
||||
s = decorate(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
427
vendor/github.com/Azure/go-autorest/autorest/adal/token.go
generated
vendored
Normal file
427
vendor/github.com/Azure/go-autorest/autorest/adal/token.go
generated
vendored
Normal file
@@ -0,0 +1,427 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/date"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRefresh = 5 * time.Minute
|
||||
|
||||
// OAuthGrantTypeDeviceCode is the "grant_type" identifier used in device flow
|
||||
OAuthGrantTypeDeviceCode = "device_code"
|
||||
|
||||
// OAuthGrantTypeClientCredentials is the "grant_type" identifier used in credential flows
|
||||
OAuthGrantTypeClientCredentials = "client_credentials"
|
||||
|
||||
// OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows
|
||||
OAuthGrantTypeRefreshToken = "refresh_token"
|
||||
|
||||
// metadataHeader is the header required by MSI extension
|
||||
metadataHeader = "Metadata"
|
||||
)
|
||||
|
||||
// OAuthTokenProvider is an interface which should be implemented by an access token retriever
|
||||
type OAuthTokenProvider interface {
|
||||
OAuthToken() string
|
||||
}
|
||||
|
||||
// Refresher is an interface for token refresh functionality
|
||||
type Refresher interface {
|
||||
Refresh() error
|
||||
RefreshExchange(resource string) error
|
||||
EnsureFresh() error
|
||||
}
|
||||
|
||||
// TokenRefreshCallback is the type representing callbacks that will be called after
|
||||
// a successful token refresh
|
||||
type TokenRefreshCallback func(Token) error
|
||||
|
||||
// Token encapsulates the access token used to authorize Azure requests.
|
||||
type Token struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
|
||||
ExpiresIn string `json:"expires_in"`
|
||||
ExpiresOn string `json:"expires_on"`
|
||||
NotBefore string `json:"not_before"`
|
||||
|
||||
Resource string `json:"resource"`
|
||||
Type string `json:"token_type"`
|
||||
}
|
||||
|
||||
// Expires returns the time.Time when the Token expires.
|
||||
func (t Token) Expires() time.Time {
|
||||
s, err := strconv.Atoi(t.ExpiresOn)
|
||||
if err != nil {
|
||||
s = -3600
|
||||
}
|
||||
|
||||
expiration := date.NewUnixTimeFromSeconds(float64(s))
|
||||
|
||||
return time.Time(expiration).UTC()
|
||||
}
|
||||
|
||||
// IsExpired returns true if the Token is expired, false otherwise.
|
||||
func (t Token) IsExpired() bool {
|
||||
return t.WillExpireIn(0)
|
||||
}
|
||||
|
||||
// WillExpireIn returns true if the Token will expire after the passed time.Duration interval
|
||||
// from now, false otherwise.
|
||||
func (t Token) WillExpireIn(d time.Duration) bool {
|
||||
return !t.Expires().After(time.Now().Add(d))
|
||||
}
|
||||
|
||||
//OAuthToken return the current access token
|
||||
func (t *Token) OAuthToken() string {
|
||||
return t.AccessToken
|
||||
}
|
||||
|
||||
// ServicePrincipalNoSecret represents a secret type that contains no secret
|
||||
// meaning it is not valid for fetching a fresh token. This is used by Manual
|
||||
type ServicePrincipalNoSecret struct {
|
||||
}
|
||||
|
||||
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret
|
||||
// It only returns an error for the ServicePrincipalNoSecret type
|
||||
func (noSecret *ServicePrincipalNoSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
|
||||
return fmt.Errorf("Manually created ServicePrincipalToken does not contain secret material to retrieve a new access token")
|
||||
}
|
||||
|
||||
// ServicePrincipalSecret is an interface that allows various secret mechanism to fill the form
|
||||
// that is submitted when acquiring an oAuth token.
|
||||
type ServicePrincipalSecret interface {
|
||||
SetAuthenticationValues(spt *ServicePrincipalToken, values *url.Values) error
|
||||
}
|
||||
|
||||
// ServicePrincipalTokenSecret implements ServicePrincipalSecret for client_secret type authorization.
|
||||
type ServicePrincipalTokenSecret struct {
|
||||
ClientSecret string
|
||||
}
|
||||
|
||||
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
|
||||
// It will populate the form submitted during oAuth Token Acquisition using the client_secret.
|
||||
func (tokenSecret *ServicePrincipalTokenSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
|
||||
v.Set("client_secret", tokenSecret.ClientSecret)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServicePrincipalCertificateSecret implements ServicePrincipalSecret for generic RSA cert auth with signed JWTs.
|
||||
type ServicePrincipalCertificateSecret struct {
|
||||
Certificate *x509.Certificate
|
||||
PrivateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
// ServicePrincipalMSISecret implements ServicePrincipalSecret for machines running the MSI Extension.
|
||||
type ServicePrincipalMSISecret struct {
|
||||
}
|
||||
|
||||
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
|
||||
func (msiSecret *ServicePrincipalMSISecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignJwt returns the JWT signed with the certificate's private key.
|
||||
func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) {
|
||||
hasher := sha1.New()
|
||||
_, err := hasher.Write(secret.Certificate.Raw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
thumbprint := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
// The jti (JWT ID) claim provides a unique identifier for the JWT.
|
||||
jti := make([]byte, 20)
|
||||
_, err = rand.Read(jti)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token := jwt.New(jwt.SigningMethodRS256)
|
||||
token.Header["x5t"] = thumbprint
|
||||
token.Claims = jwt.MapClaims{
|
||||
"aud": spt.oauthConfig.TokenEndpoint.String(),
|
||||
"iss": spt.clientID,
|
||||
"sub": spt.clientID,
|
||||
"jti": base64.URLEncoding.EncodeToString(jti),
|
||||
"nbf": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
}
|
||||
|
||||
signedString, err := token.SignedString(secret.PrivateKey)
|
||||
return signedString, err
|
||||
}
|
||||
|
||||
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
|
||||
// It will populate the form submitted during oAuth Token Acquisition using a JWT signed with a certificate.
|
||||
func (secret *ServicePrincipalCertificateSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
|
||||
jwt, err := secret.SignJwt(spt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.Set("client_assertion", jwt)
|
||||
v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServicePrincipalToken encapsulates a Token created for a Service Principal.
|
||||
type ServicePrincipalToken struct {
|
||||
Token
|
||||
|
||||
secret ServicePrincipalSecret
|
||||
oauthConfig OAuthConfig
|
||||
clientID string
|
||||
resource string
|
||||
autoRefresh bool
|
||||
refreshWithin time.Duration
|
||||
sender Sender
|
||||
|
||||
refreshCallbacks []TokenRefreshCallback
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenWithSecret create a ServicePrincipalToken using the supplied ServicePrincipalSecret implementation.
|
||||
func NewServicePrincipalTokenWithSecret(oauthConfig OAuthConfig, id string, resource string, secret ServicePrincipalSecret, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
spt := &ServicePrincipalToken{
|
||||
oauthConfig: oauthConfig,
|
||||
secret: secret,
|
||||
clientID: id,
|
||||
resource: resource,
|
||||
autoRefresh: true,
|
||||
refreshWithin: defaultRefresh,
|
||||
sender: &http.Client{},
|
||||
refreshCallbacks: callbacks,
|
||||
}
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenFromManualToken creates a ServicePrincipalToken using the supplied token
|
||||
func NewServicePrincipalTokenFromManualToken(oauthConfig OAuthConfig, clientID string, resource string, token Token, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
spt, err := NewServicePrincipalTokenWithSecret(
|
||||
oauthConfig,
|
||||
clientID,
|
||||
resource,
|
||||
&ServicePrincipalNoSecret{},
|
||||
callbacks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spt.Token = token
|
||||
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
// NewServicePrincipalToken creates a ServicePrincipalToken from the supplied Service Principal
|
||||
// credentials scoped to the named resource.
|
||||
func NewServicePrincipalToken(oauthConfig OAuthConfig, clientID string, secret string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
return NewServicePrincipalTokenWithSecret(
|
||||
oauthConfig,
|
||||
clientID,
|
||||
resource,
|
||||
&ServicePrincipalTokenSecret{
|
||||
ClientSecret: secret,
|
||||
},
|
||||
callbacks...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenFromCertificate create a ServicePrincipalToken from the supplied pkcs12 bytes.
|
||||
func NewServicePrincipalTokenFromCertificate(oauthConfig OAuthConfig, clientID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
return NewServicePrincipalTokenWithSecret(
|
||||
oauthConfig,
|
||||
clientID,
|
||||
resource,
|
||||
&ServicePrincipalCertificateSecret{
|
||||
PrivateKey: privateKey,
|
||||
Certificate: certificate,
|
||||
},
|
||||
callbacks...,
|
||||
)
|
||||
}
|
||||
|
||||
// GetMSIVMEndpoint gets the MSI endpoint on Virtual Machines.
|
||||
func GetMSIVMEndpoint() (string, error) {
|
||||
return getMSIVMEndpoint(msiPath)
|
||||
}
|
||||
|
||||
func getMSIVMEndpoint(path string) (string, error) {
|
||||
// Read MSI settings
|
||||
bytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
msiSettings := struct {
|
||||
URL string `json:"url"`
|
||||
}{}
|
||||
err = json.Unmarshal(bytes, &msiSettings)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return msiSettings.URL, nil
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenFromMSI creates a ServicePrincipalToken via the MSI VM Extension.
|
||||
func NewServicePrincipalTokenFromMSI(msiEndpoint, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
// We set the oauth config token endpoint to be MSI's endpoint
|
||||
msiEndpointURL, err := url.Parse(msiEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oauthConfig, err := NewOAuthConfig(msiEndpointURL.String(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spt := &ServicePrincipalToken{
|
||||
oauthConfig: *oauthConfig,
|
||||
secret: &ServicePrincipalMSISecret{},
|
||||
resource: resource,
|
||||
autoRefresh: true,
|
||||
refreshWithin: defaultRefresh,
|
||||
sender: &http.Client{},
|
||||
refreshCallbacks: callbacks,
|
||||
}
|
||||
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
// EnsureFresh will refresh the token if it will expire within the refresh window (as set by
|
||||
// RefreshWithin) and autoRefresh flag is on.
|
||||
func (spt *ServicePrincipalToken) EnsureFresh() error {
|
||||
if spt.autoRefresh && spt.WillExpireIn(spt.refreshWithin) {
|
||||
return spt.Refresh()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InvokeRefreshCallbacks calls any TokenRefreshCallbacks that were added to the SPT during initialization
|
||||
func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error {
|
||||
if spt.refreshCallbacks != nil {
|
||||
for _, callback := range spt.refreshCallbacks {
|
||||
err := callback(spt.Token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: TokenRefreshCallback handler failed. Error = '%v'", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Refresh obtains a fresh token for the Service Principal.
|
||||
func (spt *ServicePrincipalToken) Refresh() error {
|
||||
return spt.refreshInternal(spt.resource)
|
||||
}
|
||||
|
||||
// RefreshExchange refreshes the token, but for a different resource.
|
||||
func (spt *ServicePrincipalToken) RefreshExchange(resource string) error {
|
||||
return spt.refreshInternal(resource)
|
||||
}
|
||||
|
||||
func (spt *ServicePrincipalToken) refreshInternal(resource string) error {
|
||||
v := url.Values{}
|
||||
v.Set("client_id", spt.clientID)
|
||||
v.Set("resource", resource)
|
||||
|
||||
if spt.RefreshToken != "" {
|
||||
v.Set("grant_type", OAuthGrantTypeRefreshToken)
|
||||
v.Set("refresh_token", spt.RefreshToken)
|
||||
} else {
|
||||
v.Set("grant_type", OAuthGrantTypeClientCredentials)
|
||||
err := spt.secret.SetAuthenticationValues(spt, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s := v.Encode()
|
||||
body := ioutil.NopCloser(strings.NewReader(s))
|
||||
req, err := http.NewRequest(http.MethodPost, spt.oauthConfig.TokenEndpoint.String(), body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err)
|
||||
}
|
||||
|
||||
req.ContentLength = int64(len(s))
|
||||
req.Header.Set(contentType, mimeTypeFormPost)
|
||||
if _, ok := spt.secret.(*ServicePrincipalMSISecret); ok {
|
||||
req.Header.Set(metadataHeader, "true")
|
||||
}
|
||||
resp, err := spt.sender.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: Failed to execute the refresh request. Error = '%v'", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
rb, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: Refresh request failed. Status Code = '%d'. Failed reading response body", resp.StatusCode)
|
||||
}
|
||||
return fmt.Errorf("adal: Refresh request failed. Status Code = '%d'. Response body: %s", resp.StatusCode, string(rb))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: Failed to read a new service principal token during refresh. Error = '%v'", err)
|
||||
}
|
||||
if len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return fmt.Errorf("adal: Empty service principal token received during refresh")
|
||||
}
|
||||
var token Token
|
||||
err = json.Unmarshal(rb, &token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: Failed to unmarshal the service principal token during refresh. Error = '%v' JSON = '%s'", err, string(rb))
|
||||
}
|
||||
|
||||
spt.Token = token
|
||||
|
||||
return spt.InvokeRefreshCallbacks(token)
|
||||
}
|
||||
|
||||
// SetAutoRefresh enables or disables automatic refreshing of stale tokens.
|
||||
func (spt *ServicePrincipalToken) SetAutoRefresh(autoRefresh bool) {
|
||||
spt.autoRefresh = autoRefresh
|
||||
}
|
||||
|
||||
// SetRefreshWithin sets the interval within which if the token will expire, EnsureFresh will
|
||||
// refresh the token.
|
||||
func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) {
|
||||
spt.refreshWithin = d
|
||||
return
|
||||
}
|
||||
|
||||
// SetSender sets the http.Client used when obtaining the Service Principal token. An
|
||||
// undecorated http.Client is used by default.
|
||||
func (spt *ServicePrincipalToken) SetSender(s Sender) { spt.sender = s }
|
||||
654
vendor/github.com/Azure/go-autorest/autorest/adal/token_test.go
generated
vendored
Normal file
654
vendor/github.com/Azure/go-autorest/autorest/adal/token_test.go
generated
vendored
Normal file
@@ -0,0 +1,654 @@
|
||||
package adal
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/date"
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultFormData = "client_id=id&client_secret=secret&grant_type=client_credentials&resource=resource"
|
||||
defaultManualFormData = "client_id=id&grant_type=refresh_token&refresh_token=refreshtoken&resource=resource"
|
||||
)
|
||||
|
||||
func TestTokenExpires(t *testing.T) {
|
||||
tt := time.Now().Add(5 * time.Second)
|
||||
tk := newTokenExpiresAt(tt)
|
||||
|
||||
if tk.Expires().Equal(tt) {
|
||||
t.Fatalf("adal: Token#Expires miscalculated expiration time -- received %v, expected %v", tk.Expires(), tt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenIsExpired(t *testing.T) {
|
||||
tk := newTokenExpiresAt(time.Now().Add(-5 * time.Second))
|
||||
|
||||
if !tk.IsExpired() {
|
||||
t.Fatalf("adal: Token#IsExpired failed to mark a stale token as expired -- now %v, token expires at %v",
|
||||
time.Now().UTC(), tk.Expires())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenIsExpiredUninitialized(t *testing.T) {
|
||||
tk := &Token{}
|
||||
|
||||
if !tk.IsExpired() {
|
||||
t.Fatalf("adal: An uninitialized Token failed to mark itself as expired (expiration time %v)", tk.Expires())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenIsNoExpired(t *testing.T) {
|
||||
tk := newTokenExpiresAt(time.Now().Add(1000 * time.Second))
|
||||
|
||||
if tk.IsExpired() {
|
||||
t.Fatalf("adal: Token marked a fresh token as expired -- now %v, token expires at %v", time.Now().UTC(), tk.Expires())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenWillExpireIn(t *testing.T) {
|
||||
d := 5 * time.Second
|
||||
tk := newTokenExpiresIn(d)
|
||||
|
||||
if !tk.WillExpireIn(d) {
|
||||
t.Fatal("adal: Token#WillExpireIn mismeasured expiration time")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenSetAutoRefresh(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
if !spt.autoRefresh {
|
||||
t.Fatal("adal: ServicePrincipalToken did not default to automatic token refreshing")
|
||||
}
|
||||
|
||||
spt.SetAutoRefresh(false)
|
||||
if spt.autoRefresh {
|
||||
t.Fatal("adal: ServicePrincipalToken#SetAutoRefresh did not disable automatic token refreshing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenSetRefreshWithin(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
if spt.refreshWithin != defaultRefresh {
|
||||
t.Fatal("adal: ServicePrincipalToken did not correctly set the default refresh interval")
|
||||
}
|
||||
|
||||
spt.SetRefreshWithin(2 * defaultRefresh)
|
||||
if spt.refreshWithin != 2*defaultRefresh {
|
||||
t.Fatal("adal: ServicePrincipalToken#SetRefreshWithin did not set the refresh interval")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenSetSender(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
c := &http.Client{}
|
||||
spt.SetSender(c)
|
||||
if !reflect.DeepEqual(c, spt.sender) {
|
||||
t.Fatal("adal: ServicePrincipalToken#SetSender did not set the sender")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshUsesPOST(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
body := mocks.NewBody(newTokenJSON("test", "test"))
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")
|
||||
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if r.Method != "POST" {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set HTTP method -- expected %v, received %v", "POST", r.Method)
|
||||
}
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.Refresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("the response was not closed!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenFromMSIRefreshUsesPOST(t *testing.T) {
|
||||
resource := "https://resource"
|
||||
cb := func(token Token) error { return nil }
|
||||
|
||||
spt, err := NewServicePrincipalTokenFromMSI("http://msiendpoint/", resource, cb)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get MSI SPT: %v", err)
|
||||
}
|
||||
|
||||
body := mocks.NewBody(newTokenJSON("test", "test"))
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")
|
||||
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if r.Method != "POST" {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set HTTP method -- expected %v, received %v", "POST", r.Method)
|
||||
}
|
||||
if h := r.Header.Get("Metadata"); h != "true" {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set Metadata header for MSI")
|
||||
}
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err = spt.Refresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
|
||||
if body.IsOpen() {
|
||||
t.Fatalf("the response was not closed!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshSetsMimeType(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
body := mocks.NewBody(newTokenJSON("test", "test"))
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")
|
||||
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if r.Header.Get(http.CanonicalHeaderKey("Content-Type")) != "application/x-www-form-urlencoded" {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set Content-Type -- expected %v, received %v",
|
||||
"application/x-form-urlencoded",
|
||||
r.Header.Get(http.CanonicalHeaderKey("Content-Type")))
|
||||
}
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.Refresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshSetsURL(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
body := mocks.NewBody(newTokenJSON("test", "test"))
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")
|
||||
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if r.URL.String() != TestOAuthConfig.TokenEndpoint.String() {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set the URL -- expected %v, received %v",
|
||||
TestOAuthConfig.TokenEndpoint, r.URL)
|
||||
}
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.Refresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testServicePrincipalTokenRefreshSetsBody(t *testing.T, spt *ServicePrincipalToken, f func(*testing.T, []byte)) {
|
||||
body := mocks.NewBody(newTokenJSON("test", "test"))
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")
|
||||
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("adal: Failed to read body of Service Principal token request (%v)", err)
|
||||
}
|
||||
f(t, b)
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.Refresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenManualRefreshSetsBody(t *testing.T) {
|
||||
sptManual := newServicePrincipalTokenManual()
|
||||
testServicePrincipalTokenRefreshSetsBody(t, sptManual, func(t *testing.T, b []byte) {
|
||||
if string(b) != defaultManualFormData {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set the HTTP Request Body -- expected %v, received %v",
|
||||
defaultManualFormData, string(b))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenCertficateRefreshSetsBody(t *testing.T) {
|
||||
sptCert := newServicePrincipalTokenCertificate(t)
|
||||
testServicePrincipalTokenRefreshSetsBody(t, sptCert, func(t *testing.T, b []byte) {
|
||||
body := string(b)
|
||||
|
||||
values, _ := url.ParseQuery(body)
|
||||
if values["client_assertion_type"][0] != "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" ||
|
||||
values["client_id"][0] != "id" ||
|
||||
values["grant_type"][0] != "client_credentials" ||
|
||||
values["resource"][0] != "resource" {
|
||||
t.Fatalf("adal: ServicePrincipalTokenCertificate#Refresh did not correctly set the HTTP Request Body.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenSecretRefreshSetsBody(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
testServicePrincipalTokenRefreshSetsBody(t, spt, func(t *testing.T, b []byte) {
|
||||
if string(b) != defaultFormData {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set the HTTP Request Body -- expected %v, received %v",
|
||||
defaultFormData, string(b))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshClosesRequestBody(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
body := mocks.NewBody(newTokenJSON("test", "test"))
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")
|
||||
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.Refresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
if resp.Body.(*mocks.Body).IsOpen() {
|
||||
t.Fatal("adal: ServicePrincipalToken#Refresh failed to close the HTTP Response Body")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshRejectsResponsesWithStatusNotOK(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
body := mocks.NewBody(newTokenJSON("test", "test"))
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusUnauthorized, "Unauthorized")
|
||||
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.Refresh()
|
||||
if err == nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh should reject a response with status != %d", http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshRejectsEmptyBody(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return mocks.NewResponse(), nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.Refresh()
|
||||
if err == nil {
|
||||
t.Fatal("adal: ServicePrincipalToken#Refresh should reject an empty token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshPropagatesErrors(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
c := mocks.NewSender()
|
||||
c.SetError(fmt.Errorf("Faux Error"))
|
||||
spt.SetSender(c)
|
||||
|
||||
err := spt.Refresh()
|
||||
if err == nil {
|
||||
t.Fatal("adal: Failed to propagate the request error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshReturnsErrorIfNotOk(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
c := mocks.NewSender()
|
||||
c.AppendResponse(mocks.NewResponseWithStatus("401 NotAuthorized", http.StatusUnauthorized))
|
||||
spt.SetSender(c)
|
||||
|
||||
err := spt.Refresh()
|
||||
if err == nil {
|
||||
t.Fatalf("adal: Failed to return an when receiving a status code other than HTTP %d", http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenRefreshUnmarshals(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
|
||||
expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(date.UnixEpoch()).Seconds()))
|
||||
j := newTokenJSON(expiresOn, "resource")
|
||||
resp := mocks.NewResponseWithContent(j)
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
|
||||
err := spt.Refresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err)
|
||||
} else if spt.AccessToken != "accessToken" ||
|
||||
spt.ExpiresIn != "3600" ||
|
||||
spt.ExpiresOn != expiresOn ||
|
||||
spt.NotBefore != expiresOn ||
|
||||
spt.Resource != "resource" ||
|
||||
spt.Type != "Bearer" {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh failed correctly unmarshal the JSON -- expected %v, received %v",
|
||||
j, *spt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenEnsureFreshRefreshes(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
expireToken(&spt.Token)
|
||||
|
||||
body := mocks.NewBody(newTokenJSON("test", "test"))
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")
|
||||
|
||||
f := false
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
f = true
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.EnsureFresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#EnsureFresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
if !f {
|
||||
t.Fatal("adal: ServicePrincipalToken#EnsureFresh failed to call Refresh for stale token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenEnsureFreshSkipsIfFresh(t *testing.T) {
|
||||
spt := newServicePrincipalToken()
|
||||
setTokenToExpireIn(&spt.Token, 1000*time.Second)
|
||||
|
||||
f := false
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
f = true
|
||||
return mocks.NewResponse(), nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
err := spt.EnsureFresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#EnsureFresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
if f {
|
||||
t.Fatal("adal: ServicePrincipalToken#EnsureFresh invoked Refresh for fresh token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshCallback(t *testing.T) {
|
||||
callbackTriggered := false
|
||||
spt := newServicePrincipalToken(func(Token) error {
|
||||
callbackTriggered = true
|
||||
return nil
|
||||
})
|
||||
|
||||
expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(date.UnixEpoch()).Seconds()))
|
||||
|
||||
sender := mocks.NewSender()
|
||||
j := newTokenJSON(expiresOn, "resource")
|
||||
sender.AppendResponse(mocks.NewResponseWithContent(j))
|
||||
spt.SetSender(sender)
|
||||
err := spt.Refresh()
|
||||
if err != nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err)
|
||||
}
|
||||
if !callbackTriggered {
|
||||
t.Fatalf("adal: RefreshCallback failed to trigger call callback")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshCallbackErrorPropagates(t *testing.T) {
|
||||
errorText := "this is an error text"
|
||||
spt := newServicePrincipalToken(func(Token) error {
|
||||
return fmt.Errorf(errorText)
|
||||
})
|
||||
|
||||
expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(date.UnixEpoch()).Seconds()))
|
||||
|
||||
sender := mocks.NewSender()
|
||||
j := newTokenJSON(expiresOn, "resource")
|
||||
sender.AppendResponse(mocks.NewResponseWithContent(j))
|
||||
spt.SetSender(sender)
|
||||
err := spt.Refresh()
|
||||
|
||||
if err == nil || !strings.Contains(err.Error(), errorText) {
|
||||
t.Fatalf("adal: RefreshCallback failed to propagate error")
|
||||
}
|
||||
}
|
||||
|
||||
// This demonstrates the danger of manual token without a refresh token
|
||||
func TestServicePrincipalTokenManualRefreshFailsWithoutRefresh(t *testing.T) {
|
||||
spt := newServicePrincipalTokenManual()
|
||||
spt.RefreshToken = ""
|
||||
err := spt.Refresh()
|
||||
if err == nil {
|
||||
t.Fatalf("adal: ServicePrincipalToken#Refresh should have failed with a ManualTokenSecret without a refresh token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewServicePrincipalTokenFromMSI(t *testing.T) {
|
||||
resource := "https://resource"
|
||||
cb := func(token Token) error { return nil }
|
||||
|
||||
spt, err := NewServicePrincipalTokenFromMSI("http://msiendpoint/", resource, cb)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get MSI SPT: %v", err)
|
||||
}
|
||||
|
||||
// check some of the SPT fields
|
||||
if _, ok := spt.secret.(*ServicePrincipalMSISecret); !ok {
|
||||
t.Fatal("SPT secret was not of MSI type")
|
||||
}
|
||||
|
||||
if spt.resource != resource {
|
||||
t.Fatal("SPT came back with incorrect resource")
|
||||
}
|
||||
|
||||
if len(spt.refreshCallbacks) != 1 {
|
||||
t.Fatal("SPT had incorrect refresh callbacks.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVMEndpoint(t *testing.T) {
|
||||
tempSettingsFile, err := ioutil.TempFile("", "ManagedIdentity-Settings")
|
||||
if err != nil {
|
||||
t.Fatal("Couldn't write temp settings file")
|
||||
}
|
||||
defer os.Remove(tempSettingsFile.Name())
|
||||
|
||||
settingsContents := []byte(`{
|
||||
"url": "http://msiendpoint/"
|
||||
}`)
|
||||
|
||||
if _, err := tempSettingsFile.Write(settingsContents); err != nil {
|
||||
t.Fatal("Couldn't fill temp settings file")
|
||||
}
|
||||
|
||||
endpoint, err := getMSIVMEndpoint(tempSettingsFile.Name())
|
||||
if err != nil {
|
||||
t.Fatal("Coudn't get VM endpoint")
|
||||
}
|
||||
|
||||
if endpoint != "http://msiendpoint/" {
|
||||
t.Fatal("Didn't get correct endpoint")
|
||||
}
|
||||
}
|
||||
|
||||
func newToken() *Token {
|
||||
return &Token{
|
||||
AccessToken: "ASECRETVALUE",
|
||||
Resource: "https://azure.microsoft.com/",
|
||||
Type: "Bearer",
|
||||
}
|
||||
}
|
||||
|
||||
func newTokenJSON(expiresOn string, resource string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"access_token" : "accessToken",
|
||||
"expires_in" : "3600",
|
||||
"expires_on" : "%s",
|
||||
"not_before" : "%s",
|
||||
"resource" : "%s",
|
||||
"token_type" : "Bearer"
|
||||
}`,
|
||||
expiresOn, expiresOn, resource)
|
||||
}
|
||||
|
||||
func newTokenExpiresIn(expireIn time.Duration) *Token {
|
||||
return setTokenToExpireIn(newToken(), expireIn)
|
||||
}
|
||||
|
||||
func newTokenExpiresAt(expireAt time.Time) *Token {
|
||||
return setTokenToExpireAt(newToken(), expireAt)
|
||||
}
|
||||
|
||||
func expireToken(t *Token) *Token {
|
||||
return setTokenToExpireIn(t, 0)
|
||||
}
|
||||
|
||||
func setTokenToExpireAt(t *Token, expireAt time.Time) *Token {
|
||||
t.ExpiresIn = "3600"
|
||||
t.ExpiresOn = strconv.Itoa(int(expireAt.Sub(date.UnixEpoch()).Seconds()))
|
||||
t.NotBefore = t.ExpiresOn
|
||||
return t
|
||||
}
|
||||
|
||||
func setTokenToExpireIn(t *Token, expireIn time.Duration) *Token {
|
||||
return setTokenToExpireAt(t, time.Now().Add(expireIn))
|
||||
}
|
||||
|
||||
func newServicePrincipalToken(callbacks ...TokenRefreshCallback) *ServicePrincipalToken {
|
||||
spt, _ := NewServicePrincipalToken(TestOAuthConfig, "id", "secret", "resource", callbacks...)
|
||||
return spt
|
||||
}
|
||||
|
||||
func newServicePrincipalTokenManual() *ServicePrincipalToken {
|
||||
token := newToken()
|
||||
token.RefreshToken = "refreshtoken"
|
||||
spt, _ := NewServicePrincipalTokenFromManualToken(TestOAuthConfig, "id", "resource", *token)
|
||||
return spt
|
||||
}
|
||||
|
||||
func newServicePrincipalTokenCertificate(t *testing.T) *ServicePrincipalToken {
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(0),
|
||||
Subject: pkix.Name{CommonName: "test"},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
certificateBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
certificate, err := x509.ParseCertificate(certificateBytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
spt, _ := NewServicePrincipalTokenFromCertificate(TestOAuthConfig, "id", certificate, privateKey, "resource")
|
||||
return spt
|
||||
}
|
||||
181
vendor/github.com/Azure/go-autorest/autorest/authorization.go
generated
vendored
Normal file
181
vendor/github.com/Azure/go-autorest/autorest/authorization.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
)
|
||||
|
||||
const (
|
||||
bearerChallengeHeader = "Www-Authenticate"
|
||||
bearer = "Bearer"
|
||||
tenantID = "tenantID"
|
||||
)
|
||||
|
||||
// Authorizer is the interface that provides a PrepareDecorator used to supply request
|
||||
// authorization. Most often, the Authorizer decorator runs last so it has access to the full
|
||||
// state of the formed HTTP request.
|
||||
type Authorizer interface {
|
||||
WithAuthorization() PrepareDecorator
|
||||
}
|
||||
|
||||
// NullAuthorizer implements a default, "do nothing" Authorizer.
|
||||
type NullAuthorizer struct{}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that does nothing.
|
||||
func (na NullAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return WithNothing()
|
||||
}
|
||||
|
||||
// BearerAuthorizer implements the bearer authorization
|
||||
type BearerAuthorizer struct {
|
||||
tokenProvider adal.OAuthTokenProvider
|
||||
}
|
||||
|
||||
// NewBearerAuthorizer crates a BearerAuthorizer using the given token provider
|
||||
func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer {
|
||||
return &BearerAuthorizer{tokenProvider: tp}
|
||||
}
|
||||
|
||||
func (ba *BearerAuthorizer) withBearerAuthorization() PrepareDecorator {
|
||||
return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken()))
|
||||
}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
|
||||
// value is "Bearer " followed by the token.
|
||||
//
|
||||
// By default, the token will be automatically refreshed through the Refresher interface.
|
||||
func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
refresher, ok := ba.tokenProvider.(adal.Refresher)
|
||||
if ok {
|
||||
err := refresher.EnsureFresh()
|
||||
if err != nil {
|
||||
return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", nil,
|
||||
"Failed to refresh the Token for request to %s", r.URL)
|
||||
}
|
||||
}
|
||||
return (ba.withBearerAuthorization()(p)).Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BearerAuthorizerCallbackFunc is the authentication callback signature.
|
||||
type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
|
||||
|
||||
// BearerAuthorizerCallback implements bearer authorization via a callback.
|
||||
type BearerAuthorizerCallback struct {
|
||||
sender Sender
|
||||
callback BearerAuthorizerCallbackFunc
|
||||
}
|
||||
|
||||
// NewBearerAuthorizerCallback creates a bearer authorization callback. The callback
|
||||
// is invoked when the HTTP request is submitted.
|
||||
func NewBearerAuthorizerCallback(sender Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback {
|
||||
if sender == nil {
|
||||
sender = &http.Client{}
|
||||
}
|
||||
return &BearerAuthorizerCallback{sender: sender, callback: callback}
|
||||
}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value
|
||||
// is "Bearer " followed by the token. The BearerAuthorizer is obtained via a user-supplied callback.
|
||||
//
|
||||
// By default, the token will be automatically refreshed through the Refresher interface.
|
||||
func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
// make a copy of the request and remove the body as it's not
|
||||
// required and avoids us having to create a copy of it.
|
||||
rCopy := *r
|
||||
removeRequestBody(&rCopy)
|
||||
|
||||
resp, err := bacb.sender.Do(&rCopy)
|
||||
if err == nil && resp.StatusCode == 401 {
|
||||
defer resp.Body.Close()
|
||||
if hasBearerChallenge(resp) {
|
||||
bc, err := newBearerChallenge(resp)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
if bacb.callback != nil {
|
||||
ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"])
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
return ba.WithAuthorization()(p).Prepare(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if the HTTP response contains a bearer challenge
|
||||
func hasBearerChallenge(resp *http.Response) bool {
|
||||
authHeader := resp.Header.Get(bearerChallengeHeader)
|
||||
if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type bearerChallenge struct {
|
||||
values map[string]string
|
||||
}
|
||||
|
||||
func newBearerChallenge(resp *http.Response) (bc bearerChallenge, err error) {
|
||||
challenge := strings.TrimSpace(resp.Header.Get(bearerChallengeHeader))
|
||||
trimmedChallenge := challenge[len(bearer)+1:]
|
||||
|
||||
// challenge is a set of key=value pairs that are comma delimited
|
||||
pairs := strings.Split(trimmedChallenge, ",")
|
||||
if len(pairs) < 1 {
|
||||
err = fmt.Errorf("challenge '%s' contains no pairs", challenge)
|
||||
return bc, err
|
||||
}
|
||||
|
||||
bc.values = make(map[string]string)
|
||||
for i := range pairs {
|
||||
trimmedPair := strings.TrimSpace(pairs[i])
|
||||
pair := strings.Split(trimmedPair, "=")
|
||||
if len(pair) == 2 {
|
||||
// remove the enclosing quotes
|
||||
key := strings.Trim(pair[0], "\"")
|
||||
value := strings.Trim(pair[1], "\"")
|
||||
|
||||
switch key {
|
||||
case "authorization", "authorization_uri":
|
||||
// strip the tenant ID from the authorization URL
|
||||
asURL, err := url.Parse(value)
|
||||
if err != nil {
|
||||
return bc, err
|
||||
}
|
||||
bc.values[tenantID] = asURL.Path[1:]
|
||||
default:
|
||||
bc.values[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bc, err
|
||||
}
|
||||
188
vendor/github.com/Azure/go-autorest/autorest/authorization_test.go
generated
vendored
Normal file
188
vendor/github.com/Azure/go-autorest/autorest/authorization_test.go
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
const (
|
||||
TestTenantID = "TestTenantID"
|
||||
TestActiveDirectoryEndpoint = "https://login/test.com/"
|
||||
)
|
||||
|
||||
func TestWithAuthorizer(t *testing.T) {
|
||||
r1 := mocks.NewRequest()
|
||||
|
||||
na := &NullAuthorizer{}
|
||||
r2, err := Prepare(r1,
|
||||
na.WithAuthorization())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: NullAuthorizer#WithAuthorization returned an unexpected error (%v)", err)
|
||||
} else if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatalf("autorest: NullAuthorizer#WithAuthorization modified the request -- received %v, expected %v", r2, r1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenWithAuthorization(t *testing.T) {
|
||||
token := &adal.Token{
|
||||
AccessToken: "TestToken",
|
||||
Resource: "https://azure.microsoft.com/",
|
||||
Type: "Bearer",
|
||||
}
|
||||
|
||||
ba := NewBearerAuthorizer(token)
|
||||
req, err := Prepare(&http.Request{}, ba.WithAuthorization())
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
} else if req.Header.Get(http.CanonicalHeaderKey("Authorization")) != fmt.Sprintf("Bearer %s", token.AccessToken) {
|
||||
t.Fatal("azure: BearerAuthorizer#WithAuthorization failed to set Authorization header")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenWithAuthorizationNoRefresh(t *testing.T) {
|
||||
oauthConfig, err := adal.NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
}
|
||||
spt, err := adal.NewServicePrincipalToken(*oauthConfig, "id", "secret", "resource", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
}
|
||||
spt.SetAutoRefresh(false)
|
||||
s := mocks.NewSender()
|
||||
spt.SetSender(s)
|
||||
|
||||
ba := NewBearerAuthorizer(spt)
|
||||
req, err := Prepare(mocks.NewRequest(), ba.WithAuthorization())
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
} else if req.Header.Get(http.CanonicalHeaderKey("Authorization")) != fmt.Sprintf("Bearer %s", spt.AccessToken) {
|
||||
t.Fatal("azure: BearerAuthorizer#WithAuthorization failed to set Authorization header")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenWithAuthorizationRefresh(t *testing.T) {
|
||||
|
||||
oauthConfig, err := adal.NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
}
|
||||
refreshed := false
|
||||
spt, err := adal.NewServicePrincipalToken(*oauthConfig, "id", "secret", "resource", func(t adal.Token) error {
|
||||
refreshed = true
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
}
|
||||
|
||||
jwt := `{
|
||||
"access_token" : "accessToken",
|
||||
"expires_in" : "3600",
|
||||
"expires_on" : "test",
|
||||
"not_before" : "test",
|
||||
"resource" : "test",
|
||||
"token_type" : "Bearer"
|
||||
}`
|
||||
body := mocks.NewBody(jwt)
|
||||
resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")
|
||||
c := mocks.NewSender()
|
||||
s := DecorateSender(c,
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return resp, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
spt.SetSender(s)
|
||||
|
||||
ba := NewBearerAuthorizer(spt)
|
||||
req, err := Prepare(mocks.NewRequest(), ba.WithAuthorization())
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
} else if req.Header.Get(http.CanonicalHeaderKey("Authorization")) != fmt.Sprintf("Bearer %s", spt.AccessToken) {
|
||||
t.Fatal("azure: BearerAuthorizer#WithAuthorization failed to set Authorization header")
|
||||
}
|
||||
|
||||
if !refreshed {
|
||||
t.Fatal("azure: BearerAuthorizer#WithAuthorization must refresh the token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicePrincipalTokenWithAuthorizationReturnsErrorIfConnotRefresh(t *testing.T) {
|
||||
oauthConfig, err := adal.NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
}
|
||||
spt, err := adal.NewServicePrincipalToken(*oauthConfig, "id", "secret", "resource", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err)
|
||||
}
|
||||
|
||||
s := mocks.NewSender()
|
||||
s.AppendResponse(mocks.NewResponseWithStatus("400 Bad Request", http.StatusBadRequest))
|
||||
spt.SetSender(s)
|
||||
|
||||
ba := NewBearerAuthorizer(spt)
|
||||
_, err = Prepare(mocks.NewRequest(), ba.WithAuthorization())
|
||||
if err == nil {
|
||||
t.Fatal("azure: BearerAuthorizer#WithAuthorization failed to return an error when refresh fails")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBearerAuthorizerCallback(t *testing.T) {
|
||||
tenantString := "123-tenantID-456"
|
||||
resourceString := "https://fake.resource.net"
|
||||
|
||||
s := mocks.NewSender()
|
||||
resp := mocks.NewResponseWithStatus("401 Unauthorized", http.StatusUnauthorized)
|
||||
mocks.SetResponseHeader(resp, bearerChallengeHeader, bearer+" \"authorization\"=\"https://fake.net/"+tenantString+"\",\"resource\"=\""+resourceString+"\"")
|
||||
s.AppendResponse(resp)
|
||||
|
||||
auth := NewBearerAuthorizerCallback(s, func(tenantID, resource string) (*BearerAuthorizer, error) {
|
||||
if tenantID != tenantString {
|
||||
t.Fatal("BearerAuthorizerCallback: bad tenant ID")
|
||||
}
|
||||
if resource != resourceString {
|
||||
t.Fatal("BearerAuthorizerCallback: bad resource")
|
||||
}
|
||||
|
||||
oauthConfig, err := adal.NewOAuthConfig(TestActiveDirectoryEndpoint, tenantID)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: NewOAuthConfig returned an error (%v)", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalToken(*oauthConfig, "id", "secret", resource)
|
||||
if err != nil {
|
||||
t.Fatalf("azure: NewServicePrincipalToken returned an error (%v)", err)
|
||||
}
|
||||
|
||||
spt.SetSender(s)
|
||||
return NewBearerAuthorizer(spt), nil
|
||||
})
|
||||
|
||||
_, err := Prepare(mocks.NewRequest(), auth.WithAuthorization())
|
||||
if err == nil {
|
||||
t.Fatal("azure: BearerAuthorizerCallback#WithAuthorization failed to return an error when refresh fails")
|
||||
}
|
||||
}
|
||||
129
vendor/github.com/Azure/go-autorest/autorest/autorest.go
generated
vendored
Normal file
129
vendor/github.com/Azure/go-autorest/autorest/autorest.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines
|
||||
and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/)
|
||||
generated Go code.
|
||||
|
||||
The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending,
|
||||
and Responding. A typical pattern is:
|
||||
|
||||
req, err := Prepare(&http.Request{},
|
||||
token.WithAuthorization())
|
||||
|
||||
resp, err := Send(req,
|
||||
WithLogging(logger),
|
||||
DoErrorIfStatusCode(http.StatusInternalServerError),
|
||||
DoCloseIfError(),
|
||||
DoRetryForAttempts(5, time.Second))
|
||||
|
||||
err = Respond(resp,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
Each phase relies on decorators to modify and / or manage processing. Decorators may first modify
|
||||
and then pass the data along, pass the data first and then modify the result, or wrap themselves
|
||||
around passing the data (such as a logger might do). Decorators run in the order provided. For
|
||||
example, the following:
|
||||
|
||||
req, err := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
WithPath("a"),
|
||||
WithPath("b"),
|
||||
WithPath("c"))
|
||||
|
||||
will set the URL to:
|
||||
|
||||
https://microsoft.com/a/b/c
|
||||
|
||||
Preparers and Responders may be shared and re-used (assuming the underlying decorators support
|
||||
sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders
|
||||
shared among multiple go-routines, and a single Sender shared among multiple sending go-routines,
|
||||
all bound together by means of input / output channels.
|
||||
|
||||
Decorators hold their passed state within a closure (such as the path components in the example
|
||||
above). Be careful to share Preparers and Responders only in a context where such held state
|
||||
applies. For example, it may not make sense to share a Preparer that applies a query string from a
|
||||
fixed set of values. Similarly, sharing a Responder that reads the response body into a passed
|
||||
struct (e.g., ByUnmarshallingJson) is likely incorrect.
|
||||
|
||||
Lastly, the Swagger specification (https://swagger.io) that drives AutoRest
|
||||
(https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The
|
||||
github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure
|
||||
correct parsing and formatting.
|
||||
|
||||
Errors raised by autorest objects and methods will conform to the autorest.Error interface.
|
||||
|
||||
See the included examples for more detail. For details on the suggested use of this package by
|
||||
generated clients, see the Client described below.
|
||||
*/
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// HeaderLocation specifies the HTTP Location header.
|
||||
HeaderLocation = "Location"
|
||||
|
||||
// HeaderRetryAfter specifies the HTTP Retry-After header.
|
||||
HeaderRetryAfter = "Retry-After"
|
||||
)
|
||||
|
||||
// ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set
|
||||
// and false otherwise.
|
||||
func ResponseHasStatusCode(resp *http.Response, codes ...int) bool {
|
||||
return containsInt(codes, resp.StatusCode)
|
||||
}
|
||||
|
||||
// GetLocation retrieves the URL from the Location header of the passed response.
|
||||
func GetLocation(resp *http.Response) string {
|
||||
return resp.Header.Get(HeaderLocation)
|
||||
}
|
||||
|
||||
// GetRetryAfter extracts the retry delay from the Retry-After header of the passed response. If
|
||||
// the header is absent or is malformed, it will return the supplied default delay time.Duration.
|
||||
func GetRetryAfter(resp *http.Response, defaultDelay time.Duration) time.Duration {
|
||||
retry := resp.Header.Get(HeaderRetryAfter)
|
||||
if retry == "" {
|
||||
return defaultDelay
|
||||
}
|
||||
|
||||
d, err := time.ParseDuration(retry + "s")
|
||||
if err != nil {
|
||||
return defaultDelay
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// NewPollingRequest allocates and returns a new http.Request to poll for the passed response.
|
||||
func NewPollingRequest(resp *http.Response, cancel <-chan struct{}) (*http.Request, error) {
|
||||
location := GetLocation(resp)
|
||||
if location == "" {
|
||||
return nil, NewErrorWithResponse("autorest", "NewPollingRequest", resp, "Location header missing from response that requires polling")
|
||||
}
|
||||
|
||||
req, err := Prepare(&http.Request{Cancel: cancel},
|
||||
AsGet(),
|
||||
WithBaseURL(location))
|
||||
if err != nil {
|
||||
return nil, NewErrorWithError(err, "autorest", "NewPollingRequest", nil, "Failure creating poll request to %s", location)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
140
vendor/github.com/Azure/go-autorest/autorest/autorest_test.go
generated
vendored
Normal file
140
vendor/github.com/Azure/go-autorest/autorest/autorest_test.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
func TestResponseHasStatusCode(t *testing.T) {
|
||||
codes := []int{http.StatusOK, http.StatusAccepted}
|
||||
resp := &http.Response{StatusCode: http.StatusAccepted}
|
||||
if !ResponseHasStatusCode(resp, codes...) {
|
||||
t.Fatalf("autorest: ResponseHasStatusCode failed to find %v in %v", resp.StatusCode, codes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResponseHasStatusCodeNotPresent(t *testing.T) {
|
||||
codes := []int{http.StatusOK, http.StatusAccepted}
|
||||
resp := &http.Response{StatusCode: http.StatusInternalServerError}
|
||||
if ResponseHasStatusCode(resp, codes...) {
|
||||
t.Fatalf("autorest: ResponseHasStatusCode unexpectedly found %v in %v", resp.StatusCode, codes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPollingRequestDoesNotReturnARequestWhenLocationHeaderIsMissing(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("500 InternalServerError", http.StatusInternalServerError)
|
||||
|
||||
req, _ := NewPollingRequest(resp, nil)
|
||||
if req != nil {
|
||||
t.Fatal("autorest: NewPollingRequest returned an http.Request when the Location header was missing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPollingRequestReturnsAnErrorWhenPrepareFails(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(resp)
|
||||
resp.Header.Set(http.CanonicalHeaderKey(HeaderLocation), mocks.TestBadURL)
|
||||
|
||||
_, err := NewPollingRequest(resp, nil)
|
||||
if err == nil {
|
||||
t.Fatal("autorest: NewPollingRequest failed to return an error when Prepare fails")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPollingRequestDoesNotReturnARequestWhenPrepareFails(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(resp)
|
||||
resp.Header.Set(http.CanonicalHeaderKey(HeaderLocation), mocks.TestBadURL)
|
||||
|
||||
req, _ := NewPollingRequest(resp, nil)
|
||||
if req != nil {
|
||||
t.Fatal("autorest: NewPollingRequest returned an http.Request when Prepare failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPollingRequestReturnsAGetRequest(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(resp)
|
||||
|
||||
req, _ := NewPollingRequest(resp, nil)
|
||||
if req.Method != "GET" {
|
||||
t.Fatalf("autorest: NewPollingRequest did not create an HTTP GET request -- actual method %v", req.Method)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPollingRequestProvidesTheURL(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(resp)
|
||||
|
||||
req, _ := NewPollingRequest(resp, nil)
|
||||
if req.URL.String() != mocks.TestURL {
|
||||
t.Fatalf("autorest: NewPollingRequest did not create an HTTP with the expected URL -- received %v, expected %v", req.URL, mocks.TestURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLocation(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(resp)
|
||||
|
||||
l := GetLocation(resp)
|
||||
if len(l) == 0 {
|
||||
t.Fatalf("autorest: GetLocation failed to return Location header -- expected %v, received %v", mocks.TestURL, l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLocationReturnsEmptyStringForMissingLocation(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
|
||||
l := GetLocation(resp)
|
||||
if len(l) != 0 {
|
||||
t.Fatalf("autorest: GetLocation return a value without a Location header -- received %v", l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRetryAfter(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(resp)
|
||||
|
||||
d := GetRetryAfter(resp, DefaultPollingDelay)
|
||||
if d != mocks.TestDelay {
|
||||
t.Fatalf("autorest: GetRetryAfter failed to returned the expected delay -- expected %v, received %v", mocks.TestDelay, d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRetryAfterReturnsDefaultDelayIfRetryHeaderIsMissing(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
|
||||
d := GetRetryAfter(resp, DefaultPollingDelay)
|
||||
if d != DefaultPollingDelay {
|
||||
t.Fatalf("autorest: GetRetryAfter failed to returned the default delay for a missing Retry-After header -- expected %v, received %v",
|
||||
DefaultPollingDelay, d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRetryAfterReturnsDefaultDelayIfRetryHeaderIsMalformed(t *testing.T) {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(resp)
|
||||
resp.Header.Set(http.CanonicalHeaderKey(HeaderRetryAfter), "a very bad non-integer value")
|
||||
|
||||
d := GetRetryAfter(resp, DefaultPollingDelay)
|
||||
if d != DefaultPollingDelay {
|
||||
t.Fatalf("autorest: GetRetryAfter failed to returned the default delay for a malformed Retry-After header -- expected %v, received %v",
|
||||
DefaultPollingDelay, d)
|
||||
}
|
||||
}
|
||||
316
vendor/github.com/Azure/go-autorest/autorest/azure/async.go
generated
vendored
Normal file
316
vendor/github.com/Azure/go-autorest/autorest/azure/async.go
generated
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
package azure
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/date"
|
||||
)
|
||||
|
||||
const (
|
||||
headerAsyncOperation = "Azure-AsyncOperation"
|
||||
)
|
||||
|
||||
const (
|
||||
operationInProgress string = "InProgress"
|
||||
operationCanceled string = "Canceled"
|
||||
operationFailed string = "Failed"
|
||||
operationSucceeded string = "Succeeded"
|
||||
)
|
||||
|
||||
// DoPollForAsynchronous returns a SendDecorator that polls if the http.Response is for an Azure
|
||||
// long-running operation. It will delay between requests for the duration specified in the
|
||||
// RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by
|
||||
// closing the optional channel on the http.Request.
|
||||
func DoPollForAsynchronous(delay time.Duration) autorest.SendDecorator {
|
||||
return func(s autorest.Sender) autorest.Sender {
|
||||
return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
|
||||
resp, err = s.Do(r)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
pollingCodes := []int{http.StatusAccepted, http.StatusCreated, http.StatusOK}
|
||||
if !autorest.ResponseHasStatusCode(resp, pollingCodes...) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
ps := pollingState{}
|
||||
for err == nil {
|
||||
err = updatePollingState(resp, &ps)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if ps.hasTerminated() {
|
||||
if !ps.hasSucceeded() {
|
||||
err = ps
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
r, err = newPollingRequest(resp, ps)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
delay = autorest.GetRetryAfter(resp, delay)
|
||||
resp, err = autorest.SendWithSender(s, r,
|
||||
autorest.AfterDelay(delay))
|
||||
}
|
||||
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getAsyncOperation(resp *http.Response) string {
|
||||
return resp.Header.Get(http.CanonicalHeaderKey(headerAsyncOperation))
|
||||
}
|
||||
|
||||
func hasSucceeded(state string) bool {
|
||||
return state == operationSucceeded
|
||||
}
|
||||
|
||||
func hasTerminated(state string) bool {
|
||||
switch state {
|
||||
case operationCanceled, operationFailed, operationSucceeded:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func hasFailed(state string) bool {
|
||||
return state == operationFailed
|
||||
}
|
||||
|
||||
type provisioningTracker interface {
|
||||
state() string
|
||||
hasSucceeded() bool
|
||||
hasTerminated() bool
|
||||
}
|
||||
|
||||
type operationResource struct {
|
||||
// Note:
|
||||
// The specification states services should return the "id" field. However some return it as
|
||||
// "operationId".
|
||||
ID string `json:"id"`
|
||||
OperationID string `json:"operationId"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Properties map[string]interface{} `json:"properties"`
|
||||
OperationError ServiceError `json:"error"`
|
||||
StartTime date.Time `json:"startTime"`
|
||||
EndTime date.Time `json:"endTime"`
|
||||
PercentComplete float64 `json:"percentComplete"`
|
||||
}
|
||||
|
||||
func (or operationResource) state() string {
|
||||
return or.Status
|
||||
}
|
||||
|
||||
func (or operationResource) hasSucceeded() bool {
|
||||
return hasSucceeded(or.state())
|
||||
}
|
||||
|
||||
func (or operationResource) hasTerminated() bool {
|
||||
return hasTerminated(or.state())
|
||||
}
|
||||
|
||||
type provisioningProperties struct {
|
||||
ProvisioningState string `json:"provisioningState"`
|
||||
}
|
||||
|
||||
type provisioningStatus struct {
|
||||
Properties provisioningProperties `json:"properties,omitempty"`
|
||||
ProvisioningError ServiceError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (ps provisioningStatus) state() string {
|
||||
return ps.Properties.ProvisioningState
|
||||
}
|
||||
|
||||
func (ps provisioningStatus) hasSucceeded() bool {
|
||||
return hasSucceeded(ps.state())
|
||||
}
|
||||
|
||||
func (ps provisioningStatus) hasTerminated() bool {
|
||||
return hasTerminated(ps.state())
|
||||
}
|
||||
|
||||
func (ps provisioningStatus) hasProvisioningError() bool {
|
||||
return ps.ProvisioningError != ServiceError{}
|
||||
}
|
||||
|
||||
type pollingResponseFormat string
|
||||
|
||||
const (
|
||||
usesOperationResponse pollingResponseFormat = "OperationResponse"
|
||||
usesProvisioningStatus pollingResponseFormat = "ProvisioningStatus"
|
||||
formatIsUnknown pollingResponseFormat = ""
|
||||
)
|
||||
|
||||
type pollingState struct {
|
||||
responseFormat pollingResponseFormat
|
||||
uri string
|
||||
state string
|
||||
code string
|
||||
message string
|
||||
}
|
||||
|
||||
func (ps pollingState) hasSucceeded() bool {
|
||||
return hasSucceeded(ps.state)
|
||||
}
|
||||
|
||||
func (ps pollingState) hasTerminated() bool {
|
||||
return hasTerminated(ps.state)
|
||||
}
|
||||
|
||||
func (ps pollingState) hasFailed() bool {
|
||||
return hasFailed(ps.state)
|
||||
}
|
||||
|
||||
func (ps pollingState) Error() string {
|
||||
return fmt.Sprintf("Long running operation terminated with status '%s': Code=%q Message=%q", ps.state, ps.code, ps.message)
|
||||
}
|
||||
|
||||
// updatePollingState maps the operation status -- retrieved from either a provisioningState
|
||||
// field, the status field of an OperationResource, or inferred from the HTTP status code --
|
||||
// into a well-known states. Since the process begins from the initial request, the state
|
||||
// always comes from either a the provisioningState returned or is inferred from the HTTP
|
||||
// status code. Subsequent requests will read an Azure OperationResource object if the
|
||||
// service initially returned the Azure-AsyncOperation header. The responseFormat field notes
|
||||
// the expected response format.
|
||||
func updatePollingState(resp *http.Response, ps *pollingState) error {
|
||||
// Determine the response shape
|
||||
// -- The first response will always be a provisioningStatus response; only the polling requests,
|
||||
// depending on the header returned, may be something otherwise.
|
||||
var pt provisioningTracker
|
||||
if ps.responseFormat == usesOperationResponse {
|
||||
pt = &operationResource{}
|
||||
} else {
|
||||
pt = &provisioningStatus{}
|
||||
}
|
||||
|
||||
// If this is the first request (that is, the polling response shape is unknown), determine how
|
||||
// to poll and what to expect
|
||||
if ps.responseFormat == formatIsUnknown {
|
||||
req := resp.Request
|
||||
if req == nil {
|
||||
return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Original HTTP request is missing")
|
||||
}
|
||||
|
||||
// Prefer the Azure-AsyncOperation header
|
||||
ps.uri = getAsyncOperation(resp)
|
||||
if ps.uri != "" {
|
||||
ps.responseFormat = usesOperationResponse
|
||||
} else {
|
||||
ps.responseFormat = usesProvisioningStatus
|
||||
}
|
||||
|
||||
// Else, use the Location header
|
||||
if ps.uri == "" {
|
||||
ps.uri = autorest.GetLocation(resp)
|
||||
}
|
||||
|
||||
// Lastly, requests against an existing resource, use the last request URI
|
||||
if ps.uri == "" {
|
||||
m := strings.ToUpper(req.Method)
|
||||
if m == http.MethodPatch || m == http.MethodPut || m == http.MethodGet {
|
||||
ps.uri = req.URL.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read and interpret the response (saving the Body in case no polling is necessary)
|
||||
b := &bytes.Buffer{}
|
||||
err := autorest.Respond(resp,
|
||||
autorest.ByCopying(b),
|
||||
autorest.ByUnmarshallingJSON(pt),
|
||||
autorest.ByClosing())
|
||||
resp.Body = ioutil.NopCloser(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Interpret the results
|
||||
// -- Terminal states apply regardless
|
||||
// -- Unknown states are per-service inprogress states
|
||||
// -- Otherwise, infer state from HTTP status code
|
||||
if pt.hasTerminated() {
|
||||
ps.state = pt.state()
|
||||
} else if pt.state() != "" {
|
||||
ps.state = operationInProgress
|
||||
} else {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusAccepted:
|
||||
ps.state = operationInProgress
|
||||
|
||||
case http.StatusNoContent, http.StatusCreated, http.StatusOK:
|
||||
ps.state = operationSucceeded
|
||||
|
||||
default:
|
||||
ps.state = operationFailed
|
||||
}
|
||||
}
|
||||
|
||||
if ps.state == operationInProgress && ps.uri == "" {
|
||||
return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Unable to obtain polling URI for %s %s", resp.Request.Method, resp.Request.URL)
|
||||
}
|
||||
|
||||
// For failed operation, check for error code and message in
|
||||
// -- Operation resource
|
||||
// -- Response
|
||||
// -- Otherwise, Unknown
|
||||
if ps.hasFailed() {
|
||||
if ps.responseFormat == usesOperationResponse {
|
||||
or := pt.(*operationResource)
|
||||
ps.code = or.OperationError.Code
|
||||
ps.message = or.OperationError.Message
|
||||
} else {
|
||||
p := pt.(*provisioningStatus)
|
||||
if p.hasProvisioningError() {
|
||||
ps.code = p.ProvisioningError.Code
|
||||
ps.message = p.ProvisioningError.Message
|
||||
} else {
|
||||
ps.code = "Unknown"
|
||||
ps.message = "None"
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPollingRequest(resp *http.Response, ps pollingState) (*http.Request, error) {
|
||||
req := resp.Request
|
||||
if req == nil {
|
||||
return nil, autorest.NewError("azure", "newPollingRequest", "Azure Polling Error - Original HTTP request is missing")
|
||||
}
|
||||
|
||||
reqPoll, err := autorest.Prepare(&http.Request{Cancel: req.Cancel},
|
||||
autorest.AsGet(),
|
||||
autorest.WithBaseURL(ps.uri))
|
||||
if err != nil {
|
||||
return nil, autorest.NewErrorWithError(err, "azure", "newPollingRequest", nil, "Failure creating poll request to %s", ps.uri)
|
||||
}
|
||||
|
||||
return reqPoll, nil
|
||||
}
|
||||
1130
vendor/github.com/Azure/go-autorest/autorest/azure/async_test.go
generated
vendored
Normal file
1130
vendor/github.com/Azure/go-autorest/autorest/azure/async_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
129
vendor/github.com/Azure/go-autorest/autorest/azure/auth/authfile.go
generated
vendored
Normal file
129
vendor/github.com/Azure/go-autorest/autorest/azure/auth/authfile.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/dimchansky/utfbom"
|
||||
)
|
||||
|
||||
// ClientSetup includes authentication details and cloud specific
|
||||
// parameters for ARM clients
|
||||
type ClientSetup struct {
|
||||
*autorest.BearerAuthorizer
|
||||
File
|
||||
BaseURI string
|
||||
}
|
||||
|
||||
// File represents the authentication file
|
||||
type File struct {
|
||||
ClientID string `json:"clientId,omitempty"`
|
||||
ClientSecret string `json:"clientSecret,omitempty"`
|
||||
SubscriptionID string `json:"subscriptionId,omitempty"`
|
||||
TenantID string `json:"tenantId,omitempty"`
|
||||
ActiveDirectoryEndpoint string `json:"activeDirectoryEndpointUrl,omitempty"`
|
||||
ResourceManagerEndpoint string `json:"resourceManagerEndpointUrl,omitempty"`
|
||||
GraphResourceID string `json:"activeDirectoryGraphResourceId,omitempty"`
|
||||
SQLManagementEndpoint string `json:"sqlManagementEndpointUrl,omitempty"`
|
||||
GalleryEndpoint string `json:"galleryEndpointUrl,omitempty"`
|
||||
ManagementEndpoint string `json:"managementEndpointUrl,omitempty"`
|
||||
}
|
||||
|
||||
// GetClientSetup provides an authorizer, base URI, subscriptionID and
|
||||
// tenantID parameters from an Azure CLI auth file
|
||||
func GetClientSetup(baseURI string) (auth ClientSetup, err error) {
|
||||
fileLocation := os.Getenv("AZURE_AUTH_LOCATION")
|
||||
if fileLocation == "" {
|
||||
return auth, errors.New("auth file not found. Environment variable AZURE_AUTH_LOCATION is not set")
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadFile(fileLocation)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Auth file might be encoded
|
||||
decoded, err := decode(contents)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(decoded, &auth.File)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resource, err := getResourceForToken(auth.File, baseURI)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
auth.BaseURI = resource
|
||||
|
||||
config, err := adal.NewOAuthConfig(auth.ActiveDirectoryEndpoint, auth.TenantID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
spToken, err := adal.NewServicePrincipalToken(*config, auth.ClientID, auth.ClientSecret, resource)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
auth.BearerAuthorizer = autorest.NewBearerAuthorizer(spToken)
|
||||
return
|
||||
}
|
||||
|
||||
func decode(b []byte) ([]byte, error) {
|
||||
reader, enc := utfbom.Skip(bytes.NewReader(b))
|
||||
|
||||
switch enc {
|
||||
case utfbom.UTF16LittleEndian:
|
||||
u16 := make([]uint16, (len(b)/2)-1)
|
||||
err := binary.Read(reader, binary.LittleEndian, &u16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(string(utf16.Decode(u16))), nil
|
||||
case utfbom.UTF16BigEndian:
|
||||
u16 := make([]uint16, (len(b)/2)-1)
|
||||
err := binary.Read(reader, binary.BigEndian, &u16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(string(utf16.Decode(u16))), nil
|
||||
}
|
||||
return ioutil.ReadAll(reader)
|
||||
}
|
||||
|
||||
func getResourceForToken(f File, baseURI string) (string, error) {
|
||||
// Compare dafault base URI from the SDK to the endpoints from the public cloud
|
||||
// Base URI and token resource are the same string. This func finds the authentication
|
||||
// file field that matches the SDK base URI. The SDK defines the public cloud
|
||||
// endpoint as its default base URI
|
||||
if !strings.HasSuffix(baseURI, "/") {
|
||||
baseURI += "/"
|
||||
}
|
||||
switch baseURI {
|
||||
case azure.PublicCloud.ServiceManagementEndpoint:
|
||||
return f.ManagementEndpoint, nil
|
||||
case azure.PublicCloud.ResourceManagerEndpoint:
|
||||
return f.ResourceManagerEndpoint, nil
|
||||
case azure.PublicCloud.ActiveDirectoryEndpoint:
|
||||
return f.ActiveDirectoryEndpoint, nil
|
||||
case azure.PublicCloud.GalleryEndpoint:
|
||||
return f.GalleryEndpoint, nil
|
||||
case azure.PublicCloud.GraphEndpoint:
|
||||
return f.GraphResourceID, nil
|
||||
}
|
||||
return "", fmt.Errorf("auth: base URI not found in endpoints")
|
||||
}
|
||||
97
vendor/github.com/Azure/go-autorest/autorest/azure/auth/authfile_test.go
generated
vendored
Normal file
97
vendor/github.com/Azure/go-autorest/autorest/azure/auth/authfile_test.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
expectedFile = File{
|
||||
ClientID: "client-id-123",
|
||||
ClientSecret: "client-secret-456",
|
||||
SubscriptionID: "sub-id-789",
|
||||
TenantID: "tenant-id-123",
|
||||
ActiveDirectoryEndpoint: "https://login.microsoftonline.com",
|
||||
ResourceManagerEndpoint: "https://management.azure.com/",
|
||||
GraphResourceID: "https://graph.windows.net/",
|
||||
SQLManagementEndpoint: "https://management.core.windows.net:8443/",
|
||||
GalleryEndpoint: "https://gallery.azure.com/",
|
||||
ManagementEndpoint: "https://management.core.windows.net/",
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetClientSetup(t *testing.T) {
|
||||
os.Setenv("AZURE_AUTH_LOCATION", filepath.Join(getCredsPath(), "credsutf16le.json"))
|
||||
setup, err := GetClientSetup("https://management.azure.com")
|
||||
if err != nil {
|
||||
t.Logf("GetClientSetup failed, got error %v", err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if setup.BaseURI != "https://management.azure.com/" {
|
||||
t.Logf("auth.BaseURI not set correctly, expected 'https://management.azure.com/', got '%s'", setup.BaseURI)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedFile, setup.File) {
|
||||
t.Logf("auth.File not set correctly, expected %v, got %v", expectedFile, setup.File)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if setup.BearerAuthorizer == nil {
|
||||
t.Log("auth.Authorizer not set correctly, got nil")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeAndUnmarshal(t *testing.T) {
|
||||
tests := []string{
|
||||
"credsutf8.json",
|
||||
"credsutf16le.json",
|
||||
"credsutf16be.json",
|
||||
}
|
||||
creds := getCredsPath()
|
||||
for _, test := range tests {
|
||||
b, err := ioutil.ReadFile(filepath.Join(creds, test))
|
||||
if err != nil {
|
||||
t.Logf("error reading file '%s': %s", test, err)
|
||||
t.Fail()
|
||||
}
|
||||
decoded, err := decode(b)
|
||||
if err != nil {
|
||||
t.Logf("error decoding file '%s': %s", test, err)
|
||||
t.Fail()
|
||||
}
|
||||
var got File
|
||||
err = json.Unmarshal(decoded, &got)
|
||||
if err != nil {
|
||||
t.Logf("error unmarshaling file '%s': %s", test, err)
|
||||
t.Fail()
|
||||
}
|
||||
if !reflect.DeepEqual(expectedFile, got) {
|
||||
t.Logf("unmarshaled map expected %v, got %v", expectedFile, got)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getCredsPath() string {
|
||||
gopath := os.Getenv("GOPATH")
|
||||
return filepath.Join(gopath, "src", "github.com", "Azure", "go-autorest", "testdata")
|
||||
}
|
||||
|
||||
func areMapsEqual(a, b map[string]string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for k := range a {
|
||||
if a[k] != b[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
200
vendor/github.com/Azure/go-autorest/autorest/azure/azure.go
generated
vendored
Normal file
200
vendor/github.com/Azure/go-autorest/autorest/azure/azure.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
Package azure provides Azure-specific implementations used with AutoRest.
|
||||
|
||||
See the included examples for more detail.
|
||||
*/
|
||||
package azure
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
)
|
||||
|
||||
const (
|
||||
// HeaderClientID is the Azure extension header to set a user-specified request ID.
|
||||
HeaderClientID = "x-ms-client-request-id"
|
||||
|
||||
// HeaderReturnClientID is the Azure extension header to set if the user-specified request ID
|
||||
// should be included in the response.
|
||||
HeaderReturnClientID = "x-ms-return-client-request-id"
|
||||
|
||||
// HeaderRequestID is the Azure extension header of the service generated request ID returned
|
||||
// in the response.
|
||||
HeaderRequestID = "x-ms-request-id"
|
||||
)
|
||||
|
||||
// ServiceError encapsulates the error response from an Azure service.
|
||||
type ServiceError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Details *[]interface{} `json:"details"`
|
||||
}
|
||||
|
||||
func (se ServiceError) Error() string {
|
||||
if se.Details != nil {
|
||||
d, err := json.Marshal(*(se.Details))
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, *se.Details)
|
||||
}
|
||||
return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, string(d))
|
||||
}
|
||||
return fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message)
|
||||
}
|
||||
|
||||
// RequestError describes an error response returned by Azure service.
|
||||
type RequestError struct {
|
||||
autorest.DetailedError
|
||||
|
||||
// The error returned by the Azure service.
|
||||
ServiceError *ServiceError `json:"error"`
|
||||
|
||||
// The request id (from the x-ms-request-id-header) of the request.
|
||||
RequestID string
|
||||
}
|
||||
|
||||
// Error returns a human-friendly error message from service error.
|
||||
func (e RequestError) Error() string {
|
||||
return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v",
|
||||
e.StatusCode, e.ServiceError)
|
||||
}
|
||||
|
||||
// IsAzureError returns true if the passed error is an Azure Service error; false otherwise.
|
||||
func IsAzureError(e error) bool {
|
||||
_, ok := e.(*RequestError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// NewErrorWithError creates a new Error conforming object from the
|
||||
// passed packageType, method, statusCode of the given resp (UndefinedStatusCode
|
||||
// if resp is nil), message, and original error. message is treated as a format
|
||||
// string to which the optional args apply.
|
||||
func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError {
|
||||
if v, ok := original.(*RequestError); ok {
|
||||
return *v
|
||||
}
|
||||
|
||||
statusCode := autorest.UndefinedStatusCode
|
||||
if resp != nil {
|
||||
statusCode = resp.StatusCode
|
||||
}
|
||||
return RequestError{
|
||||
DetailedError: autorest.DetailedError{
|
||||
Original: original,
|
||||
PackageType: packageType,
|
||||
Method: method,
|
||||
StatusCode: statusCode,
|
||||
Message: fmt.Sprintf(message, args...),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of
|
||||
// x-ms-client-request-id whose value is the passed, undecorated UUID (e.g.,
|
||||
// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id
|
||||
// header to true such that UUID accompanies the http.Response.
|
||||
func WithReturningClientID(uuid string) autorest.PrepareDecorator {
|
||||
preparer := autorest.CreatePreparer(
|
||||
WithClientID(uuid),
|
||||
WithReturnClientID(true))
|
||||
|
||||
return func(p autorest.Preparer) autorest.Preparer {
|
||||
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
return preparer.Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithClientID returns a PrepareDecorator that adds an HTTP extension header of
|
||||
// x-ms-client-request-id whose value is passed, undecorated UUID (e.g.,
|
||||
// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA").
|
||||
func WithClientID(uuid string) autorest.PrepareDecorator {
|
||||
return autorest.WithHeader(HeaderClientID, uuid)
|
||||
}
|
||||
|
||||
// WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of
|
||||
// x-ms-return-client-request-id whose boolean value indicates if the value of the
|
||||
// x-ms-client-request-id header should be included in the http.Response.
|
||||
func WithReturnClientID(b bool) autorest.PrepareDecorator {
|
||||
return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b))
|
||||
}
|
||||
|
||||
// ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the
|
||||
// http.Request sent to the service (and returned in the http.Response)
|
||||
func ExtractClientID(resp *http.Response) string {
|
||||
return autorest.ExtractHeaderValue(HeaderClientID, resp)
|
||||
}
|
||||
|
||||
// ExtractRequestID extracts the Azure server generated request identifier from the
|
||||
// x-ms-request-id header.
|
||||
func ExtractRequestID(resp *http.Response) string {
|
||||
return autorest.ExtractHeaderValue(HeaderRequestID, resp)
|
||||
}
|
||||
|
||||
// WithErrorUnlessStatusCode returns a RespondDecorator that emits an
|
||||
// azure.RequestError by reading the response body unless the response HTTP status code
|
||||
// is among the set passed.
|
||||
//
|
||||
// If there is a chance service may return responses other than the Azure error
|
||||
// format and the response cannot be parsed into an error, a decoding error will
|
||||
// be returned containing the response body. In any case, the Responder will
|
||||
// return an error if the status code is not satisfied.
|
||||
//
|
||||
// If this Responder returns an error, the response body will be replaced with
|
||||
// an in-memory reader, which needs no further closing.
|
||||
func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
|
||||
return func(r autorest.Responder) autorest.Responder {
|
||||
return autorest.ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
|
||||
var e RequestError
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Copy and replace the Body in case it does not contain an error object.
|
||||
// This will leave the Body available to the caller.
|
||||
b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e)
|
||||
resp.Body = ioutil.NopCloser(&b)
|
||||
if decodeErr != nil {
|
||||
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
|
||||
} else if e.ServiceError == nil {
|
||||
// Check if error is unwrapped ServiceError
|
||||
if err := json.Unmarshal(b.Bytes(), &e.ServiceError); err != nil || e.ServiceError.Message == "" {
|
||||
e.ServiceError = &ServiceError{
|
||||
Code: "Unknown",
|
||||
Message: "Unknown service error",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e.RequestID = ExtractRequestID(resp)
|
||||
if e.StatusCode == nil {
|
||||
e.StatusCode = resp.StatusCode
|
||||
}
|
||||
err = &e
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
513
vendor/github.com/Azure/go-autorest/autorest/azure/azure_test.go
generated
vendored
Normal file
513
vendor/github.com/Azure/go-autorest/autorest/azure/azure_test.go
generated
vendored
Normal file
@@ -0,0 +1,513 @@
|
||||
package azure
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
const (
|
||||
headerAuthorization = "Authorization"
|
||||
longDelay = 5 * time.Second
|
||||
retryDelay = 10 * time.Millisecond
|
||||
testLogPrefix = "azure:"
|
||||
)
|
||||
|
||||
// Use a Client Inspector to set the request identifier.
|
||||
func ExampleWithClientID() {
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
req, _ := autorest.Prepare(&http.Request{},
|
||||
autorest.AsGet(),
|
||||
autorest.WithBaseURL("https://microsoft.com/a/b/c/"))
|
||||
|
||||
c := autorest.Client{Sender: mocks.NewSender()}
|
||||
c.RequestInspector = WithReturningClientID(uuid)
|
||||
|
||||
autorest.SendWithSender(c, req)
|
||||
fmt.Printf("Inspector added the %s header with the value %s\n",
|
||||
HeaderClientID, req.Header.Get(HeaderClientID))
|
||||
fmt.Printf("Inspector added the %s header with the value %s\n",
|
||||
HeaderReturnClientID, req.Header.Get(HeaderReturnClientID))
|
||||
// Output:
|
||||
// Inspector added the x-ms-client-request-id header with the value 71FDB9F4-5E49-4C12-B266-DE7B4FD999A6
|
||||
// Inspector added the x-ms-return-client-request-id header with the value true
|
||||
}
|
||||
|
||||
func TestWithReturningClientIDReturnsError(t *testing.T) {
|
||||
var errIn error
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
_, errOut := autorest.Prepare(&http.Request{},
|
||||
withErrorPrepareDecorator(&errIn),
|
||||
WithReturningClientID(uuid))
|
||||
|
||||
if errOut == nil || errIn != errOut {
|
||||
t.Fatalf("azure: WithReturningClientID failed to exit early when receiving an error -- expected (%v), received (%v)",
|
||||
errIn, errOut)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithClientID(t *testing.T) {
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
req, _ := autorest.Prepare(&http.Request{},
|
||||
WithClientID(uuid))
|
||||
|
||||
if req.Header.Get(HeaderClientID) != uuid {
|
||||
t.Fatalf("azure: WithClientID failed to set %s -- expected %s, received %s",
|
||||
HeaderClientID, uuid, req.Header.Get(HeaderClientID))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithReturnClientID(t *testing.T) {
|
||||
b := false
|
||||
req, _ := autorest.Prepare(&http.Request{},
|
||||
WithReturnClientID(b))
|
||||
|
||||
if req.Header.Get(HeaderReturnClientID) != strconv.FormatBool(b) {
|
||||
t.Fatalf("azure: WithReturnClientID failed to set %s -- expected %s, received %s",
|
||||
HeaderClientID, strconv.FormatBool(b), req.Header.Get(HeaderClientID))
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractClientID(t *testing.T) {
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
resp := mocks.NewResponse()
|
||||
mocks.SetResponseHeader(resp, HeaderClientID, uuid)
|
||||
|
||||
if ExtractClientID(resp) != uuid {
|
||||
t.Fatalf("azure: ExtractClientID failed to extract the %s -- expected %s, received %s",
|
||||
HeaderClientID, uuid, ExtractClientID(resp))
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractRequestID(t *testing.T) {
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
resp := mocks.NewResponse()
|
||||
mocks.SetResponseHeader(resp, HeaderRequestID, uuid)
|
||||
|
||||
if ExtractRequestID(resp) != uuid {
|
||||
t.Fatalf("azure: ExtractRequestID failed to extract the %s -- expected %s, received %s",
|
||||
HeaderRequestID, uuid, ExtractRequestID(resp))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAzureError_ReturnsTrueForAzureError(t *testing.T) {
|
||||
if !IsAzureError(&RequestError{}) {
|
||||
t.Fatalf("azure: IsAzureError failed to return true for an Azure Service error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAzureError_ReturnsFalseForNonAzureError(t *testing.T) {
|
||||
if IsAzureError(fmt.Errorf("An Error")) {
|
||||
t.Fatalf("azure: IsAzureError return true for an non-Azure Service error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_UsesReponseStatusCode(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("Error"), "packageType", "method", mocks.NewResponseWithStatus("Forbidden", http.StatusForbidden), "message")
|
||||
if e.StatusCode != http.StatusForbidden {
|
||||
t.Fatalf("azure: NewErrorWithError failed to use the Status Code of the passed Response -- expected %v, received %v", http.StatusForbidden, e.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_ReturnsUnwrappedError(t *testing.T) {
|
||||
e1 := RequestError{}
|
||||
e1.ServiceError = &ServiceError{Code: "42", Message: "A Message"}
|
||||
e1.StatusCode = 200
|
||||
e1.RequestID = "A RequestID"
|
||||
e2 := NewErrorWithError(&e1, "packageType", "method", nil, "message")
|
||||
|
||||
if !reflect.DeepEqual(e1, e2) {
|
||||
t.Fatalf("azure: NewErrorWithError wrapped an RequestError -- expected %T, received %T", e1, e2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_WrapsAnError(t *testing.T) {
|
||||
e1 := fmt.Errorf("Inner Error")
|
||||
var e2 interface{} = NewErrorWithError(e1, "packageType", "method", nil, "message")
|
||||
|
||||
if _, ok := e2.(RequestError); !ok {
|
||||
t.Fatalf("azure: NewErrorWithError failed to wrap a standard error -- received %T", e2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessStatusCode_NotAnAzureError(t *testing.T) {
|
||||
body := `<html>
|
||||
<head>
|
||||
<title>IIS Error page</title>
|
||||
</head>
|
||||
<body>Some non-JSON error page</body>
|
||||
</html>`
|
||||
r := mocks.NewResponseWithContent(body)
|
||||
r.Request = mocks.NewRequest()
|
||||
r.StatusCode = http.StatusBadRequest
|
||||
r.Status = http.StatusText(r.StatusCode)
|
||||
|
||||
err := autorest.Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByClosing())
|
||||
ok, _ := err.(*RequestError)
|
||||
if ok != nil {
|
||||
t.Fatalf("azure: azure.RequestError returned from malformed response: %v", err)
|
||||
}
|
||||
|
||||
// the error body should still be there
|
||||
defer r.Body.Close()
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(b) != body {
|
||||
t.Fatalf("response body is wrong. got=%q exptected=%q", string(b), body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessStatusCode_FoundAzureErrorWithoutDetails(t *testing.T) {
|
||||
j := `{
|
||||
"error": {
|
||||
"code": "InternalError",
|
||||
"message": "Azure is having trouble right now."
|
||||
}
|
||||
}`
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
r := mocks.NewResponseWithContent(j)
|
||||
mocks.SetResponseHeader(r, HeaderRequestID, uuid)
|
||||
r.Request = mocks.NewRequest()
|
||||
r.StatusCode = http.StatusInternalServerError
|
||||
r.Status = http.StatusText(r.StatusCode)
|
||||
|
||||
err := autorest.Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByClosing())
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("azure: returned nil error for proper error response")
|
||||
}
|
||||
azErr, ok := err.(*RequestError)
|
||||
if !ok {
|
||||
t.Fatalf("azure: returned error is not azure.RequestError: %T", err)
|
||||
}
|
||||
|
||||
expected := "autorest/azure: Service returned an error. Status=500 Code=\"InternalError\" Message=\"Azure is having trouble right now.\""
|
||||
if !reflect.DeepEqual(expected, azErr.Error()) {
|
||||
t.Fatalf("azure: service error is not unmarshaled properly.\nexpected=%v\ngot=%v", expected, azErr.Error())
|
||||
}
|
||||
|
||||
if expected := http.StatusInternalServerError; azErr.StatusCode != expected {
|
||||
t.Fatalf("azure: got wrong StatusCode=%d Expected=%d", azErr.StatusCode, expected)
|
||||
}
|
||||
if expected := uuid; azErr.RequestID != expected {
|
||||
t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID)
|
||||
}
|
||||
|
||||
_ = azErr.Error()
|
||||
|
||||
// the error body should still be there
|
||||
defer r.Body.Close()
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(b) != j {
|
||||
t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessStatusCode_FoundAzureErrorWithDetails(t *testing.T) {
|
||||
j := `{
|
||||
"error": {
|
||||
"code": "InternalError",
|
||||
"message": "Azure is having trouble right now.",
|
||||
"details": [{"code": "conflict1", "message":"error message1"},
|
||||
{"code": "conflict2", "message":"error message2"}]
|
||||
}
|
||||
}`
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
r := mocks.NewResponseWithContent(j)
|
||||
mocks.SetResponseHeader(r, HeaderRequestID, uuid)
|
||||
r.Request = mocks.NewRequest()
|
||||
r.StatusCode = http.StatusInternalServerError
|
||||
r.Status = http.StatusText(r.StatusCode)
|
||||
|
||||
err := autorest.Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByClosing())
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("azure: returned nil error for proper error response")
|
||||
}
|
||||
azErr, ok := err.(*RequestError)
|
||||
if !ok {
|
||||
t.Fatalf("azure: returned error is not azure.RequestError: %T", err)
|
||||
}
|
||||
|
||||
if expected := "InternalError"; azErr.ServiceError.Code != expected {
|
||||
t.Fatalf("azure: wrong error code. expected=%q; got=%q", expected, azErr.ServiceError.Code)
|
||||
}
|
||||
if azErr.ServiceError.Message == "" {
|
||||
t.Fatalf("azure: error message is not unmarshaled properly")
|
||||
}
|
||||
b, _ := json.Marshal(*azErr.ServiceError.Details)
|
||||
if string(b) != `[{"code":"conflict1","message":"error message1"},{"code":"conflict2","message":"error message2"}]` {
|
||||
t.Fatalf("azure: error details is not unmarshaled properly")
|
||||
}
|
||||
|
||||
if expected := http.StatusInternalServerError; azErr.StatusCode != expected {
|
||||
t.Fatalf("azure: got wrong StatusCode=%v Expected=%d", azErr.StatusCode, expected)
|
||||
}
|
||||
if expected := uuid; azErr.RequestID != expected {
|
||||
t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID)
|
||||
}
|
||||
|
||||
_ = azErr.Error()
|
||||
|
||||
// the error body should still be there
|
||||
defer r.Body.Close()
|
||||
b, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(b) != j {
|
||||
t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessStatusCode_NoAzureError(t *testing.T) {
|
||||
j := `{
|
||||
"Status":"NotFound"
|
||||
}`
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
r := mocks.NewResponseWithContent(j)
|
||||
mocks.SetResponseHeader(r, HeaderRequestID, uuid)
|
||||
r.Request = mocks.NewRequest()
|
||||
r.StatusCode = http.StatusInternalServerError
|
||||
r.Status = http.StatusText(r.StatusCode)
|
||||
|
||||
err := autorest.Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByClosing())
|
||||
if err == nil {
|
||||
t.Fatalf("azure: returned nil error for proper error response")
|
||||
}
|
||||
azErr, ok := err.(*RequestError)
|
||||
if !ok {
|
||||
t.Fatalf("azure: returned error is not azure.RequestError: %T", err)
|
||||
}
|
||||
|
||||
expected := &ServiceError{
|
||||
Code: "Unknown",
|
||||
Message: "Unknown service error",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, azErr.ServiceError) {
|
||||
t.Fatalf("azure: service error is not unmarshaled properly. expected=%q\ngot=%q", expected, azErr.ServiceError)
|
||||
}
|
||||
|
||||
if expected := http.StatusInternalServerError; azErr.StatusCode != expected {
|
||||
t.Fatalf("azure: got wrong StatusCode=%v Expected=%d", azErr.StatusCode, expected)
|
||||
}
|
||||
if expected := uuid; azErr.RequestID != expected {
|
||||
t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID)
|
||||
}
|
||||
|
||||
_ = azErr.Error()
|
||||
|
||||
// the error body should still be there
|
||||
defer r.Body.Close()
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(b) != j {
|
||||
t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessStatusCode_UnwrappedError(t *testing.T) {
|
||||
j := `{
|
||||
"target": null,
|
||||
"code": "InternalError",
|
||||
"message": "Azure is having trouble right now.",
|
||||
"details": [{"code": "conflict1", "message":"error message1"},
|
||||
{"code": "conflict2", "message":"error message2"}],
|
||||
"innererror": []
|
||||
}`
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
r := mocks.NewResponseWithContent(j)
|
||||
mocks.SetResponseHeader(r, HeaderRequestID, uuid)
|
||||
r.Request = mocks.NewRequest()
|
||||
r.StatusCode = http.StatusInternalServerError
|
||||
r.Status = http.StatusText(r.StatusCode)
|
||||
|
||||
err := autorest.Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByClosing())
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("azure: returned nil error for proper error response")
|
||||
}
|
||||
|
||||
azErr, ok := err.(*RequestError)
|
||||
if !ok {
|
||||
t.Fatalf("returned error is not azure.RequestError: %T", err)
|
||||
}
|
||||
|
||||
if expected := http.StatusInternalServerError; azErr.StatusCode != expected {
|
||||
t.Logf("Incorrect StatusCode got: %v want: %d", azErr.StatusCode, expected)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if expected := "Azure is having trouble right now."; azErr.ServiceError.Message != expected {
|
||||
t.Logf("Incorrect Message\n\tgot: %q\n\twant: %q", azErr.Message, expected)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if expected := uuid; azErr.RequestID != expected {
|
||||
t.Logf("Incorrect request ID\n\tgot: %q\n\twant: %q", azErr.RequestID, expected)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expectedServiceErrorDetails := `[{"code":"conflict1","message":"error message1"},{"code":"conflict2","message":"error message2"}]`
|
||||
if azErr.ServiceError == nil {
|
||||
t.Logf("`ServiceError` was nil when it shouldn't have been.")
|
||||
t.Fail()
|
||||
} else if azErr.ServiceError.Details == nil {
|
||||
t.Logf("`ServiceError.Details` was nil when it should have been %q", expectedServiceErrorDetails)
|
||||
t.Fail()
|
||||
} else if details, _ := json.Marshal(*azErr.ServiceError.Details); expectedServiceErrorDetails != string(details) {
|
||||
t.Logf("Error detaisl was not unmarshaled properly.\n\tgot: %q\n\twant: %q", string(details), expectedServiceErrorDetails)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
// the error body should still be there
|
||||
defer r.Body.Close()
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(b) != j {
|
||||
t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRequestErrorString_WithError(t *testing.T) {
|
||||
j := `{
|
||||
"error": {
|
||||
"code": "InternalError",
|
||||
"message": "Conflict",
|
||||
"details": [{"code": "conflict1", "message":"error message1"}]
|
||||
}
|
||||
}`
|
||||
uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
|
||||
r := mocks.NewResponseWithContent(j)
|
||||
mocks.SetResponseHeader(r, HeaderRequestID, uuid)
|
||||
r.Request = mocks.NewRequest()
|
||||
r.StatusCode = http.StatusInternalServerError
|
||||
r.Status = http.StatusText(r.StatusCode)
|
||||
|
||||
err := autorest.Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByClosing())
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("azure: returned nil error for proper error response")
|
||||
}
|
||||
azErr, _ := err.(*RequestError)
|
||||
expected := "autorest/azure: Service returned an error. Status=500 Code=\"InternalError\" Message=\"Conflict\" Details=[{\"code\":\"conflict1\",\"message\":\"error message1\"}]"
|
||||
if expected != azErr.Error() {
|
||||
t.Fatalf("azure: send wrong RequestError.\nexpected=%v\ngot=%v", expected, azErr.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func withErrorPrepareDecorator(e *error) autorest.PrepareDecorator {
|
||||
return func(p autorest.Preparer) autorest.Preparer {
|
||||
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
*e = fmt.Errorf("azure: Faux Prepare Error")
|
||||
return r, *e
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func withAsyncResponseDecorator(n int) autorest.SendDecorator {
|
||||
i := 0
|
||||
return func(s autorest.Sender) autorest.Sender {
|
||||
return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := s.Do(r)
|
||||
if err == nil {
|
||||
if i < n {
|
||||
resp.StatusCode = http.StatusCreated
|
||||
resp.Header = http.Header{}
|
||||
resp.Header.Add(http.CanonicalHeaderKey(headerAsyncOperation), mocks.TestURL)
|
||||
i++
|
||||
} else {
|
||||
resp.StatusCode = http.StatusOK
|
||||
resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation))
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockAuthorizer struct{}
|
||||
|
||||
func (ma mockAuthorizer) WithAuthorization() autorest.PrepareDecorator {
|
||||
return autorest.WithHeader(headerAuthorization, mocks.TestAuthorizationHeader)
|
||||
}
|
||||
|
||||
type mockFailingAuthorizer struct{}
|
||||
|
||||
func (mfa mockFailingAuthorizer) WithAuthorization() autorest.PrepareDecorator {
|
||||
return func(p autorest.Preparer) autorest.Preparer {
|
||||
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
return r, fmt.Errorf("ERROR: mockFailingAuthorizer returned expected error")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockInspector struct {
|
||||
wasInvoked bool
|
||||
}
|
||||
|
||||
func (mi *mockInspector) WithInspection() autorest.PrepareDecorator {
|
||||
return func(p autorest.Preparer) autorest.Preparer {
|
||||
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
mi.wasInvoked = true
|
||||
return p.Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (mi *mockInspector) ByInspecting() autorest.RespondDecorator {
|
||||
return func(r autorest.Responder) autorest.Responder {
|
||||
return autorest.ResponderFunc(func(resp *http.Response) error {
|
||||
mi.wasInvoked = true
|
||||
return r.Respond(resp)
|
||||
})
|
||||
}
|
||||
}
|
||||
65
vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go
generated
vendored
Normal file
65
vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package cli
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/dimchansky/utfbom"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// Profile represents a Profile from the Azure CLI
|
||||
type Profile struct {
|
||||
InstallationID string `json:"installationId"`
|
||||
Subscriptions []Subscription `json:"subscriptions"`
|
||||
}
|
||||
|
||||
// Subscription represents a Subscription from the Azure CLI
|
||||
type Subscription struct {
|
||||
EnvironmentName string `json:"environmentName"`
|
||||
ID string `json:"id"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
Name string `json:"name"`
|
||||
State string `json:"state"`
|
||||
TenantID string `json:"tenantId"`
|
||||
}
|
||||
|
||||
// ProfilePath returns the path where the Azure Profile is stored from the Azure CLI
|
||||
func ProfilePath() (string, error) {
|
||||
return homedir.Expand("~/.azure/azureProfile.json")
|
||||
}
|
||||
|
||||
// LoadProfile restores a Profile object from a file located at 'path'.
|
||||
func LoadProfile(path string) (result Profile, err error) {
|
||||
var contents []byte
|
||||
contents, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to open file (%s) while loading token: %v", path, err)
|
||||
return
|
||||
}
|
||||
reader := utfbom.SkipOnly(bytes.NewReader(contents))
|
||||
|
||||
dec := json.NewDecoder(reader)
|
||||
if err = dec.Decode(&result); err != nil {
|
||||
err = fmt.Errorf("failed to decode contents of file (%s) into a Profile representation: %v", path, err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
103
vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go
generated
vendored
Normal file
103
vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package cli
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/date"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// Token represents an AccessToken from the Azure CLI
|
||||
type Token struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
Authority string `json:"_authority"`
|
||||
ClientID string `json:"_clientId"`
|
||||
ExpiresOn string `json:"expiresOn"`
|
||||
IdentityProvider string `json:"identityProvider"`
|
||||
IsMRRT bool `json:"isMRRT"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
Resource string `json:"resource"`
|
||||
TokenType string `json:"tokenType"`
|
||||
UserID string `json:"userId"`
|
||||
}
|
||||
|
||||
// ToADALToken converts an Azure CLI `Token`` to an `adal.Token``
|
||||
func (t Token) ToADALToken() (converted adal.Token, err error) {
|
||||
tokenExpirationDate, err := ParseExpirationDate(t.ExpiresOn)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error parsing Token Expiration Date %q: %+v", t.ExpiresOn, err)
|
||||
return
|
||||
}
|
||||
|
||||
difference := tokenExpirationDate.Sub(date.UnixEpoch())
|
||||
|
||||
converted = adal.Token{
|
||||
AccessToken: t.AccessToken,
|
||||
Type: t.TokenType,
|
||||
ExpiresIn: "3600",
|
||||
ExpiresOn: strconv.Itoa(int(difference.Seconds())),
|
||||
RefreshToken: t.RefreshToken,
|
||||
Resource: t.Resource,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AccessTokensPath returns the path where access tokens are stored from the Azure CLI
|
||||
func AccessTokensPath() (string, error) {
|
||||
return homedir.Expand("~/.azure/accessTokens.json")
|
||||
}
|
||||
|
||||
// ParseExpirationDate parses either a Azure CLI or CloudShell date into a time object
|
||||
func ParseExpirationDate(input string) (*time.Time, error) {
|
||||
// CloudShell (and potentially the Azure CLI in future)
|
||||
expirationDate, cloudShellErr := time.Parse(time.RFC3339, input)
|
||||
if cloudShellErr != nil {
|
||||
// Azure CLI (Python) e.g. 2017-08-31 19:48:57.998857 (plus the local timezone)
|
||||
const cliFormat = "2006-01-02 15:04:05.999999"
|
||||
expirationDate, cliErr := time.ParseInLocation(cliFormat, input, time.Local)
|
||||
if cliErr == nil {
|
||||
return &expirationDate, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Error parsing expiration date %q.\n\nCloudShell Error: \n%+v\n\nCLI Error:\n%+v", input, cloudShellErr, cliErr)
|
||||
}
|
||||
|
||||
return &expirationDate, nil
|
||||
}
|
||||
|
||||
// LoadTokens restores a set of Token objects from a file located at 'path'.
|
||||
func LoadTokens(path string) ([]Token, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var tokens []Token
|
||||
|
||||
dec := json.NewDecoder(file)
|
||||
if err = dec.Decode(&tokens); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode contents of file (%s) into a `cli.Token` representation: %v", path, err)
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
144
vendor/github.com/Azure/go-autorest/autorest/azure/environments.go
generated
vendored
Normal file
144
vendor/github.com/Azure/go-autorest/autorest/azure/environments.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
package azure
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var environments = map[string]Environment{
|
||||
"AZURECHINACLOUD": ChinaCloud,
|
||||
"AZUREGERMANCLOUD": GermanCloud,
|
||||
"AZUREPUBLICCLOUD": PublicCloud,
|
||||
"AZUREUSGOVERNMENTCLOUD": USGovernmentCloud,
|
||||
}
|
||||
|
||||
// Environment represents a set of endpoints for each of Azure's Clouds.
|
||||
type Environment struct {
|
||||
Name string `json:"name"`
|
||||
ManagementPortalURL string `json:"managementPortalURL"`
|
||||
PublishSettingsURL string `json:"publishSettingsURL"`
|
||||
ServiceManagementEndpoint string `json:"serviceManagementEndpoint"`
|
||||
ResourceManagerEndpoint string `json:"resourceManagerEndpoint"`
|
||||
ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"`
|
||||
GalleryEndpoint string `json:"galleryEndpoint"`
|
||||
KeyVaultEndpoint string `json:"keyVaultEndpoint"`
|
||||
GraphEndpoint string `json:"graphEndpoint"`
|
||||
StorageEndpointSuffix string `json:"storageEndpointSuffix"`
|
||||
SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"`
|
||||
TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"`
|
||||
KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"`
|
||||
ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"`
|
||||
ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"`
|
||||
ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"`
|
||||
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
|
||||
}
|
||||
|
||||
var (
|
||||
// PublicCloud is the default public Azure cloud environment
|
||||
PublicCloud = Environment{
|
||||
Name: "AzurePublicCloud",
|
||||
ManagementPortalURL: "https://manage.windowsazure.com/",
|
||||
PublishSettingsURL: "https://manage.windowsazure.com/publishsettings/index",
|
||||
ServiceManagementEndpoint: "https://management.core.windows.net/",
|
||||
ResourceManagerEndpoint: "https://management.azure.com/",
|
||||
ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
|
||||
GalleryEndpoint: "https://gallery.azure.com/",
|
||||
KeyVaultEndpoint: "https://vault.azure.net/",
|
||||
GraphEndpoint: "https://graph.windows.net/",
|
||||
StorageEndpointSuffix: "core.windows.net",
|
||||
SQLDatabaseDNSSuffix: "database.windows.net",
|
||||
TrafficManagerDNSSuffix: "trafficmanager.net",
|
||||
KeyVaultDNSSuffix: "vault.azure.net",
|
||||
ServiceBusEndpointSuffix: "servicebus.azure.com",
|
||||
ServiceManagementVMDNSSuffix: "cloudapp.net",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.azure.com",
|
||||
ContainerRegistryDNSSuffix: "azurecr.io",
|
||||
}
|
||||
|
||||
// USGovernmentCloud is the cloud environment for the US Government
|
||||
USGovernmentCloud = Environment{
|
||||
Name: "AzureUSGovernmentCloud",
|
||||
ManagementPortalURL: "https://manage.windowsazure.us/",
|
||||
PublishSettingsURL: "https://manage.windowsazure.us/publishsettings/index",
|
||||
ServiceManagementEndpoint: "https://management.core.usgovcloudapi.net/",
|
||||
ResourceManagerEndpoint: "https://management.usgovcloudapi.net/",
|
||||
ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
|
||||
GalleryEndpoint: "https://gallery.usgovcloudapi.net/",
|
||||
KeyVaultEndpoint: "https://vault.usgovcloudapi.net/",
|
||||
GraphEndpoint: "https://graph.usgovcloudapi.net/",
|
||||
StorageEndpointSuffix: "core.usgovcloudapi.net",
|
||||
SQLDatabaseDNSSuffix: "database.usgovcloudapi.net",
|
||||
TrafficManagerDNSSuffix: "usgovtrafficmanager.net",
|
||||
KeyVaultDNSSuffix: "vault.usgovcloudapi.net",
|
||||
ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net",
|
||||
ServiceManagementVMDNSSuffix: "usgovcloudapp.net",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us",
|
||||
ContainerRegistryDNSSuffix: "azurecr.io",
|
||||
}
|
||||
|
||||
// ChinaCloud is the cloud environment operated in China
|
||||
ChinaCloud = Environment{
|
||||
Name: "AzureChinaCloud",
|
||||
ManagementPortalURL: "https://manage.chinacloudapi.com/",
|
||||
PublishSettingsURL: "https://manage.chinacloudapi.com/publishsettings/index",
|
||||
ServiceManagementEndpoint: "https://management.core.chinacloudapi.cn/",
|
||||
ResourceManagerEndpoint: "https://management.chinacloudapi.cn/",
|
||||
ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/",
|
||||
GalleryEndpoint: "https://gallery.chinacloudapi.cn/",
|
||||
KeyVaultEndpoint: "https://vault.azure.cn/",
|
||||
GraphEndpoint: "https://graph.chinacloudapi.cn/",
|
||||
StorageEndpointSuffix: "core.chinacloudapi.cn",
|
||||
SQLDatabaseDNSSuffix: "database.chinacloudapi.cn",
|
||||
TrafficManagerDNSSuffix: "trafficmanager.cn",
|
||||
KeyVaultDNSSuffix: "vault.azure.cn",
|
||||
ServiceBusEndpointSuffix: "servicebus.chinacloudapi.net",
|
||||
ServiceManagementVMDNSSuffix: "chinacloudapp.cn",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.azure.cn",
|
||||
ContainerRegistryDNSSuffix: "azurecr.io",
|
||||
}
|
||||
|
||||
// GermanCloud is the cloud environment operated in Germany
|
||||
GermanCloud = Environment{
|
||||
Name: "AzureGermanCloud",
|
||||
ManagementPortalURL: "http://portal.microsoftazure.de/",
|
||||
PublishSettingsURL: "https://manage.microsoftazure.de/publishsettings/index",
|
||||
ServiceManagementEndpoint: "https://management.core.cloudapi.de/",
|
||||
ResourceManagerEndpoint: "https://management.microsoftazure.de/",
|
||||
ActiveDirectoryEndpoint: "https://login.microsoftonline.de/",
|
||||
GalleryEndpoint: "https://gallery.cloudapi.de/",
|
||||
KeyVaultEndpoint: "https://vault.microsoftazure.de/",
|
||||
GraphEndpoint: "https://graph.cloudapi.de/",
|
||||
StorageEndpointSuffix: "core.cloudapi.de",
|
||||
SQLDatabaseDNSSuffix: "database.cloudapi.de",
|
||||
TrafficManagerDNSSuffix: "azuretrafficmanager.de",
|
||||
KeyVaultDNSSuffix: "vault.microsoftazure.de",
|
||||
ServiceBusEndpointSuffix: "servicebus.cloudapi.de",
|
||||
ServiceManagementVMDNSSuffix: "azurecloudapp.de",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de",
|
||||
ContainerRegistryDNSSuffix: "azurecr.io",
|
||||
}
|
||||
)
|
||||
|
||||
// EnvironmentFromName returns an Environment based on the common name specified
|
||||
func EnvironmentFromName(name string) (Environment, error) {
|
||||
name = strings.ToUpper(name)
|
||||
env, ok := environments[name]
|
||||
if !ok {
|
||||
return env, fmt.Errorf("autorest/azure: There is no cloud environment matching the name %q", name)
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
230
vendor/github.com/Azure/go-autorest/autorest/azure/environments_test.go
generated
vendored
Normal file
230
vendor/github.com/Azure/go-autorest/autorest/azure/environments_test.go
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
// test
|
||||
package azure
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEnvironmentFromName(t *testing.T) {
|
||||
name := "azurechinacloud"
|
||||
if env, _ := EnvironmentFromName(name); env != ChinaCloud {
|
||||
t.Errorf("Expected to get ChinaCloud for %q", name)
|
||||
}
|
||||
|
||||
name = "AzureChinaCloud"
|
||||
if env, _ := EnvironmentFromName(name); env != ChinaCloud {
|
||||
t.Errorf("Expected to get ChinaCloud for %q", name)
|
||||
}
|
||||
|
||||
name = "azuregermancloud"
|
||||
if env, _ := EnvironmentFromName(name); env != GermanCloud {
|
||||
t.Errorf("Expected to get GermanCloud for %q", name)
|
||||
}
|
||||
|
||||
name = "AzureGermanCloud"
|
||||
if env, _ := EnvironmentFromName(name); env != GermanCloud {
|
||||
t.Errorf("Expected to get GermanCloud for %q", name)
|
||||
}
|
||||
|
||||
name = "azurepubliccloud"
|
||||
if env, _ := EnvironmentFromName(name); env != PublicCloud {
|
||||
t.Errorf("Expected to get PublicCloud for %q", name)
|
||||
}
|
||||
|
||||
name = "AzurePublicCloud"
|
||||
if env, _ := EnvironmentFromName(name); env != PublicCloud {
|
||||
t.Errorf("Expected to get PublicCloud for %q", name)
|
||||
}
|
||||
|
||||
name = "azureusgovernmentcloud"
|
||||
if env, _ := EnvironmentFromName(name); env != USGovernmentCloud {
|
||||
t.Errorf("Expected to get USGovernmentCloud for %q", name)
|
||||
}
|
||||
|
||||
name = "AzureUSGovernmentCloud"
|
||||
if env, _ := EnvironmentFromName(name); env != USGovernmentCloud {
|
||||
t.Errorf("Expected to get USGovernmentCloud for %q", name)
|
||||
}
|
||||
|
||||
name = "thisisnotarealcloudenv"
|
||||
if _, err := EnvironmentFromName(name); err == nil {
|
||||
t.Errorf("Expected to get an error for %q", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeserializeEnvironment(t *testing.T) {
|
||||
env := `{
|
||||
"name": "--name--",
|
||||
"ActiveDirectoryEndpoint": "--active-directory-endpoint--",
|
||||
"galleryEndpoint": "--gallery-endpoint--",
|
||||
"graphEndpoint": "--graph-endpoint--",
|
||||
"keyVaultDNSSuffix": "--key-vault-dns-suffix--",
|
||||
"keyVaultEndpoint": "--key-vault-endpoint--",
|
||||
"managementPortalURL": "--management-portal-url--",
|
||||
"publishSettingsURL": "--publish-settings-url--",
|
||||
"resourceManagerEndpoint": "--resource-manager-endpoint--",
|
||||
"serviceBusEndpointSuffix": "--service-bus-endpoint-suffix--",
|
||||
"serviceManagementEndpoint": "--service-management-endpoint--",
|
||||
"sqlDatabaseDNSSuffix": "--sql-database-dns-suffix--",
|
||||
"storageEndpointSuffix": "--storage-endpoint-suffix--",
|
||||
"trafficManagerDNSSuffix": "--traffic-manager-dns-suffix--",
|
||||
"serviceManagementVMDNSSuffix": "--asm-vm-dns-suffix--",
|
||||
"resourceManagerVMDNSSuffix": "--arm-vm-dns-suffix--",
|
||||
"containerRegistryDNSSuffix": "--container-registry-dns-suffix--"
|
||||
}`
|
||||
|
||||
testSubject := Environment{}
|
||||
err := json.Unmarshal([]byte(env), &testSubject)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unmarshal: %s", err)
|
||||
}
|
||||
|
||||
if "--name--" != testSubject.Name {
|
||||
t.Errorf("Expected Name to be \"--name--\", but got %q", testSubject.Name)
|
||||
}
|
||||
if "--management-portal-url--" != testSubject.ManagementPortalURL {
|
||||
t.Errorf("Expected ManagementPortalURL to be \"--management-portal-url--\", but got %q", testSubject.ManagementPortalURL)
|
||||
}
|
||||
if "--publish-settings-url--" != testSubject.PublishSettingsURL {
|
||||
t.Errorf("Expected PublishSettingsURL to be \"--publish-settings-url--\", but got %q", testSubject.PublishSettingsURL)
|
||||
}
|
||||
if "--service-management-endpoint--" != testSubject.ServiceManagementEndpoint {
|
||||
t.Errorf("Expected ServiceManagementEndpoint to be \"--service-management-endpoint--\", but got %q", testSubject.ServiceManagementEndpoint)
|
||||
}
|
||||
if "--resource-manager-endpoint--" != testSubject.ResourceManagerEndpoint {
|
||||
t.Errorf("Expected ResourceManagerEndpoint to be \"--resource-manager-endpoint--\", but got %q", testSubject.ResourceManagerEndpoint)
|
||||
}
|
||||
if "--active-directory-endpoint--" != testSubject.ActiveDirectoryEndpoint {
|
||||
t.Errorf("Expected ActiveDirectoryEndpoint to be \"--active-directory-endpoint--\", but got %q", testSubject.ActiveDirectoryEndpoint)
|
||||
}
|
||||
if "--gallery-endpoint--" != testSubject.GalleryEndpoint {
|
||||
t.Errorf("Expected GalleryEndpoint to be \"--gallery-endpoint--\", but got %q", testSubject.GalleryEndpoint)
|
||||
}
|
||||
if "--key-vault-endpoint--" != testSubject.KeyVaultEndpoint {
|
||||
t.Errorf("Expected KeyVaultEndpoint to be \"--key-vault-endpoint--\", but got %q", testSubject.KeyVaultEndpoint)
|
||||
}
|
||||
if "--graph-endpoint--" != testSubject.GraphEndpoint {
|
||||
t.Errorf("Expected GraphEndpoint to be \"--graph-endpoint--\", but got %q", testSubject.GraphEndpoint)
|
||||
}
|
||||
if "--storage-endpoint-suffix--" != testSubject.StorageEndpointSuffix {
|
||||
t.Errorf("Expected StorageEndpointSuffix to be \"--storage-endpoint-suffix--\", but got %q", testSubject.StorageEndpointSuffix)
|
||||
}
|
||||
if "--sql-database-dns-suffix--" != testSubject.SQLDatabaseDNSSuffix {
|
||||
t.Errorf("Expected sql-database-dns-suffix to be \"--sql-database-dns-suffix--\", but got %q", testSubject.SQLDatabaseDNSSuffix)
|
||||
}
|
||||
if "--key-vault-dns-suffix--" != testSubject.KeyVaultDNSSuffix {
|
||||
t.Errorf("Expected StorageEndpointSuffix to be \"--key-vault-dns-suffix--\", but got %q", testSubject.KeyVaultDNSSuffix)
|
||||
}
|
||||
if "--service-bus-endpoint-suffix--" != testSubject.ServiceBusEndpointSuffix {
|
||||
t.Errorf("Expected StorageEndpointSuffix to be \"--service-bus-endpoint-suffix--\", but got %q", testSubject.ServiceBusEndpointSuffix)
|
||||
}
|
||||
if "--asm-vm-dns-suffix--" != testSubject.ServiceManagementVMDNSSuffix {
|
||||
t.Errorf("Expected ServiceManagementVMDNSSuffix to be \"--asm-vm-dns-suffix--\", but got %q", testSubject.ServiceManagementVMDNSSuffix)
|
||||
}
|
||||
if "--arm-vm-dns-suffix--" != testSubject.ResourceManagerVMDNSSuffix {
|
||||
t.Errorf("Expected ResourceManagerVMDNSSuffix to be \"--arm-vm-dns-suffix--\", but got %q", testSubject.ResourceManagerVMDNSSuffix)
|
||||
}
|
||||
if "--container-registry-dns-suffix--" != testSubject.ContainerRegistryDNSSuffix {
|
||||
t.Errorf("Expected ContainerRegistryDNSSuffix to be \"--container-registry-dns-suffix--\", but got %q", testSubject.ContainerRegistryDNSSuffix)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTripSerialization(t *testing.T) {
|
||||
env := Environment{
|
||||
Name: "--unit-test--",
|
||||
ManagementPortalURL: "--management-portal-url",
|
||||
PublishSettingsURL: "--publish-settings-url--",
|
||||
ServiceManagementEndpoint: "--service-management-endpoint--",
|
||||
ResourceManagerEndpoint: "--resource-management-endpoint--",
|
||||
ActiveDirectoryEndpoint: "--active-directory-endpoint--",
|
||||
GalleryEndpoint: "--gallery-endpoint--",
|
||||
KeyVaultEndpoint: "--key-vault--endpoint--",
|
||||
GraphEndpoint: "--graph-endpoint--",
|
||||
StorageEndpointSuffix: "--storage-endpoint-suffix--",
|
||||
SQLDatabaseDNSSuffix: "--sql-database-dns-suffix--",
|
||||
TrafficManagerDNSSuffix: "--traffic-manager-dns-suffix--",
|
||||
KeyVaultDNSSuffix: "--key-vault-dns-suffix--",
|
||||
ServiceBusEndpointSuffix: "--service-bus-endpoint-suffix--",
|
||||
ServiceManagementVMDNSSuffix: "--asm-vm-dns-suffix--",
|
||||
ResourceManagerVMDNSSuffix: "--arm-vm-dns-suffix--",
|
||||
ContainerRegistryDNSSuffix: "--container-registry-dns-suffix--",
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal: %s", err)
|
||||
}
|
||||
|
||||
testSubject := Environment{}
|
||||
err = json.Unmarshal(bytes, &testSubject)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unmarshal: %s", err)
|
||||
}
|
||||
|
||||
if env.Name != testSubject.Name {
|
||||
t.Errorf("Expected Name to be %q, but got %q", env.Name, testSubject.Name)
|
||||
}
|
||||
if env.ManagementPortalURL != testSubject.ManagementPortalURL {
|
||||
t.Errorf("Expected ManagementPortalURL to be %q, but got %q", env.ManagementPortalURL, testSubject.ManagementPortalURL)
|
||||
}
|
||||
if env.PublishSettingsURL != testSubject.PublishSettingsURL {
|
||||
t.Errorf("Expected PublishSettingsURL to be %q, but got %q", env.PublishSettingsURL, testSubject.PublishSettingsURL)
|
||||
}
|
||||
if env.ServiceManagementEndpoint != testSubject.ServiceManagementEndpoint {
|
||||
t.Errorf("Expected ServiceManagementEndpoint to be %q, but got %q", env.ServiceManagementEndpoint, testSubject.ServiceManagementEndpoint)
|
||||
}
|
||||
if env.ResourceManagerEndpoint != testSubject.ResourceManagerEndpoint {
|
||||
t.Errorf("Expected ResourceManagerEndpoint to be %q, but got %q", env.ResourceManagerEndpoint, testSubject.ResourceManagerEndpoint)
|
||||
}
|
||||
if env.ActiveDirectoryEndpoint != testSubject.ActiveDirectoryEndpoint {
|
||||
t.Errorf("Expected ActiveDirectoryEndpoint to be %q, but got %q", env.ActiveDirectoryEndpoint, testSubject.ActiveDirectoryEndpoint)
|
||||
}
|
||||
if env.GalleryEndpoint != testSubject.GalleryEndpoint {
|
||||
t.Errorf("Expected GalleryEndpoint to be %q, but got %q", env.GalleryEndpoint, testSubject.GalleryEndpoint)
|
||||
}
|
||||
if env.KeyVaultEndpoint != testSubject.KeyVaultEndpoint {
|
||||
t.Errorf("Expected KeyVaultEndpoint to be %q, but got %q", env.KeyVaultEndpoint, testSubject.KeyVaultEndpoint)
|
||||
}
|
||||
if env.GraphEndpoint != testSubject.GraphEndpoint {
|
||||
t.Errorf("Expected GraphEndpoint to be %q, but got %q", env.GraphEndpoint, testSubject.GraphEndpoint)
|
||||
}
|
||||
if env.StorageEndpointSuffix != testSubject.StorageEndpointSuffix {
|
||||
t.Errorf("Expected StorageEndpointSuffix to be %q, but got %q", env.StorageEndpointSuffix, testSubject.StorageEndpointSuffix)
|
||||
}
|
||||
if env.SQLDatabaseDNSSuffix != testSubject.SQLDatabaseDNSSuffix {
|
||||
t.Errorf("Expected SQLDatabaseDNSSuffix to be %q, but got %q", env.SQLDatabaseDNSSuffix, testSubject.SQLDatabaseDNSSuffix)
|
||||
}
|
||||
if env.TrafficManagerDNSSuffix != testSubject.TrafficManagerDNSSuffix {
|
||||
t.Errorf("Expected TrafficManagerDNSSuffix to be %q, but got %q", env.TrafficManagerDNSSuffix, testSubject.TrafficManagerDNSSuffix)
|
||||
}
|
||||
if env.KeyVaultDNSSuffix != testSubject.KeyVaultDNSSuffix {
|
||||
t.Errorf("Expected KeyVaultDNSSuffix to be %q, but got %q", env.KeyVaultDNSSuffix, testSubject.KeyVaultDNSSuffix)
|
||||
}
|
||||
if env.ServiceBusEndpointSuffix != testSubject.ServiceBusEndpointSuffix {
|
||||
t.Errorf("Expected ServiceBusEndpointSuffix to be %q, but got %q", env.ServiceBusEndpointSuffix, testSubject.ServiceBusEndpointSuffix)
|
||||
}
|
||||
if env.ServiceManagementVMDNSSuffix != testSubject.ServiceManagementVMDNSSuffix {
|
||||
t.Errorf("Expected ServiceManagementVMDNSSuffix to be %q, but got %q", env.ServiceManagementVMDNSSuffix, testSubject.ServiceManagementVMDNSSuffix)
|
||||
}
|
||||
if env.ResourceManagerVMDNSSuffix != testSubject.ResourceManagerVMDNSSuffix {
|
||||
t.Errorf("Expected ResourceManagerVMDNSSuffix to be %q, but got %q", env.ResourceManagerVMDNSSuffix, testSubject.ResourceManagerVMDNSSuffix)
|
||||
}
|
||||
if env.ContainerRegistryDNSSuffix != testSubject.ContainerRegistryDNSSuffix {
|
||||
t.Errorf("Expected ContainerRegistryDNSSuffix to be %q, but got %q", env.ContainerRegistryDNSSuffix, testSubject.ContainerRegistryDNSSuffix)
|
||||
}
|
||||
}
|
||||
127
vendor/github.com/Azure/go-autorest/autorest/azure/example/README.md
generated
vendored
Normal file
127
vendor/github.com/Azure/go-autorest/autorest/azure/example/README.md
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
# autorest azure example
|
||||
|
||||
## Usage (device mode)
|
||||
|
||||
This shows how to use the example for device auth.
|
||||
|
||||
1. Execute this. It will save your token to /tmp/azure-example-token:
|
||||
|
||||
```
|
||||
./example -tenantId "13de0a15-b5db-44b9-b682-b4ba82afbd29" -subscriptionId "aff271ee-e9be-4441-b9bb-42f5af4cbaeb" -mode "device" -tokenCachePath "/tmp/azure-example-token"
|
||||
```
|
||||
|
||||
2. Execute it again, it will load the token from cache and not prompt for auth again.
|
||||
|
||||
## Usage (certificate mode)
|
||||
|
||||
This example covers how to make an authenticated call to the Azure Resource Manager APIs, using certificate-based authentication.
|
||||
|
||||
0. Export some required variables
|
||||
|
||||
```
|
||||
export SUBSCRIPTION_ID="aff271ee-e9be-4441-b9bb-42f5af4cbaeb"
|
||||
export TENANT_ID="13de0a15-b5db-44b9-b682-b4ba82afbd29"
|
||||
export RESOURCE_GROUP="someresourcegroup"
|
||||
```
|
||||
|
||||
* replace both values with your own
|
||||
|
||||
1. Create a private key
|
||||
|
||||
```
|
||||
openssl genrsa -out "example.key" 2048
|
||||
```
|
||||
|
||||
|
||||
|
||||
2. Create the certificate
|
||||
|
||||
```
|
||||
openssl req -new -key "example.key" -subj "/CN=example" -out "example.csr"
|
||||
|
||||
openssl x509 -req -in "example.csr" -signkey "example.key" -out "example.crt" -days 10000
|
||||
```
|
||||
|
||||
|
||||
|
||||
3. Create the PKCS12 version of the certificate (with no password)
|
||||
|
||||
```
|
||||
openssl pkcs12 -export -out "example.pfx" -inkey "example.key" -in "example.crt" -passout pass:
|
||||
```
|
||||
|
||||
|
||||
|
||||
4. Register a new Azure AD Application with the certificate contents
|
||||
|
||||
```
|
||||
certificateContents="$(tail -n+2 "example.key" | head -n-1)"
|
||||
|
||||
azure ad app create \
|
||||
--name "example-azuread-app" \
|
||||
--home-page="http://example-azuread-app/home" \
|
||||
--identifier-uris "http://example-azuread-app/app" \
|
||||
--key-usage "Verify" \
|
||||
--end-date "2020-01-01" \
|
||||
--key-value "${certificateContents}"
|
||||
```
|
||||
|
||||
|
||||
|
||||
5. Create a new service principal using the "Application Id" from the previous step
|
||||
|
||||
```
|
||||
azure ad sp create "APPLICATION_ID"
|
||||
```
|
||||
|
||||
* Replace APPLICATION_ID with the "Application Id" returned in step 4
|
||||
|
||||
|
||||
|
||||
6. Grant your service principal necessary permissions
|
||||
|
||||
```
|
||||
azure role assignment create \
|
||||
--resource-group "${RESOURCE_GROUP}" \
|
||||
--roleName "Contributor" \
|
||||
--subscription "${SUBSCRIPTION_ID}" \
|
||||
--spn "http://example-azuread-app/app"
|
||||
```
|
||||
|
||||
* Replace SUBSCRIPTION_ID with your subscription id
|
||||
* Replace RESOURCE_GROUP with the resource group for the assignment
|
||||
* Ensure that the `spn` parameter matches an `identifier-url` from Step 4
|
||||
|
||||
|
||||
|
||||
7. Run this example app to see your resource groups
|
||||
|
||||
```
|
||||
go run main.go \
|
||||
--tenantId="${TENANT_ID}" \
|
||||
--subscriptionId="${SUBSCRIPTION_ID}" \
|
||||
--applicationId="http://example-azuread-app/app" \
|
||||
--certificatePath="certificate.pfx"
|
||||
```
|
||||
|
||||
|
||||
You should see something like this as output:
|
||||
|
||||
```
|
||||
2015/11/08 18:28:39 Using these settings:
|
||||
2015/11/08 18:28:39 * certificatePath: certificate.pfx
|
||||
2015/11/08 18:28:39 * applicationID: http://example-azuread-app/app
|
||||
2015/11/08 18:28:39 * tenantID: 13de0a15-b5db-44b9-b682-b4ba82afbd29
|
||||
2015/11/08 18:28:39 * subscriptionID: aff271ee-e9be-4441-b9bb-42f5af4cbaeb
|
||||
2015/11/08 18:28:39 loading certificate...
|
||||
2015/11/08 18:28:39 retrieve oauth token...
|
||||
2015/11/08 18:28:39 querying the list of resource groups...
|
||||
2015/11/08 18:28:50
|
||||
2015/11/08 18:28:50 Groups: {"value":[{"id":"/subscriptions/aff271ee-e9be-4441-b9bb-42f5af4cbaeb/resourceGroups/kube-66f30810","name":"kube-66f30810","location":"westus","tags":{},"properties":{"provisioningState":"Succeeded"}}]}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
You may need to wait sometime between executing step 4, step 5 and step 6. If you issue those requests too quickly, you might hit an AD server that is not consistent with the server where the resource was created.
|
||||
272
vendor/github.com/Azure/go-autorest/autorest/azure/example/main.go
generated
vendored
Normal file
272
vendor/github.com/Azure/go-autorest/autorest/azure/example/main.go
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
package main
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
)
|
||||
|
||||
const (
|
||||
resourceGroupURLTemplate = "https://management.azure.com"
|
||||
apiVersion = "2015-01-01"
|
||||
nativeAppClientID = "a87032a7-203c-4bf7-913c-44c50d23409a"
|
||||
resource = "https://management.core.windows.net/"
|
||||
)
|
||||
|
||||
var (
|
||||
mode string
|
||||
tenantID string
|
||||
subscriptionID string
|
||||
applicationID string
|
||||
|
||||
tokenCachePath string
|
||||
forceRefresh bool
|
||||
impatient bool
|
||||
|
||||
certificatePath string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&mode, "mode", "device", "mode of operation for SPT creation")
|
||||
flag.StringVar(&certificatePath, "certificatePath", "", "path to pk12/pfx certificate")
|
||||
flag.StringVar(&applicationID, "applicationId", "", "application id")
|
||||
flag.StringVar(&tenantID, "tenantId", "", "tenant id")
|
||||
flag.StringVar(&subscriptionID, "subscriptionId", "", "subscription id")
|
||||
flag.StringVar(&tokenCachePath, "tokenCachePath", "", "location of oauth token cache")
|
||||
flag.BoolVar(&forceRefresh, "forceRefresh", false, "pass true to force a token refresh")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
log.Printf("mode(%s) certPath(%s) appID(%s) tenantID(%s), subID(%s)\n",
|
||||
mode, certificatePath, applicationID, tenantID, subscriptionID)
|
||||
|
||||
if mode == "certificate" &&
|
||||
(strings.TrimSpace(tenantID) == "" || strings.TrimSpace(subscriptionID) == "") {
|
||||
log.Fatalln("Bad usage. Using certificate mode. Please specify tenantID, subscriptionID")
|
||||
}
|
||||
|
||||
if mode != "certificate" && mode != "device" {
|
||||
log.Fatalln("Bad usage. Mode must be one of 'certificate' or 'device'.")
|
||||
}
|
||||
|
||||
if mode == "device" && strings.TrimSpace(applicationID) == "" {
|
||||
log.Println("Using device mode auth. Will use `azkube` clientID since none was specified on the comand line.")
|
||||
applicationID = nativeAppClientID
|
||||
}
|
||||
|
||||
if mode == "certificate" && strings.TrimSpace(certificatePath) == "" {
|
||||
log.Fatalln("Bad usage. Mode 'certificate' requires the 'certificatePath' argument.")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(subscriptionID) == "" || strings.TrimSpace(applicationID) == "" {
|
||||
log.Fatalln("Bad usage. Must specify the 'tenantId' and 'subscriptionId'")
|
||||
}
|
||||
}
|
||||
|
||||
func getSptFromCachedToken(oauthConfig adal.OAuthConfig, clientID, resource string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
|
||||
token, err := adal.LoadToken(tokenCachePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load token from cache: %v", err)
|
||||
}
|
||||
|
||||
spt, _ := adal.NewServicePrincipalTokenFromManualToken(
|
||||
oauthConfig,
|
||||
clientID,
|
||||
resource,
|
||||
*token,
|
||||
callbacks...)
|
||||
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||
privateKey, certificate, err := pkcs12.Decode(pkcs, password)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey)
|
||||
if !isRsaKey {
|
||||
return nil, nil, fmt.Errorf("PKCS#12 certificate must contain an RSA private key")
|
||||
}
|
||||
|
||||
return certificate, rsaPrivateKey, nil
|
||||
}
|
||||
|
||||
func getSptFromCertificate(oauthConfig adal.OAuthConfig, clientID, resource, certicatePath string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
|
||||
certData, err := ioutil.ReadFile(certificatePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err)
|
||||
}
|
||||
|
||||
certificate, rsaPrivateKey, err := decodePkcs12(certData, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
|
||||
}
|
||||
|
||||
spt, _ := adal.NewServicePrincipalTokenFromCertificate(
|
||||
oauthConfig,
|
||||
clientID,
|
||||
certificate,
|
||||
rsaPrivateKey,
|
||||
resource,
|
||||
callbacks...)
|
||||
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
func getSptFromDeviceFlow(oauthConfig adal.OAuthConfig, clientID, resource string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
|
||||
oauthClient := &autorest.Client{}
|
||||
deviceCode, err := adal.InitiateDeviceAuth(oauthClient, oauthConfig, clientID, resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to start device auth flow: %s", err)
|
||||
}
|
||||
|
||||
fmt.Println(*deviceCode.Message)
|
||||
|
||||
token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to finish device auth flow: %s", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(
|
||||
oauthConfig,
|
||||
clientID,
|
||||
resource,
|
||||
*token,
|
||||
callbacks...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get oauth token from device flow: %v", err)
|
||||
}
|
||||
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
func printResourceGroups(client *autorest.Client) error {
|
||||
p := map[string]interface{}{"subscription-id": subscriptionID}
|
||||
q := map[string]interface{}{"api-version": apiVersion}
|
||||
|
||||
req, _ := autorest.Prepare(&http.Request{},
|
||||
autorest.AsGet(),
|
||||
autorest.WithBaseURL(resourceGroupURLTemplate),
|
||||
autorest.WithPathParameters("/subscriptions/{subscription-id}/resourcegroups", p),
|
||||
autorest.WithQueryParameters(q))
|
||||
|
||||
resp, err := autorest.SendWithSender(client, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value := struct {
|
||||
ResourceGroups []struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"value"`
|
||||
}{}
|
||||
|
||||
defer resp.Body.Close()
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
err = dec.Decode(&value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var groupNames = make([]string, len(value.ResourceGroups))
|
||||
for i, name := range value.ResourceGroups {
|
||||
groupNames[i] = name.Name
|
||||
}
|
||||
|
||||
log.Println("Groups:", strings.Join(groupNames, ", "))
|
||||
return err
|
||||
}
|
||||
|
||||
func saveToken(spt adal.Token) {
|
||||
if tokenCachePath != "" {
|
||||
err := adal.SaveToken(tokenCachePath, 0600, spt)
|
||||
if err != nil {
|
||||
log.Println("error saving token", err)
|
||||
} else {
|
||||
log.Println("saved token to", tokenCachePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var spt *adal.ServicePrincipalToken
|
||||
var err error
|
||||
|
||||
callback := func(t adal.Token) error {
|
||||
log.Println("refresh callback was called")
|
||||
saveToken(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, tenantID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if tokenCachePath != "" {
|
||||
log.Println("tokenCachePath specified; attempting to load from", tokenCachePath)
|
||||
spt, err = getSptFromCachedToken(*oauthConfig, applicationID, resource, callback)
|
||||
if err != nil {
|
||||
spt = nil // just in case, this is the condition below
|
||||
log.Println("loading from cache failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
if spt == nil {
|
||||
log.Println("authenticating via 'mode'", mode)
|
||||
switch mode {
|
||||
case "device":
|
||||
spt, err = getSptFromDeviceFlow(*oauthConfig, applicationID, resource, callback)
|
||||
case "certificate":
|
||||
spt, err = getSptFromCertificate(*oauthConfig, applicationID, resource, certificatePath, callback)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalln("failed to retrieve token:", err)
|
||||
}
|
||||
|
||||
// should save it as soon as you get it since Refresh won't be called for some time
|
||||
if tokenCachePath != "" {
|
||||
saveToken(spt.Token)
|
||||
}
|
||||
}
|
||||
|
||||
client := &autorest.Client{}
|
||||
client.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||
|
||||
printResourceGroups(client)
|
||||
|
||||
if forceRefresh {
|
||||
err = spt.Refresh()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
printResourceGroups(client)
|
||||
}
|
||||
}
|
||||
188
vendor/github.com/Azure/go-autorest/autorest/azure/rp.go
generated
vendored
Normal file
188
vendor/github.com/Azure/go-autorest/autorest/azure/rp.go
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
)
|
||||
|
||||
// DoRetryWithRegistration tries to register the resource provider in case it is unregistered.
|
||||
// It also handles request retries
|
||||
func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator {
|
||||
return func(s autorest.Sender) autorest.Sender {
|
||||
return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
|
||||
rr := autorest.NewRetriableRequest(r)
|
||||
for currentAttempt := 0; currentAttempt < client.RetryAttempts; currentAttempt++ {
|
||||
err = rr.Prepare()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
resp, err = autorest.SendWithSender(s, rr.Request(),
|
||||
autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
|
||||
)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusConflict {
|
||||
return resp, err
|
||||
}
|
||||
var re RequestError
|
||||
err = autorest.Respond(
|
||||
resp,
|
||||
autorest.ByUnmarshallingJSON(&re),
|
||||
)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if re.ServiceError != nil && re.ServiceError.Code == "MissingSubscriptionRegistration" {
|
||||
err = register(client, r, re)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("failed auto registering Resource Provider: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resp, errors.New("failed request and resource provider registration")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getProvider(re RequestError) (string, error) {
|
||||
if re.ServiceError != nil {
|
||||
if re.ServiceError.Details != nil && len(*re.ServiceError.Details) > 0 {
|
||||
detail := (*re.ServiceError.Details)[0].(map[string]interface{})
|
||||
return detail["target"].(string), nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("provider was not found in the response")
|
||||
}
|
||||
|
||||
func register(client autorest.Client, originalReq *http.Request, re RequestError) error {
|
||||
subID := getSubscription(originalReq.URL.Path)
|
||||
if subID == "" {
|
||||
return errors.New("missing parameter subscriptionID to register resource provider")
|
||||
}
|
||||
providerName, err := getProvider(re)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing parameter provider to register resource provider: %s", err)
|
||||
}
|
||||
newURL := url.URL{
|
||||
Scheme: originalReq.URL.Scheme,
|
||||
Host: originalReq.URL.Host,
|
||||
}
|
||||
|
||||
// taken from the resources SDK
|
||||
// with almost identical code, this sections are easier to mantain
|
||||
// It is also not a good idea to import the SDK here
|
||||
// https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L252
|
||||
pathParameters := map[string]interface{}{
|
||||
"resourceProviderNamespace": autorest.Encode("path", providerName),
|
||||
"subscriptionId": autorest.Encode("path", subID),
|
||||
}
|
||||
|
||||
const APIVersion = "2016-09-01"
|
||||
queryParameters := map[string]interface{}{
|
||||
"api-version": APIVersion,
|
||||
}
|
||||
|
||||
preparer := autorest.CreatePreparer(
|
||||
autorest.AsPost(),
|
||||
autorest.WithBaseURL(newURL.String()),
|
||||
autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/register", pathParameters),
|
||||
autorest.WithQueryParameters(queryParameters),
|
||||
)
|
||||
|
||||
req, err := preparer.Prepare(&http.Request{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Cancel = originalReq.Cancel
|
||||
|
||||
resp, err := autorest.SendWithSender(client, req,
|
||||
autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
RegistrationState *string `json:"registrationState,omitempty"`
|
||||
}
|
||||
var provider Provider
|
||||
|
||||
err = autorest.Respond(
|
||||
resp,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByUnmarshallingJSON(&provider),
|
||||
autorest.ByClosing(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// poll for registered provisioning state
|
||||
now := time.Now()
|
||||
for err == nil && time.Since(now) < client.PollingDuration {
|
||||
// taken from the resources SDK
|
||||
// https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L45
|
||||
preparer := autorest.CreatePreparer(
|
||||
autorest.AsGet(),
|
||||
autorest.WithBaseURL(newURL.String()),
|
||||
autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}", pathParameters),
|
||||
autorest.WithQueryParameters(queryParameters),
|
||||
)
|
||||
req, err = preparer.Prepare(&http.Request{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Cancel = originalReq.Cancel
|
||||
|
||||
resp, err := autorest.SendWithSender(client.Sender, req,
|
||||
autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = autorest.Respond(
|
||||
resp,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByUnmarshallingJSON(&provider),
|
||||
autorest.ByClosing(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if provider.RegistrationState != nil &&
|
||||
*provider.RegistrationState == "Registered" {
|
||||
break
|
||||
}
|
||||
|
||||
delayed := autorest.DelayWithRetryAfter(resp, originalReq.Cancel)
|
||||
if !delayed {
|
||||
autorest.DelayForBackoff(client.PollingDelay, 0, originalReq.Cancel)
|
||||
}
|
||||
}
|
||||
if !(time.Since(now) < client.PollingDuration) {
|
||||
return errors.New("polling for resource provider registration has exceeded the polling duration")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getSubscription(path string) string {
|
||||
parts := strings.Split(path, "/")
|
||||
for i, v := range parts {
|
||||
if v == "subscriptions" && (i+1) < len(parts) {
|
||||
return parts[i+1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
67
vendor/github.com/Azure/go-autorest/autorest/azure/rp_test.go
generated
vendored
Normal file
67
vendor/github.com/Azure/go-autorest/autorest/azure/rp_test.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
func TestDoRetryWithRegistration(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
// first response, should retry because it is a transient error
|
||||
client.AppendResponse(mocks.NewResponseWithStatus("Internal server error", http.StatusInternalServerError))
|
||||
// response indicates the resource provider has not been registered
|
||||
client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{
|
||||
"error":{
|
||||
"code":"MissingSubscriptionRegistration",
|
||||
"message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions.",
|
||||
"details":[
|
||||
{
|
||||
"code":"MissingSubscriptionRegistration",
|
||||
"target":"Microsoft.EventGrid",
|
||||
"message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`), http.StatusConflict, "MissingSubscriptionRegistration"))
|
||||
// first poll response, still not ready
|
||||
client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{
|
||||
"registrationState": "Registering"
|
||||
}
|
||||
`), http.StatusOK, "200 OK"))
|
||||
// last poll response, respurce provider has been registered
|
||||
client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{
|
||||
"registrationState": "Registered"
|
||||
}
|
||||
`), http.StatusOK, "200 OK"))
|
||||
// retry original request, response is successful
|
||||
client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK))
|
||||
|
||||
req := mocks.NewRequestForURL("https://lol/subscriptions/rofl")
|
||||
req.Body = mocks.NewBody("lolol")
|
||||
r, err := autorest.SendWithSender(client, req,
|
||||
DoRetryWithRegistration(autorest.Client{
|
||||
PollingDelay: time.Second,
|
||||
PollingDuration: time.Second * 10,
|
||||
RetryAttempts: 5,
|
||||
RetryDuration: time.Second,
|
||||
Sender: client,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("got error: %v", err)
|
||||
}
|
||||
|
||||
autorest.Respond(r,
|
||||
autorest.ByDiscardingBody(),
|
||||
autorest.ByClosing(),
|
||||
)
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
t.Fatalf("azure: Sender#DoRetryWithRegistration -- Got: StatusCode %v; Want: StatusCode 200 OK", r.StatusCode)
|
||||
}
|
||||
}
|
||||
250
vendor/github.com/Azure/go-autorest/autorest/client.go
generated
vendored
Normal file
250
vendor/github.com/Azure/go-autorest/autorest/client.go
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultPollingDelay is a reasonable delay between polling requests.
|
||||
DefaultPollingDelay = 60 * time.Second
|
||||
|
||||
// DefaultPollingDuration is a reasonable total polling duration.
|
||||
DefaultPollingDuration = 15 * time.Minute
|
||||
|
||||
// DefaultRetryAttempts is number of attempts for retry status codes (5xx).
|
||||
DefaultRetryAttempts = 3
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultUserAgent builds a string containing the Go version, system archityecture and OS,
|
||||
// and the go-autorest version.
|
||||
defaultUserAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
|
||||
runtime.Version(),
|
||||
runtime.GOARCH,
|
||||
runtime.GOOS,
|
||||
Version(),
|
||||
)
|
||||
|
||||
// StatusCodesForRetry are a defined group of status code for which the client will retry
|
||||
StatusCodesForRetry = []int{
|
||||
http.StatusRequestTimeout, // 408
|
||||
http.StatusTooManyRequests, // 429
|
||||
http.StatusInternalServerError, // 500
|
||||
http.StatusBadGateway, // 502
|
||||
http.StatusServiceUnavailable, // 503
|
||||
http.StatusGatewayTimeout, // 504
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
requestFormat = `HTTP Request Begin ===================================================
|
||||
%s
|
||||
===================================================== HTTP Request End
|
||||
`
|
||||
responseFormat = `HTTP Response Begin ===================================================
|
||||
%s
|
||||
===================================================== HTTP Response End
|
||||
`
|
||||
)
|
||||
|
||||
// Response serves as the base for all responses from generated clients. It provides access to the
|
||||
// last http.Response.
|
||||
type Response struct {
|
||||
*http.Response `json:"-"`
|
||||
}
|
||||
|
||||
// LoggingInspector implements request and response inspectors that log the full request and
|
||||
// response to a supplied log.
|
||||
type LoggingInspector struct {
|
||||
Logger *log.Logger
|
||||
}
|
||||
|
||||
// WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The
|
||||
// body is restored after being emitted.
|
||||
//
|
||||
// Note: Since it reads the entire Body, this decorator should not be used where body streaming is
|
||||
// important. It is best used to trace JSON or similar body values.
|
||||
func (li LoggingInspector) WithInspection() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
var body, b bytes.Buffer
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body))
|
||||
if err := r.Write(&b); err != nil {
|
||||
return nil, fmt.Errorf("Failed to write response: %v", err)
|
||||
}
|
||||
|
||||
li.Logger.Printf(requestFormat, b.String())
|
||||
|
||||
r.Body = ioutil.NopCloser(&body)
|
||||
return p.Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The
|
||||
// body is restored after being emitted.
|
||||
//
|
||||
// Note: Since it reads the entire Body, this decorator should not be used where body streaming is
|
||||
// important. It is best used to trace JSON or similar body values.
|
||||
func (li LoggingInspector) ByInspecting() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
var body, b bytes.Buffer
|
||||
defer resp.Body.Close()
|
||||
resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body))
|
||||
if err := resp.Write(&b); err != nil {
|
||||
return fmt.Errorf("Failed to write response: %v", err)
|
||||
}
|
||||
|
||||
li.Logger.Printf(responseFormat, b.String())
|
||||
|
||||
resp.Body = ioutil.NopCloser(&body)
|
||||
return r.Respond(resp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Client is the base for autorest generated clients. It provides default, "do nothing"
|
||||
// implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the
|
||||
// standard, undecorated http.Client as a default Sender.
|
||||
//
|
||||
// Generated clients should also use Error (see NewError and NewErrorWithError) for errors and
|
||||
// return responses that compose with Response.
|
||||
//
|
||||
// Most customization of generated clients is best achieved by supplying a custom Authorizer, custom
|
||||
// RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit
|
||||
// breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence
|
||||
// sending the request by providing a decorated Sender.
|
||||
type Client struct {
|
||||
Authorizer Authorizer
|
||||
Sender Sender
|
||||
RequestInspector PrepareDecorator
|
||||
ResponseInspector RespondDecorator
|
||||
|
||||
// PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header
|
||||
PollingDelay time.Duration
|
||||
|
||||
// PollingDuration sets the maximum polling time after which an error is returned.
|
||||
PollingDuration time.Duration
|
||||
|
||||
// RetryAttempts sets the default number of retry attempts for client.
|
||||
RetryAttempts int
|
||||
|
||||
// RetryDuration sets the delay duration for retries.
|
||||
RetryDuration time.Duration
|
||||
|
||||
// UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent
|
||||
// through the Do method.
|
||||
UserAgent string
|
||||
|
||||
Jar http.CookieJar
|
||||
}
|
||||
|
||||
// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed
|
||||
// string.
|
||||
func NewClientWithUserAgent(ua string) Client {
|
||||
c := Client{
|
||||
PollingDelay: DefaultPollingDelay,
|
||||
PollingDuration: DefaultPollingDuration,
|
||||
RetryAttempts: DefaultRetryAttempts,
|
||||
RetryDuration: 30 * time.Second,
|
||||
UserAgent: defaultUserAgent,
|
||||
}
|
||||
c.AddToUserAgent(ua)
|
||||
return c
|
||||
}
|
||||
|
||||
// AddToUserAgent adds an extension to the current user agent
|
||||
func (c *Client) AddToUserAgent(extension string) error {
|
||||
if extension != "" {
|
||||
c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent)
|
||||
}
|
||||
|
||||
// Do implements the Sender interface by invoking the active Sender after applying authorization.
|
||||
// If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent
|
||||
// is set, apply set the User-Agent header.
|
||||
func (c Client) Do(r *http.Request) (*http.Response, error) {
|
||||
if r.UserAgent() == "" {
|
||||
r, _ = Prepare(r,
|
||||
WithUserAgent(c.UserAgent))
|
||||
}
|
||||
r, err := Prepare(r,
|
||||
c.WithInspection(),
|
||||
c.WithAuthorization())
|
||||
if err != nil {
|
||||
return nil, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed")
|
||||
}
|
||||
resp, err := SendWithSender(c.sender(), r)
|
||||
Respond(resp,
|
||||
c.ByInspecting())
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// sender returns the Sender to which to send requests.
|
||||
func (c Client) sender() Sender {
|
||||
if c.Sender == nil {
|
||||
j, _ := cookiejar.New(nil)
|
||||
return &http.Client{Jar: j}
|
||||
}
|
||||
return c.Sender
|
||||
}
|
||||
|
||||
// WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator
|
||||
// from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer.
|
||||
func (c Client) WithAuthorization() PrepareDecorator {
|
||||
return c.authorizer().WithAuthorization()
|
||||
}
|
||||
|
||||
// authorizer returns the Authorizer to use.
|
||||
func (c Client) authorizer() Authorizer {
|
||||
if c.Authorizer == nil {
|
||||
return NullAuthorizer{}
|
||||
}
|
||||
return c.Authorizer
|
||||
}
|
||||
|
||||
// WithInspection is a convenience method that passes the request to the supplied RequestInspector,
|
||||
// if present, or returns the WithNothing PrepareDecorator otherwise.
|
||||
func (c Client) WithInspection() PrepareDecorator {
|
||||
if c.RequestInspector == nil {
|
||||
return WithNothing()
|
||||
}
|
||||
return c.RequestInspector
|
||||
}
|
||||
|
||||
// ByInspecting is a convenience method that passes the response to the supplied ResponseInspector,
|
||||
// if present, or returns the ByIgnoring RespondDecorator otherwise.
|
||||
func (c Client) ByInspecting() RespondDecorator {
|
||||
if c.ResponseInspector == nil {
|
||||
return ByIgnoring()
|
||||
}
|
||||
return c.ResponseInspector
|
||||
}
|
||||
356
vendor/github.com/Azure/go-autorest/autorest/client_test.go
generated
vendored
Normal file
356
vendor/github.com/Azure/go-autorest/autorest/client_test.go
generated
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
func TestLoggingInspectorWithInspection(t *testing.T) {
|
||||
b := bytes.Buffer{}
|
||||
c := Client{}
|
||||
li := LoggingInspector{Logger: log.New(&b, "", 0)}
|
||||
c.RequestInspector = li.WithInspection()
|
||||
|
||||
Prepare(mocks.NewRequestWithContent("Content"),
|
||||
c.WithInspection())
|
||||
|
||||
if len(b.String()) <= 0 {
|
||||
t.Fatal("autorest: LoggingInspector#WithInspection did not record Request to the log")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggingInspectorWithInspectionEmitsErrors(t *testing.T) {
|
||||
b := bytes.Buffer{}
|
||||
c := Client{}
|
||||
r := mocks.NewRequestWithContent("Content")
|
||||
li := LoggingInspector{Logger: log.New(&b, "", 0)}
|
||||
c.RequestInspector = li.WithInspection()
|
||||
|
||||
if _, err := Prepare(r,
|
||||
c.WithInspection()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(b.String()) <= 0 {
|
||||
t.Fatal("autorest: LoggingInspector#WithInspection did not record Request to the log")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggingInspectorWithInspectionRestoresBody(t *testing.T) {
|
||||
b := bytes.Buffer{}
|
||||
c := Client{}
|
||||
r := mocks.NewRequestWithContent("Content")
|
||||
li := LoggingInspector{Logger: log.New(&b, "", 0)}
|
||||
c.RequestInspector = li.WithInspection()
|
||||
|
||||
Prepare(r,
|
||||
c.WithInspection())
|
||||
|
||||
s, _ := ioutil.ReadAll(r.Body)
|
||||
if len(s) <= 0 {
|
||||
t.Fatal("autorest: LoggingInspector#WithInspection did not restore the Request body")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggingInspectorByInspecting(t *testing.T) {
|
||||
b := bytes.Buffer{}
|
||||
c := Client{}
|
||||
li := LoggingInspector{Logger: log.New(&b, "", 0)}
|
||||
c.ResponseInspector = li.ByInspecting()
|
||||
|
||||
Respond(mocks.NewResponseWithContent("Content"),
|
||||
c.ByInspecting())
|
||||
|
||||
if len(b.String()) <= 0 {
|
||||
t.Fatal("autorest: LoggingInspector#ByInspection did not record Response to the log")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggingInspectorByInspectingEmitsErrors(t *testing.T) {
|
||||
b := bytes.Buffer{}
|
||||
c := Client{}
|
||||
r := mocks.NewResponseWithContent("Content")
|
||||
li := LoggingInspector{Logger: log.New(&b, "", 0)}
|
||||
c.ResponseInspector = li.ByInspecting()
|
||||
|
||||
if err := Respond(r,
|
||||
c.ByInspecting()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(b.String()) <= 0 {
|
||||
t.Fatal("autorest: LoggingInspector#ByInspection did not record Response to the log")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggingInspectorByInspectingRestoresBody(t *testing.T) {
|
||||
b := bytes.Buffer{}
|
||||
c := Client{}
|
||||
r := mocks.NewResponseWithContent("Content")
|
||||
li := LoggingInspector{Logger: log.New(&b, "", 0)}
|
||||
c.ResponseInspector = li.ByInspecting()
|
||||
|
||||
Respond(r,
|
||||
c.ByInspecting())
|
||||
|
||||
s, _ := ioutil.ReadAll(r.Body)
|
||||
if len(s) <= 0 {
|
||||
t.Fatal("autorest: LoggingInspector#ByInspecting did not restore the Response body")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClientWithUserAgent(t *testing.T) {
|
||||
ua := "UserAgent"
|
||||
c := NewClientWithUserAgent(ua)
|
||||
completeUA := fmt.Sprintf("%s %s", defaultUserAgent, ua)
|
||||
|
||||
if c.UserAgent != completeUA {
|
||||
t.Fatalf("autorest: NewClientWithUserAgent failed to set the UserAgent -- expected %s, received %s",
|
||||
completeUA, c.UserAgent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddToUserAgent(t *testing.T) {
|
||||
ua := "UserAgent"
|
||||
c := NewClientWithUserAgent(ua)
|
||||
ext := "extension"
|
||||
err := c.AddToUserAgent(ext)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: AddToUserAgent returned error -- expected nil, received %s", err)
|
||||
}
|
||||
completeUA := fmt.Sprintf("%s %s %s", defaultUserAgent, ua, ext)
|
||||
|
||||
if c.UserAgent != completeUA {
|
||||
t.Fatalf("autorest: AddToUserAgent failed to add an extension to the UserAgent -- expected %s, received %s",
|
||||
completeUA, c.UserAgent)
|
||||
}
|
||||
|
||||
err = c.AddToUserAgent("")
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: AddToUserAgent didn't return error -- expected %s, received nil",
|
||||
fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent))
|
||||
}
|
||||
if c.UserAgent != completeUA {
|
||||
t.Fatalf("autorest: AddToUserAgent failed to not add an empty extension to the UserAgent -- expected %s, received %s",
|
||||
completeUA, c.UserAgent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientSenderReturnsHttpClientByDefault(t *testing.T) {
|
||||
c := Client{}
|
||||
|
||||
if fmt.Sprintf("%T", c.sender()) != "*http.Client" {
|
||||
t.Fatal("autorest: Client#sender failed to return http.Client by default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientSenderReturnsSetSender(t *testing.T) {
|
||||
c := Client{}
|
||||
|
||||
s := mocks.NewSender()
|
||||
c.Sender = s
|
||||
|
||||
if c.sender() != s {
|
||||
t.Fatal("autorest: Client#sender failed to return set Sender")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDoInvokesSender(t *testing.T) {
|
||||
c := Client{}
|
||||
|
||||
s := mocks.NewSender()
|
||||
c.Sender = s
|
||||
|
||||
c.Do(&http.Request{})
|
||||
if s.Attempts() != 1 {
|
||||
t.Fatal("autorest: Client#Do failed to invoke the Sender")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDoSetsUserAgent(t *testing.T) {
|
||||
ua := "UserAgent"
|
||||
c := Client{UserAgent: ua}
|
||||
r := mocks.NewRequest()
|
||||
s := mocks.NewSender()
|
||||
c.Sender = s
|
||||
|
||||
c.Do(r)
|
||||
|
||||
if r.UserAgent() != ua {
|
||||
t.Fatalf("autorest: Client#Do failed to correctly set User-Agent header: %s=%s",
|
||||
http.CanonicalHeaderKey(headerUserAgent), r.UserAgent())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDoSetsAuthorization(t *testing.T) {
|
||||
r := mocks.NewRequest()
|
||||
s := mocks.NewSender()
|
||||
c := Client{Authorizer: mockAuthorizer{}, Sender: s}
|
||||
|
||||
c.Do(r)
|
||||
if len(r.Header.Get(http.CanonicalHeaderKey(headerAuthorization))) <= 0 {
|
||||
t.Fatalf("autorest: Client#Send failed to set Authorization header -- %s=%s",
|
||||
http.CanonicalHeaderKey(headerAuthorization),
|
||||
r.Header.Get(http.CanonicalHeaderKey(headerAuthorization)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDoInvokesRequestInspector(t *testing.T) {
|
||||
r := mocks.NewRequest()
|
||||
s := mocks.NewSender()
|
||||
i := &mockInspector{}
|
||||
c := Client{RequestInspector: i.WithInspection(), Sender: s}
|
||||
|
||||
c.Do(r)
|
||||
if !i.wasInvoked {
|
||||
t.Fatal("autorest: Client#Send failed to invoke the RequestInspector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDoInvokesResponseInspector(t *testing.T) {
|
||||
r := mocks.NewRequest()
|
||||
s := mocks.NewSender()
|
||||
i := &mockInspector{}
|
||||
c := Client{ResponseInspector: i.ByInspecting(), Sender: s}
|
||||
|
||||
c.Do(r)
|
||||
if !i.wasInvoked {
|
||||
t.Fatal("autorest: Client#Send failed to invoke the ResponseInspector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDoReturnsErrorIfPrepareFails(t *testing.T) {
|
||||
c := Client{}
|
||||
s := mocks.NewSender()
|
||||
c.Authorizer = mockFailingAuthorizer{}
|
||||
c.Sender = s
|
||||
|
||||
_, err := c.Do(&http.Request{})
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: Client#Do failed to return an error when Prepare failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDoDoesNotSendIfPrepareFails(t *testing.T) {
|
||||
c := Client{}
|
||||
s := mocks.NewSender()
|
||||
c.Authorizer = mockFailingAuthorizer{}
|
||||
c.Sender = s
|
||||
|
||||
c.Do(&http.Request{})
|
||||
if s.Attempts() > 0 {
|
||||
t.Fatal("autorest: Client#Do failed to invoke the Sender")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientAuthorizerReturnsNullAuthorizerByDefault(t *testing.T) {
|
||||
c := Client{}
|
||||
|
||||
if fmt.Sprintf("%T", c.authorizer()) != "autorest.NullAuthorizer" {
|
||||
t.Fatal("autorest: Client#authorizer failed to return the NullAuthorizer by default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientAuthorizerReturnsSetAuthorizer(t *testing.T) {
|
||||
c := Client{}
|
||||
c.Authorizer = mockAuthorizer{}
|
||||
|
||||
if fmt.Sprintf("%T", c.authorizer()) != "autorest.mockAuthorizer" {
|
||||
t.Fatal("autorest: Client#authorizer failed to return the set Authorizer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientWithAuthorizer(t *testing.T) {
|
||||
c := Client{}
|
||||
c.Authorizer = mockAuthorizer{}
|
||||
|
||||
req, _ := Prepare(&http.Request{},
|
||||
c.WithAuthorization())
|
||||
|
||||
if req.Header.Get(headerAuthorization) == "" {
|
||||
t.Fatal("autorest: Client#WithAuthorizer failed to return the WithAuthorizer from the active Authorizer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientWithInspection(t *testing.T) {
|
||||
c := Client{}
|
||||
r := &mockInspector{}
|
||||
c.RequestInspector = r.WithInspection()
|
||||
|
||||
Prepare(&http.Request{},
|
||||
c.WithInspection())
|
||||
|
||||
if !r.wasInvoked {
|
||||
t.Fatal("autorest: Client#WithInspection failed to invoke RequestInspector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientWithInspectionSetsDefault(t *testing.T) {
|
||||
c := Client{}
|
||||
|
||||
r1 := &http.Request{}
|
||||
r2, _ := Prepare(r1,
|
||||
c.WithInspection())
|
||||
|
||||
if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatal("autorest: Client#WithInspection failed to provide a default RequestInspector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientByInspecting(t *testing.T) {
|
||||
c := Client{}
|
||||
r := &mockInspector{}
|
||||
c.ResponseInspector = r.ByInspecting()
|
||||
|
||||
Respond(&http.Response{},
|
||||
c.ByInspecting())
|
||||
|
||||
if !r.wasInvoked {
|
||||
t.Fatal("autorest: Client#ByInspecting failed to invoke ResponseInspector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientByInspectingSetsDefault(t *testing.T) {
|
||||
c := Client{}
|
||||
|
||||
r := &http.Response{}
|
||||
Respond(r,
|
||||
c.ByInspecting())
|
||||
|
||||
if !reflect.DeepEqual(r, &http.Response{}) {
|
||||
t.Fatal("autorest: Client#ByInspecting failed to provide a default ResponseInspector")
|
||||
}
|
||||
}
|
||||
|
||||
func randomString(n int) string {
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
|
||||
s := make([]byte, n)
|
||||
for i := range s {
|
||||
s[i] = chars[r.Intn(len(chars))]
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
96
vendor/github.com/Azure/go-autorest/autorest/date/date.go
generated
vendored
Normal file
96
vendor/github.com/Azure/go-autorest/autorest/date/date.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
Package date provides time.Time derivatives that conform to the Swagger.io (https://swagger.io/)
|
||||
defined date formats: Date and DateTime. Both types may, in most cases, be used in lieu of
|
||||
time.Time types. And both convert to time.Time through a ToTime method.
|
||||
*/
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
fullDate = "2006-01-02"
|
||||
fullDateJSON = `"2006-01-02"`
|
||||
dateFormat = "%04d-%02d-%02d"
|
||||
jsonFormat = `"%04d-%02d-%02d"`
|
||||
)
|
||||
|
||||
// Date defines a type similar to time.Time but assumes a layout of RFC3339 full-date (i.e.,
|
||||
// 2006-01-02).
|
||||
type Date struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// ParseDate create a new Date from the passed string.
|
||||
func ParseDate(date string) (d Date, err error) {
|
||||
return parseDate(date, fullDate)
|
||||
}
|
||||
|
||||
func parseDate(date string, format string) (Date, error) {
|
||||
d, err := time.Parse(format, date)
|
||||
return Date{Time: d}, err
|
||||
}
|
||||
|
||||
// MarshalBinary preserves the Date as a byte array conforming to RFC3339 full-date (i.e.,
|
||||
// 2006-01-02).
|
||||
func (d Date) MarshalBinary() ([]byte, error) {
|
||||
return d.MarshalText()
|
||||
}
|
||||
|
||||
// UnmarshalBinary reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e.,
|
||||
// 2006-01-02).
|
||||
func (d *Date) UnmarshalBinary(data []byte) error {
|
||||
return d.UnmarshalText(data)
|
||||
}
|
||||
|
||||
// MarshalJSON preserves the Date as a JSON string conforming to RFC3339 full-date (i.e.,
|
||||
// 2006-01-02).
|
||||
func (d Date) MarshalJSON() (json []byte, err error) {
|
||||
return []byte(fmt.Sprintf(jsonFormat, d.Year(), d.Month(), d.Day())), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON reconstitutes the Date from a JSON string conforming to RFC3339 full-date (i.e.,
|
||||
// 2006-01-02).
|
||||
func (d *Date) UnmarshalJSON(data []byte) (err error) {
|
||||
d.Time, err = time.Parse(fullDateJSON, string(data))
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalText preserves the Date as a byte array conforming to RFC3339 full-date (i.e.,
|
||||
// 2006-01-02).
|
||||
func (d Date) MarshalText() (text []byte, err error) {
|
||||
return []byte(fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day())), nil
|
||||
}
|
||||
|
||||
// UnmarshalText reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e.,
|
||||
// 2006-01-02).
|
||||
func (d *Date) UnmarshalText(data []byte) (err error) {
|
||||
d.Time, err = time.Parse(fullDate, string(data))
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns the Date formatted as an RFC3339 full-date string (i.e., 2006-01-02).
|
||||
func (d Date) String() string {
|
||||
return fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day())
|
||||
}
|
||||
|
||||
// ToTime returns a Date as a time.Time
|
||||
func (d Date) ToTime() time.Time {
|
||||
return d.Time
|
||||
}
|
||||
237
vendor/github.com/Azure/go-autorest/autorest/date/date_test.go
generated
vendored
Normal file
237
vendor/github.com/Azure/go-autorest/autorest/date/date_test.go
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleParseDate() {
|
||||
d, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d)
|
||||
// Output: 2001-02-03
|
||||
}
|
||||
|
||||
func ExampleDate() {
|
||||
d, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC3339, "2001-02-04T00:00:00Z")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// Date acts as time.Time when the receiver
|
||||
if d.Before(t) {
|
||||
fmt.Printf("Before ")
|
||||
} else {
|
||||
fmt.Printf("After ")
|
||||
}
|
||||
|
||||
// Convert Date when needing a time.Time
|
||||
if t.After(d.ToTime()) {
|
||||
fmt.Printf("After")
|
||||
} else {
|
||||
fmt.Printf("Before")
|
||||
}
|
||||
// Output: Before After
|
||||
}
|
||||
|
||||
func ExampleDate_MarshalBinary() {
|
||||
d, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
t, err := d.MarshalBinary()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(t))
|
||||
// Output: 2001-02-03
|
||||
}
|
||||
|
||||
func ExampleDate_UnmarshalBinary() {
|
||||
d := Date{}
|
||||
t := "2001-02-03"
|
||||
|
||||
if err := d.UnmarshalBinary([]byte(t)); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d)
|
||||
// Output: 2001-02-03
|
||||
}
|
||||
|
||||
func ExampleDate_MarshalJSON() {
|
||||
d, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
j, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(j))
|
||||
// Output: "2001-02-03"
|
||||
}
|
||||
|
||||
func ExampleDate_UnmarshalJSON() {
|
||||
var d struct {
|
||||
Date Date `json:"date"`
|
||||
}
|
||||
j := `{"date" : "2001-02-03"}`
|
||||
|
||||
if err := json.Unmarshal([]byte(j), &d); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d.Date)
|
||||
// Output: 2001-02-03
|
||||
}
|
||||
|
||||
func ExampleDate_MarshalText() {
|
||||
d, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
t, err := d.MarshalText()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(t))
|
||||
// Output: 2001-02-03
|
||||
}
|
||||
|
||||
func ExampleDate_UnmarshalText() {
|
||||
d := Date{}
|
||||
t := "2001-02-03"
|
||||
|
||||
if err := d.UnmarshalText([]byte(t)); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d)
|
||||
// Output: 2001-02-03
|
||||
}
|
||||
|
||||
func TestDateString(t *testing.T) {
|
||||
d, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
t.Fatalf("date: String failed (%v)", err)
|
||||
}
|
||||
if d.String() != "2001-02-03" {
|
||||
t.Fatalf("date: String failed (%v)", d.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateBinaryRoundTrip(t *testing.T) {
|
||||
d1, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
t.Fatalf("date: ParseDate failed (%v)", err)
|
||||
}
|
||||
t1, err := d1.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("date: MarshalBinary failed (%v)", err)
|
||||
}
|
||||
|
||||
d2 := Date{}
|
||||
if err = d2.UnmarshalBinary(t1); err != nil {
|
||||
t.Fatalf("date: UnmarshalBinary failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date: Round-trip Binary failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateJSONRoundTrip(t *testing.T) {
|
||||
type s struct {
|
||||
Date Date `json:"date"`
|
||||
}
|
||||
var err error
|
||||
d1 := s{}
|
||||
d1.Date, err = ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
t.Fatalf("date: ParseDate failed (%v)", err)
|
||||
}
|
||||
|
||||
j, err := json.Marshal(d1)
|
||||
if err != nil {
|
||||
t.Fatalf("date: MarshalJSON failed (%v)", err)
|
||||
}
|
||||
|
||||
d2 := s{}
|
||||
if err = json.Unmarshal(j, &d2); err != nil {
|
||||
t.Fatalf("date: UnmarshalJSON failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date: Round-trip JSON failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateTextRoundTrip(t *testing.T) {
|
||||
d1, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
t.Fatalf("date: ParseDate failed (%v)", err)
|
||||
}
|
||||
t1, err := d1.MarshalText()
|
||||
if err != nil {
|
||||
t.Fatalf("date: MarshalText failed (%v)", err)
|
||||
}
|
||||
d2 := Date{}
|
||||
if err = d2.UnmarshalText(t1); err != nil {
|
||||
t.Fatalf("date: UnmarshalText failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date: Round-trip Text failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateToTime(t *testing.T) {
|
||||
var d Date
|
||||
d, err := ParseDate("2001-02-03")
|
||||
if err != nil {
|
||||
t.Fatalf("date: ParseDate failed (%v)", err)
|
||||
}
|
||||
var _ time.Time = d.ToTime()
|
||||
}
|
||||
|
||||
func TestDateUnmarshalJSONReturnsError(t *testing.T) {
|
||||
var d struct {
|
||||
Date Date `json:"date"`
|
||||
}
|
||||
j := `{"date" : "February 3, 2001"}`
|
||||
|
||||
if err := json.Unmarshal([]byte(j), &d); err == nil {
|
||||
t.Fatal("date: Date failed to return error for malformed JSON date")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateUnmarshalTextReturnsError(t *testing.T) {
|
||||
d := Date{}
|
||||
txt := "February 3, 2001"
|
||||
|
||||
if err := d.UnmarshalText([]byte(txt)); err == nil {
|
||||
t.Fatal("date: Date failed to return error for malformed Text date")
|
||||
}
|
||||
}
|
||||
103
vendor/github.com/Azure/go-autorest/autorest/date/time.go
generated
vendored
Normal file
103
vendor/github.com/Azure/go-autorest/autorest/date/time.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases.
|
||||
const (
|
||||
azureUtcFormatJSON = `"2006-01-02T15:04:05.999999999"`
|
||||
azureUtcFormat = "2006-01-02T15:04:05.999999999"
|
||||
rfc3339JSON = `"` + time.RFC3339Nano + `"`
|
||||
rfc3339 = time.RFC3339Nano
|
||||
tzOffsetRegex = `(Z|z|\+|-)(\d+:\d+)*"*$`
|
||||
)
|
||||
|
||||
// Time defines a type similar to time.Time but assumes a layout of RFC3339 date-time (i.e.,
|
||||
// 2006-01-02T15:04:05Z).
|
||||
type Time struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// MarshalBinary preserves the Time as a byte array conforming to RFC3339 date-time (i.e.,
|
||||
// 2006-01-02T15:04:05Z).
|
||||
func (t Time) MarshalBinary() ([]byte, error) {
|
||||
return t.Time.MarshalText()
|
||||
}
|
||||
|
||||
// UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC3339 date-time
|
||||
// (i.e., 2006-01-02T15:04:05Z).
|
||||
func (t *Time) UnmarshalBinary(data []byte) error {
|
||||
return t.UnmarshalText(data)
|
||||
}
|
||||
|
||||
// MarshalJSON preserves the Time as a JSON string conforming to RFC3339 date-time (i.e.,
|
||||
// 2006-01-02T15:04:05Z).
|
||||
func (t Time) MarshalJSON() (json []byte, err error) {
|
||||
return t.Time.MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC3339 date-time
|
||||
// (i.e., 2006-01-02T15:04:05Z).
|
||||
func (t *Time) UnmarshalJSON(data []byte) (err error) {
|
||||
timeFormat := azureUtcFormatJSON
|
||||
match, err := regexp.Match(tzOffsetRegex, data)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if match {
|
||||
timeFormat = rfc3339JSON
|
||||
}
|
||||
t.Time, err = ParseTime(timeFormat, string(data))
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalText preserves the Time as a byte array conforming to RFC3339 date-time (i.e.,
|
||||
// 2006-01-02T15:04:05Z).
|
||||
func (t Time) MarshalText() (text []byte, err error) {
|
||||
return t.Time.MarshalText()
|
||||
}
|
||||
|
||||
// UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC3339 date-time
|
||||
// (i.e., 2006-01-02T15:04:05Z).
|
||||
func (t *Time) UnmarshalText(data []byte) (err error) {
|
||||
timeFormat := azureUtcFormat
|
||||
match, err := regexp.Match(tzOffsetRegex, data)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if match {
|
||||
timeFormat = rfc3339
|
||||
}
|
||||
t.Time, err = ParseTime(timeFormat, string(data))
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns the Time formatted as an RFC3339 date-time string (i.e.,
|
||||
// 2006-01-02T15:04:05Z).
|
||||
func (t Time) String() string {
|
||||
// Note: time.Time.String does not return an RFC3339 compliant string, time.Time.MarshalText does.
|
||||
b, err := t.MarshalText()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// ToTime returns a Time as a time.Time
|
||||
func (t Time) ToTime() time.Time {
|
||||
return t.Time
|
||||
}
|
||||
277
vendor/github.com/Azure/go-autorest/autorest/date/time_test.go
generated
vendored
Normal file
277
vendor/github.com/Azure/go-autorest/autorest/date/time_test.go
generated
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleParseTime() {
|
||||
d, _ := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
fmt.Println(d)
|
||||
// Output: 2001-02-03 04:05:06 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleTime_MarshalBinary() {
|
||||
ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
d := Time{ti}
|
||||
t, err := d.MarshalBinary()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(t))
|
||||
// Output: 2001-02-03T04:05:06Z
|
||||
}
|
||||
|
||||
func ExampleTime_UnmarshalBinary() {
|
||||
d := Time{}
|
||||
t := "2001-02-03T04:05:06Z"
|
||||
|
||||
if err := d.UnmarshalBinary([]byte(t)); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d)
|
||||
// Output: 2001-02-03T04:05:06Z
|
||||
}
|
||||
|
||||
func ExampleTime_MarshalJSON() {
|
||||
d, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
j, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(j))
|
||||
// Output: "2001-02-03T04:05:06Z"
|
||||
}
|
||||
|
||||
func ExampleTime_UnmarshalJSON() {
|
||||
var d struct {
|
||||
Time Time `json:"datetime"`
|
||||
}
|
||||
j := `{"datetime" : "2001-02-03T04:05:06Z"}`
|
||||
|
||||
if err := json.Unmarshal([]byte(j), &d); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d.Time)
|
||||
// Output: 2001-02-03T04:05:06Z
|
||||
}
|
||||
|
||||
func ExampleTime_MarshalText() {
|
||||
d, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
t, err := d.MarshalText()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(t))
|
||||
// Output: 2001-02-03T04:05:06Z
|
||||
}
|
||||
|
||||
func ExampleTime_UnmarshalText() {
|
||||
d := Time{}
|
||||
t := "2001-02-03T04:05:06Z"
|
||||
|
||||
if err := d.UnmarshalText([]byte(t)); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d)
|
||||
// Output: 2001-02-03T04:05:06Z
|
||||
}
|
||||
|
||||
func TestUnmarshalTextforInvalidDate(t *testing.T) {
|
||||
d := Time{}
|
||||
dt := "2001-02-03T04:05:06AAA"
|
||||
|
||||
if err := d.UnmarshalText([]byte(dt)); err == nil {
|
||||
t.Fatalf("date: Time#Unmarshal was expecting error for invalid date")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSONforInvalidDate(t *testing.T) {
|
||||
d := Time{}
|
||||
dt := `"2001-02-03T04:05:06AAA"`
|
||||
|
||||
if err := d.UnmarshalJSON([]byte(dt)); err == nil {
|
||||
t.Fatalf("date: Time#Unmarshal was expecting error for invalid date")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeString(t *testing.T) {
|
||||
ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
d := Time{ti}
|
||||
if d.String() != "2001-02-03T04:05:06Z" {
|
||||
t.Fatalf("date: Time#String failed (%v)", d.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeStringReturnsEmptyStringForError(t *testing.T) {
|
||||
d := Time{Time: time.Date(20000, 01, 01, 01, 01, 01, 01, time.UTC)}
|
||||
if d.String() != "" {
|
||||
t.Fatalf("date: Time#String failed empty string for an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeBinaryRoundTrip(t *testing.T) {
|
||||
ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
t.Fatalf("date: Time#ParseTime failed (%v)", err)
|
||||
}
|
||||
d1 := Time{ti}
|
||||
t1, err := d1.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("date: Time#MarshalBinary failed (%v)", err)
|
||||
}
|
||||
|
||||
d2 := Time{}
|
||||
if err = d2.UnmarshalBinary(t1); err != nil {
|
||||
t.Fatalf("date: Time#UnmarshalBinary failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date:Round-trip Binary failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeJSONRoundTrip(t *testing.T) {
|
||||
type s struct {
|
||||
Time Time `json:"datetime"`
|
||||
}
|
||||
|
||||
ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
t.Fatalf("date: Time#ParseTime failed (%v)", err)
|
||||
}
|
||||
|
||||
d1 := s{Time: Time{ti}}
|
||||
j, err := json.Marshal(d1)
|
||||
if err != nil {
|
||||
t.Fatalf("date: Time#MarshalJSON failed (%v)", err)
|
||||
}
|
||||
|
||||
d2 := s{}
|
||||
if err = json.Unmarshal(j, &d2); err != nil {
|
||||
t.Fatalf("date: Time#UnmarshalJSON failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date: Round-trip JSON failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeTextRoundTrip(t *testing.T) {
|
||||
ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
t.Fatalf("date: Time#ParseTime failed (%v)", err)
|
||||
}
|
||||
d1 := Time{Time: ti}
|
||||
t1, err := d1.MarshalText()
|
||||
if err != nil {
|
||||
t.Fatalf("date: Time#MarshalText failed (%v)", err)
|
||||
}
|
||||
|
||||
d2 := Time{}
|
||||
if err = d2.UnmarshalText(t1); err != nil {
|
||||
t.Fatalf("date: Time#UnmarshalText failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date: Round-trip Text failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeToTime(t *testing.T) {
|
||||
ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
d := Time{ti}
|
||||
if err != nil {
|
||||
t.Fatalf("date: Time#ParseTime failed (%v)", err)
|
||||
}
|
||||
var _ time.Time = d.ToTime()
|
||||
}
|
||||
|
||||
func TestUnmarshalJSONNoOffset(t *testing.T) {
|
||||
var d struct {
|
||||
Time Time `json:"datetime"`
|
||||
}
|
||||
j := `{"datetime" : "2001-02-03T04:05:06.789"}`
|
||||
|
||||
if err := json.Unmarshal([]byte(j), &d); err != nil {
|
||||
t.Fatalf("date: Time#Unmarshal failed (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSONPosOffset(t *testing.T) {
|
||||
var d struct {
|
||||
Time Time `json:"datetime"`
|
||||
}
|
||||
j := `{"datetime" : "1980-01-02T00:11:35.01+01:00"}`
|
||||
|
||||
if err := json.Unmarshal([]byte(j), &d); err != nil {
|
||||
t.Fatalf("date: Time#Unmarshal failed (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSONNegOffset(t *testing.T) {
|
||||
var d struct {
|
||||
Time Time `json:"datetime"`
|
||||
}
|
||||
j := `{"datetime" : "1492-10-12T10:15:01.789-08:00"}`
|
||||
|
||||
if err := json.Unmarshal([]byte(j), &d); err != nil {
|
||||
t.Fatalf("date: Time#Unmarshal failed (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalTextNoOffset(t *testing.T) {
|
||||
d := Time{}
|
||||
t1 := "2001-02-03T04:05:06"
|
||||
|
||||
if err := d.UnmarshalText([]byte(t1)); err != nil {
|
||||
t.Fatalf("date: Time#UnmarshalText failed (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalTextPosOffset(t *testing.T) {
|
||||
d := Time{}
|
||||
t1 := "2001-02-03T04:05:06+00:30"
|
||||
|
||||
if err := d.UnmarshalText([]byte(t1)); err != nil {
|
||||
t.Fatalf("date: Time#UnmarshalText failed (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalTextNegOffset(t *testing.T) {
|
||||
d := Time{}
|
||||
t1 := "2001-02-03T04:05:06-11:00"
|
||||
|
||||
if err := d.UnmarshalText([]byte(t1)); err != nil {
|
||||
t.Fatalf("date: Time#UnmarshalText failed (%v)", err)
|
||||
}
|
||||
}
|
||||
100
vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go
generated
vendored
Normal file
100
vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
rfc1123JSON = `"` + time.RFC1123 + `"`
|
||||
rfc1123 = time.RFC1123
|
||||
)
|
||||
|
||||
// TimeRFC1123 defines a type similar to time.Time but assumes a layout of RFC1123 date-time (i.e.,
|
||||
// Mon, 02 Jan 2006 15:04:05 MST).
|
||||
type TimeRFC1123 struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC1123 date-time
|
||||
// (i.e., Mon, 02 Jan 2006 15:04:05 MST).
|
||||
func (t *TimeRFC1123) UnmarshalJSON(data []byte) (err error) {
|
||||
t.Time, err = ParseTime(rfc1123JSON, string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON preserves the Time as a JSON string conforming to RFC1123 date-time (i.e.,
|
||||
// Mon, 02 Jan 2006 15:04:05 MST).
|
||||
func (t TimeRFC1123) MarshalJSON() ([]byte, error) {
|
||||
if y := t.Year(); y < 0 || y >= 10000 {
|
||||
return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
|
||||
}
|
||||
b := []byte(t.Format(rfc1123JSON))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// MarshalText preserves the Time as a byte array conforming to RFC1123 date-time (i.e.,
|
||||
// Mon, 02 Jan 2006 15:04:05 MST).
|
||||
func (t TimeRFC1123) MarshalText() ([]byte, error) {
|
||||
if y := t.Year(); y < 0 || y >= 10000 {
|
||||
return nil, errors.New("Time.MarshalText: year outside of range [0,9999]")
|
||||
}
|
||||
|
||||
b := []byte(t.Format(rfc1123))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC1123 date-time
|
||||
// (i.e., Mon, 02 Jan 2006 15:04:05 MST).
|
||||
func (t *TimeRFC1123) UnmarshalText(data []byte) (err error) {
|
||||
t.Time, err = ParseTime(rfc1123, string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary preserves the Time as a byte array conforming to RFC1123 date-time (i.e.,
|
||||
// Mon, 02 Jan 2006 15:04:05 MST).
|
||||
func (t TimeRFC1123) MarshalBinary() ([]byte, error) {
|
||||
return t.MarshalText()
|
||||
}
|
||||
|
||||
// UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC1123 date-time
|
||||
// (i.e., Mon, 02 Jan 2006 15:04:05 MST).
|
||||
func (t *TimeRFC1123) UnmarshalBinary(data []byte) error {
|
||||
return t.UnmarshalText(data)
|
||||
}
|
||||
|
||||
// ToTime returns a Time as a time.Time
|
||||
func (t TimeRFC1123) ToTime() time.Time {
|
||||
return t.Time
|
||||
}
|
||||
|
||||
// String returns the Time formatted as an RFC1123 date-time string (i.e.,
|
||||
// Mon, 02 Jan 2006 15:04:05 MST).
|
||||
func (t TimeRFC1123) String() string {
|
||||
// Note: time.Time.String does not return an RFC1123 compliant string, time.Time.MarshalText does.
|
||||
b, err := t.MarshalText()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
226
vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123_test.go
generated
vendored
Normal file
226
vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123_test.go
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleTimeRFC1123() {
|
||||
d, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d)
|
||||
// Output: 2006-01-02 15:04:05 +0000 MST
|
||||
}
|
||||
|
||||
func ExampleTimeRFC1123_MarshalBinary() {
|
||||
ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
d := TimeRFC1123{ti}
|
||||
b, err := d.MarshalBinary()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
// Output: Mon, 02 Jan 2006 15:04:05 MST
|
||||
}
|
||||
|
||||
func ExampleTimeRFC1123_UnmarshalBinary() {
|
||||
d := TimeRFC1123{}
|
||||
t := "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
if err := d.UnmarshalBinary([]byte(t)); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d)
|
||||
// Output: Mon, 02 Jan 2006 15:04:05 MST
|
||||
}
|
||||
|
||||
func ExampleTimeRFC1123_MarshalJSON() {
|
||||
ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
d := TimeRFC1123{ti}
|
||||
j, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(j))
|
||||
// Output: "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
}
|
||||
|
||||
func TestTimeRFC1123MarshalJSONInvalid(t *testing.T) {
|
||||
ti := time.Date(20000, 01, 01, 00, 00, 00, 00, time.UTC)
|
||||
d := TimeRFC1123{ti}
|
||||
if _, err := json.Marshal(d); err == nil {
|
||||
t.Fatalf("date: TimeRFC1123#Marshal failed for invalid date")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleTimeRFC1123_UnmarshalJSON() {
|
||||
var d struct {
|
||||
Time TimeRFC1123 `json:"datetime"`
|
||||
}
|
||||
j := `{"datetime" : "Mon, 02 Jan 2006 15:04:05 MST"}`
|
||||
|
||||
if err := json.Unmarshal([]byte(j), &d); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d.Time)
|
||||
// Output: Mon, 02 Jan 2006 15:04:05 MST
|
||||
}
|
||||
|
||||
func ExampleTimeRFC1123_MarshalText() {
|
||||
ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
d := TimeRFC1123{ti}
|
||||
t, err := d.MarshalText()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(string(t))
|
||||
// Output: Sat, 03 Feb 2001 04:05:06 UTC
|
||||
}
|
||||
|
||||
func ExampleTimeRFC1123_UnmarshalText() {
|
||||
d := TimeRFC1123{}
|
||||
t := "Sat, 03 Feb 2001 04:05:06 UTC"
|
||||
|
||||
if err := d.UnmarshalText([]byte(t)); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(d)
|
||||
// Output: Sat, 03 Feb 2001 04:05:06 UTC
|
||||
}
|
||||
|
||||
func TestUnmarshalJSONforInvalidDateRfc1123(t *testing.T) {
|
||||
dt := `"Mon, 02 Jan 2000000 15:05 MST"`
|
||||
d := TimeRFC1123{}
|
||||
if err := d.UnmarshalJSON([]byte(dt)); err == nil {
|
||||
t.Fatalf("date: TimeRFC1123#Unmarshal failed for invalid date")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalTextforInvalidDateRfc1123(t *testing.T) {
|
||||
dt := "Mon, 02 Jan 2000000 15:05 MST"
|
||||
d := TimeRFC1123{}
|
||||
if err := d.UnmarshalText([]byte(dt)); err == nil {
|
||||
t.Fatalf("date: TimeRFC1123#Unmarshal failed for invalid date")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeStringRfc1123(t *testing.T) {
|
||||
ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
d := TimeRFC1123{ti}
|
||||
if d.String() != "Mon, 02 Jan 2006 15:04:05 MST" {
|
||||
t.Fatalf("date: TimeRFC1123#String failed (%v)", d.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeStringReturnsEmptyStringForErrorRfc1123(t *testing.T) {
|
||||
d := TimeRFC1123{Time: time.Date(20000, 01, 01, 01, 01, 01, 01, time.UTC)}
|
||||
if d.String() != "" {
|
||||
t.Fatalf("date: TimeRFC1123#String failed empty string for an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeBinaryRoundTripRfc1123(t *testing.T) {
|
||||
ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z")
|
||||
if err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#ParseTime failed (%v)", err)
|
||||
}
|
||||
d1 := TimeRFC1123{ti}
|
||||
t1, err := d1.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#MarshalBinary failed (%v)", err)
|
||||
}
|
||||
|
||||
d2 := TimeRFC1123{}
|
||||
if err = d2.UnmarshalBinary(t1); err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#UnmarshalBinary failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date: Round-trip Binary failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeJSONRoundTripRfc1123(t *testing.T) {
|
||||
type s struct {
|
||||
Time TimeRFC1123 `json:"datetime"`
|
||||
}
|
||||
var err error
|
||||
ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST")
|
||||
if err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#ParseTime failed (%v)", err)
|
||||
}
|
||||
d1 := s{Time: TimeRFC1123{ti}}
|
||||
j, err := json.Marshal(d1)
|
||||
if err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#MarshalJSON failed (%v)", err)
|
||||
}
|
||||
|
||||
d2 := s{}
|
||||
if err = json.Unmarshal(j, &d2); err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#UnmarshalJSON failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date: Round-trip JSON failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeTextRoundTripRfc1123(t *testing.T) {
|
||||
ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST")
|
||||
if err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#ParseTime failed (%v)", err)
|
||||
}
|
||||
d1 := TimeRFC1123{Time: ti}
|
||||
t1, err := d1.MarshalText()
|
||||
if err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#MarshalText failed (%v)", err)
|
||||
}
|
||||
|
||||
d2 := TimeRFC1123{}
|
||||
if err = d2.UnmarshalText(t1); err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#UnmarshalText failed (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d1, d2) {
|
||||
t.Fatalf("date: Round-trip Text failed (%v, %v)", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeToTimeRFC1123(t *testing.T) {
|
||||
ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST")
|
||||
d := TimeRFC1123{ti}
|
||||
if err != nil {
|
||||
t.Fatalf("date: TimeRFC1123#ParseTime failed (%v)", err)
|
||||
}
|
||||
var _ time.Time = d.ToTime()
|
||||
}
|
||||
123
vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go
generated
vendored
Normal file
123
vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// unixEpoch is the moment in time that should be treated as timestamp 0.
|
||||
var unixEpoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
// UnixTime marshals and unmarshals a time that is represented as the number
|
||||
// of seconds (ignoring skip-seconds) since the Unix Epoch.
|
||||
type UnixTime time.Time
|
||||
|
||||
// Duration returns the time as a Duration since the UnixEpoch.
|
||||
func (t UnixTime) Duration() time.Duration {
|
||||
return time.Time(t).Sub(unixEpoch)
|
||||
}
|
||||
|
||||
// NewUnixTimeFromSeconds creates a UnixTime as a number of seconds from the UnixEpoch.
|
||||
func NewUnixTimeFromSeconds(seconds float64) UnixTime {
|
||||
return NewUnixTimeFromDuration(time.Duration(seconds * float64(time.Second)))
|
||||
}
|
||||
|
||||
// NewUnixTimeFromNanoseconds creates a UnixTime as a number of nanoseconds from the UnixEpoch.
|
||||
func NewUnixTimeFromNanoseconds(nanoseconds int64) UnixTime {
|
||||
return NewUnixTimeFromDuration(time.Duration(nanoseconds))
|
||||
}
|
||||
|
||||
// NewUnixTimeFromDuration creates a UnixTime as a duration of time since the UnixEpoch.
|
||||
func NewUnixTimeFromDuration(dur time.Duration) UnixTime {
|
||||
return UnixTime(unixEpoch.Add(dur))
|
||||
}
|
||||
|
||||
// UnixEpoch retreives the moment considered the Unix Epoch. I.e. The time represented by '0'
|
||||
func UnixEpoch() time.Time {
|
||||
return unixEpoch
|
||||
}
|
||||
|
||||
// MarshalJSON preserves the UnixTime as a JSON number conforming to Unix Timestamp requirements.
|
||||
// (i.e. the number of seconds since midnight January 1st, 1970 not considering leap seconds.)
|
||||
func (t UnixTime) MarshalJSON() ([]byte, error) {
|
||||
buffer := &bytes.Buffer{}
|
||||
enc := json.NewEncoder(buffer)
|
||||
err := enc.Encode(float64(time.Time(t).UnixNano()) / 1e9)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON reconstitures a UnixTime saved as a JSON number of the number of seconds since
|
||||
// midnight January 1st, 1970.
|
||||
func (t *UnixTime) UnmarshalJSON(text []byte) error {
|
||||
dec := json.NewDecoder(bytes.NewReader(text))
|
||||
|
||||
var secondsSinceEpoch float64
|
||||
if err := dec.Decode(&secondsSinceEpoch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = NewUnixTimeFromSeconds(secondsSinceEpoch)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText stores the number of seconds since the Unix Epoch as a textual floating point number.
|
||||
func (t UnixTime) MarshalText() ([]byte, error) {
|
||||
cast := time.Time(t)
|
||||
return cast.MarshalText()
|
||||
}
|
||||
|
||||
// UnmarshalText populates a UnixTime with a value stored textually as a floating point number of seconds since the Unix Epoch.
|
||||
func (t *UnixTime) UnmarshalText(raw []byte) error {
|
||||
var unmarshaled time.Time
|
||||
|
||||
if err := unmarshaled.UnmarshalText(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = UnixTime(unmarshaled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary converts a UnixTime into a binary.LittleEndian float64 of nanoseconds since the epoch.
|
||||
func (t UnixTime) MarshalBinary() ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
payload := int64(t.Duration())
|
||||
|
||||
if err := binary.Write(buf, binary.LittleEndian, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary converts a from a binary.LittleEndian float64 of nanoseconds since the epoch into a UnixTime.
|
||||
func (t *UnixTime) UnmarshalBinary(raw []byte) error {
|
||||
var nanosecondsSinceEpoch int64
|
||||
|
||||
if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, &nanosecondsSinceEpoch); err != nil {
|
||||
return err
|
||||
}
|
||||
*t = NewUnixTimeFromNanoseconds(nanosecondsSinceEpoch)
|
||||
return nil
|
||||
}
|
||||
283
vendor/github.com/Azure/go-autorest/autorest/date/unixtime_test.go
generated
vendored
Normal file
283
vendor/github.com/Azure/go-autorest/autorest/date/unixtime_test.go
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
// +build go1.7
|
||||
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleUnixTime_MarshalJSON() {
|
||||
epoch := UnixTime(UnixEpoch())
|
||||
text, _ := json.Marshal(epoch)
|
||||
fmt.Print(string(text))
|
||||
// Output: 0
|
||||
}
|
||||
|
||||
func ExampleUnixTime_UnmarshalJSON() {
|
||||
var myTime UnixTime
|
||||
json.Unmarshal([]byte("1.3e2"), &myTime)
|
||||
fmt.Printf("%v", time.Time(myTime))
|
||||
// Output: 1970-01-01 00:02:10 +0000 UTC
|
||||
}
|
||||
|
||||
func TestUnixTime_MarshalJSON(t *testing.T) {
|
||||
testCases := []time.Time{
|
||||
UnixEpoch().Add(-1 * time.Second), // One second befote the Unix Epoch
|
||||
time.Date(2017, time.April, 14, 20, 27, 47, 0, time.UTC), // The time this test was written
|
||||
UnixEpoch(),
|
||||
time.Date(1800, 01, 01, 0, 0, 0, 0, time.UTC),
|
||||
time.Date(2200, 12, 29, 00, 01, 37, 82, time.UTC),
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.String(), func(subT *testing.T) {
|
||||
var actual, expected float64
|
||||
var marshaled []byte
|
||||
|
||||
target := UnixTime(tc)
|
||||
expected = float64(target.Duration().Nanoseconds()) / 1e9
|
||||
|
||||
if temp, err := json.Marshal(target); err == nil {
|
||||
marshaled = temp
|
||||
} else {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(marshaled))
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
diff := math.Abs(actual - expected)
|
||||
subT.Logf("\ngot :\t%g\nwant:\t%g\ndiff:\t%g", actual, expected, diff)
|
||||
if diff > 1e-9 { //Must be within 1 nanosecond of one another
|
||||
subT.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTime_UnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
text string
|
||||
expected time.Time
|
||||
}{
|
||||
{"1", UnixEpoch().Add(time.Second)},
|
||||
{"0", UnixEpoch()},
|
||||
{"1492203742", time.Date(2017, time.April, 14, 21, 02, 22, 0, time.UTC)}, // The time this test was written
|
||||
{"-1", time.Date(1969, time.December, 31, 23, 59, 59, 0, time.UTC)},
|
||||
{"1.5", UnixEpoch().Add(1500 * time.Millisecond)},
|
||||
{"0e1", UnixEpoch()}, // See http://json.org for 'number' format definition.
|
||||
{"1.3e+2", UnixEpoch().Add(130 * time.Second)},
|
||||
{"1.6E-10", UnixEpoch()}, // This is so small, it should get truncated into the UnixEpoch
|
||||
{"2E-6", UnixEpoch().Add(2 * time.Microsecond)},
|
||||
{"1.289345e9", UnixEpoch().Add(1289345000 * time.Second)},
|
||||
{"1e-9", UnixEpoch().Add(time.Nanosecond)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.text, func(subT *testing.T) {
|
||||
var rehydrated UnixTime
|
||||
if err := json.Unmarshal([]byte(tc.text), &rehydrated); err != nil {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if time.Time(rehydrated) != tc.expected {
|
||||
subT.Logf("\ngot: \t%v\nwant:\t%v\ndiff:\t%v", time.Time(rehydrated), tc.expected, time.Time(rehydrated).Sub(tc.expected))
|
||||
subT.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTime_JSONRoundTrip(t *testing.T) {
|
||||
testCases := []time.Time{
|
||||
UnixEpoch(),
|
||||
time.Date(2005, time.November, 5, 0, 0, 0, 0, time.UTC), // The day V for Vendetta (film) was released.
|
||||
UnixEpoch().Add(-6 * time.Second),
|
||||
UnixEpoch().Add(800 * time.Hour),
|
||||
UnixEpoch().Add(time.Nanosecond),
|
||||
time.Date(2015, time.September, 05, 4, 30, 12, 9992, time.UTC),
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.String(), func(subT *testing.T) {
|
||||
subject := UnixTime(tc)
|
||||
var marshaled []byte
|
||||
if temp, err := json.Marshal(subject); err == nil {
|
||||
marshaled = temp
|
||||
} else {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var unmarshaled UnixTime
|
||||
if err := json.Unmarshal(marshaled, &unmarshaled); err != nil {
|
||||
subT.Error(err)
|
||||
}
|
||||
|
||||
actual := time.Time(unmarshaled)
|
||||
diff := actual.Sub(tc)
|
||||
subT.Logf("\ngot :\t%s\nwant:\t%s\ndiff:\t%s", actual.String(), tc.String(), diff.String())
|
||||
|
||||
if diff > time.Duration(100) { // We lose some precision be working in floats. We shouldn't lose more than 100 nanoseconds.
|
||||
subT.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTime_MarshalBinary(t *testing.T) {
|
||||
testCases := []struct {
|
||||
expected int64
|
||||
subject time.Time
|
||||
}{
|
||||
{0, UnixEpoch()},
|
||||
{-15 * int64(time.Second), UnixEpoch().Add(-15 * time.Second)},
|
||||
{54, UnixEpoch().Add(54 * time.Nanosecond)},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run("", func(subT *testing.T) {
|
||||
var marshaled []byte
|
||||
|
||||
if temp, err := UnixTime(tc.subject).MarshalBinary(); err == nil {
|
||||
marshaled = temp
|
||||
} else {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var unmarshaled int64
|
||||
if err := binary.Read(bytes.NewReader(marshaled), binary.LittleEndian, &unmarshaled); err != nil {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if unmarshaled != tc.expected {
|
||||
subT.Logf("\ngot: \t%d\nwant:\t%d", unmarshaled, tc.expected)
|
||||
subT.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTime_BinaryRoundTrip(t *testing.T) {
|
||||
testCases := []time.Time{
|
||||
UnixEpoch(),
|
||||
UnixEpoch().Add(800 * time.Minute),
|
||||
UnixEpoch().Add(7 * time.Hour),
|
||||
UnixEpoch().Add(-1 * time.Nanosecond),
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.String(), func(subT *testing.T) {
|
||||
original := UnixTime(tc)
|
||||
var marshaled []byte
|
||||
|
||||
if temp, err := original.MarshalBinary(); err == nil {
|
||||
marshaled = temp
|
||||
} else {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var traveled UnixTime
|
||||
if err := traveled.UnmarshalBinary(marshaled); err != nil {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if traveled != original {
|
||||
subT.Logf("\ngot: \t%s\nwant:\t%s", time.Time(original).String(), time.Time(traveled).String())
|
||||
subT.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTime_MarshalText(t *testing.T) {
|
||||
testCases := []time.Time{
|
||||
UnixEpoch(),
|
||||
UnixEpoch().Add(45 * time.Second),
|
||||
UnixEpoch().Add(time.Nanosecond),
|
||||
UnixEpoch().Add(-100000 * time.Second),
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
expected, _ := tc.MarshalText()
|
||||
t.Run("", func(subT *testing.T) {
|
||||
var marshaled []byte
|
||||
|
||||
if temp, err := UnixTime(tc).MarshalText(); err == nil {
|
||||
marshaled = temp
|
||||
} else {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if string(marshaled) != string(expected) {
|
||||
subT.Logf("\ngot: \t%s\nwant:\t%s", string(marshaled), string(expected))
|
||||
subT.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnixTime_TextRoundTrip(t *testing.T) {
|
||||
testCases := []time.Time{
|
||||
UnixEpoch(),
|
||||
UnixEpoch().Add(-1 * time.Nanosecond),
|
||||
UnixEpoch().Add(1 * time.Nanosecond),
|
||||
time.Date(2017, time.April, 17, 21, 00, 00, 00, time.UTC),
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.String(), func(subT *testing.T) {
|
||||
unixTC := UnixTime(tc)
|
||||
|
||||
var marshaled []byte
|
||||
|
||||
if temp, err := unixTC.MarshalText(); err == nil {
|
||||
marshaled = temp
|
||||
} else {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var unmarshaled UnixTime
|
||||
if err := unmarshaled.UnmarshalText(marshaled); err != nil {
|
||||
subT.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if unmarshaled != unixTC {
|
||||
t.Logf("\ngot: \t%s\nwant:\t%s", time.Time(unmarshaled).String(), tc.String())
|
||||
t.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
25
vendor/github.com/Azure/go-autorest/autorest/date/utility.go
generated
vendored
Normal file
25
vendor/github.com/Azure/go-autorest/autorest/date/utility.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package date
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ParseTime to parse Time string to specified format.
|
||||
func ParseTime(format string, t string) (d time.Time, err error) {
|
||||
return time.Parse(format, strings.ToUpper(t))
|
||||
}
|
||||
98
vendor/github.com/Azure/go-autorest/autorest/error.go
generated
vendored
Normal file
98
vendor/github.com/Azure/go-autorest/autorest/error.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
// UndefinedStatusCode is used when HTTP status code is not available for an error.
|
||||
UndefinedStatusCode = 0
|
||||
)
|
||||
|
||||
// DetailedError encloses a error with details of the package, method, and associated HTTP
|
||||
// status code (if any).
|
||||
type DetailedError struct {
|
||||
Original error
|
||||
|
||||
// PackageType is the package type of the object emitting the error. For types, the value
|
||||
// matches that produced the the '%T' format specifier of the fmt package. For other elements,
|
||||
// such as functions, it is just the package name (e.g., "autorest").
|
||||
PackageType string
|
||||
|
||||
// Method is the name of the method raising the error.
|
||||
Method string
|
||||
|
||||
// StatusCode is the HTTP Response StatusCode (if non-zero) that led to the error.
|
||||
StatusCode interface{}
|
||||
|
||||
// Message is the error message.
|
||||
Message string
|
||||
|
||||
// Service Error is the response body of failed API in bytes
|
||||
ServiceError []byte
|
||||
|
||||
// Response is the response object that was returned during failure if applicable.
|
||||
Response *http.Response
|
||||
}
|
||||
|
||||
// NewError creates a new Error conforming object from the passed packageType, method, and
|
||||
// message. message is treated as a format string to which the optional args apply.
|
||||
func NewError(packageType string, method string, message string, args ...interface{}) DetailedError {
|
||||
return NewErrorWithError(nil, packageType, method, nil, message, args...)
|
||||
}
|
||||
|
||||
// NewErrorWithResponse creates a new Error conforming object from the passed
|
||||
// packageType, method, statusCode of the given resp (UndefinedStatusCode if
|
||||
// resp is nil), and message. message is treated as a format string to which the
|
||||
// optional args apply.
|
||||
func NewErrorWithResponse(packageType string, method string, resp *http.Response, message string, args ...interface{}) DetailedError {
|
||||
return NewErrorWithError(nil, packageType, method, resp, message, args...)
|
||||
}
|
||||
|
||||
// NewErrorWithError creates a new Error conforming object from the
|
||||
// passed packageType, method, statusCode of the given resp (UndefinedStatusCode
|
||||
// if resp is nil), message, and original error. message is treated as a format
|
||||
// string to which the optional args apply.
|
||||
func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) DetailedError {
|
||||
if v, ok := original.(DetailedError); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
statusCode := UndefinedStatusCode
|
||||
if resp != nil {
|
||||
statusCode = resp.StatusCode
|
||||
}
|
||||
|
||||
return DetailedError{
|
||||
Original: original,
|
||||
PackageType: packageType,
|
||||
Method: method,
|
||||
StatusCode: statusCode,
|
||||
Message: fmt.Sprintf(message, args...),
|
||||
Response: resp,
|
||||
}
|
||||
}
|
||||
|
||||
// Error returns a formatted containing all available details (i.e., PackageType, Method,
|
||||
// StatusCode, Message, and original error (if any)).
|
||||
func (e DetailedError) Error() string {
|
||||
if e.Original == nil {
|
||||
return fmt.Sprintf("%s#%s: %s: StatusCode=%d", e.PackageType, e.Method, e.Message, e.StatusCode)
|
||||
}
|
||||
return fmt.Sprintf("%s#%s: %s: StatusCode=%d -- Original Error: %v", e.PackageType, e.Method, e.Message, e.StatusCode, e.Original)
|
||||
}
|
||||
202
vendor/github.com/Azure/go-autorest/autorest/error_test.go
generated
vendored
Normal file
202
vendor/github.com/Azure/go-autorest/autorest/error_test.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewErrorWithError_AssignsPackageType(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message")
|
||||
|
||||
if e.PackageType != "packageType" {
|
||||
t.Fatalf("autorest: Error failed to set package type -- expected %v, received %v", "packageType", e.PackageType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_AssignsMethod(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message")
|
||||
|
||||
if e.Method != "method" {
|
||||
t.Fatalf("autorest: Error failed to set method -- expected %v, received %v", "method", e.Method)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_AssignsMessage(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message")
|
||||
|
||||
if e.Message != "message" {
|
||||
t.Fatalf("autorest: Error failed to set message -- expected %v, received %v", "message", e.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_AssignsUndefinedStatusCodeIfRespNil(t *testing.T) {
|
||||
e := NewErrorWithError(nil, "packageType", "method", nil, "message")
|
||||
if e.StatusCode != UndefinedStatusCode {
|
||||
t.Fatalf("autorest: Error failed to set status code -- expected %v, received %v", UndefinedStatusCode, e.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_AssignsStatusCode(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", &http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
Status: http.StatusText(http.StatusBadRequest)}, "message")
|
||||
|
||||
if e.StatusCode != http.StatusBadRequest {
|
||||
t.Fatalf("autorest: Error failed to set status code -- expected %v, received %v", http.StatusBadRequest, e.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_AcceptsArgs(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message %s", "arg")
|
||||
|
||||
if matched, _ := regexp.MatchString(`.*arg.*`, e.Message); !matched {
|
||||
t.Fatalf("autorest: Error failed to apply message arguments -- expected %v, received %v",
|
||||
`.*arg.*`, e.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_AssignsError(t *testing.T) {
|
||||
err := fmt.Errorf("original")
|
||||
e := NewErrorWithError(err, "packageType", "method", nil, "message")
|
||||
|
||||
if e.Original != err {
|
||||
t.Fatalf("autorest: Error failed to set error -- expected %v, received %v", err, e.Original)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithResponse_ContainsStatusCode(t *testing.T) {
|
||||
e := NewErrorWithResponse("packageType", "method", &http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
Status: http.StatusText(http.StatusBadRequest)}, "message")
|
||||
|
||||
if e.StatusCode != http.StatusBadRequest {
|
||||
t.Fatalf("autorest: Error failed to set status code -- expected %v, received %v", http.StatusBadRequest, e.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithResponse_nilResponse_ReportsUndefinedStatusCode(t *testing.T) {
|
||||
e := NewErrorWithResponse("packageType", "method", nil, "message")
|
||||
|
||||
if e.StatusCode != UndefinedStatusCode {
|
||||
t.Fatalf("autorest: Error failed to set status code -- expected %v, received %v", UndefinedStatusCode, e.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithResponse_Forwards(t *testing.T) {
|
||||
e1 := NewError("packageType", "method", "message %s", "arg")
|
||||
e2 := NewErrorWithResponse("packageType", "method", nil, "message %s", "arg")
|
||||
|
||||
if !reflect.DeepEqual(e1, e2) {
|
||||
t.Fatal("autorest: NewError did not return an error equivelent to NewErrorWithError")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_Forwards(t *testing.T) {
|
||||
e1 := NewError("packageType", "method", "message %s", "arg")
|
||||
e2 := NewErrorWithError(nil, "packageType", "method", nil, "message %s", "arg")
|
||||
|
||||
if !reflect.DeepEqual(e1, e2) {
|
||||
t.Fatal("autorest: NewError did not return an error equivelent to NewErrorWithError")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_DoesNotWrapADetailedError(t *testing.T) {
|
||||
e1 := NewError("packageType1", "method1", "message1 %s", "arg1")
|
||||
e2 := NewErrorWithError(e1, "packageType2", "method2", nil, "message2 %s", "arg2")
|
||||
|
||||
if !reflect.DeepEqual(e1, e2) {
|
||||
t.Fatalf("autorest: NewErrorWithError incorrectly wrapped a DetailedError -- expected %v, received %v", e1, e2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewErrorWithError_WrapsAnError(t *testing.T) {
|
||||
e1 := fmt.Errorf("Inner Error")
|
||||
var e2 interface{} = NewErrorWithError(e1, "packageType", "method", nil, "message")
|
||||
|
||||
if _, ok := e2.(DetailedError); !ok {
|
||||
t.Fatalf("autorest: NewErrorWithError failed to wrap a standard error -- received %T", e2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetailedError(t *testing.T) {
|
||||
err := fmt.Errorf("original")
|
||||
e := NewErrorWithError(err, "packageType", "method", nil, "message")
|
||||
|
||||
if matched, _ := regexp.MatchString(`.*original.*`, e.Error()); !matched {
|
||||
t.Fatalf("autorest: Error#Error failed to return original error message -- expected %v, received %v",
|
||||
`.*original.*`, e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetailedErrorConstainsPackageType(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message")
|
||||
|
||||
if matched, _ := regexp.MatchString(`.*packageType.*`, e.Error()); !matched {
|
||||
t.Fatalf("autorest: Error#String failed to include PackageType -- expected %v, received %v",
|
||||
`.*packageType.*`, e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetailedErrorConstainsMethod(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message")
|
||||
|
||||
if matched, _ := regexp.MatchString(`.*method.*`, e.Error()); !matched {
|
||||
t.Fatalf("autorest: Error#String failed to include Method -- expected %v, received %v",
|
||||
`.*method.*`, e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetailedErrorConstainsMessage(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message")
|
||||
|
||||
if matched, _ := regexp.MatchString(`.*message.*`, e.Error()); !matched {
|
||||
t.Fatalf("autorest: Error#String failed to include Message -- expected %v, received %v",
|
||||
`.*message.*`, e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetailedErrorConstainsStatusCode(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", &http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
Status: http.StatusText(http.StatusBadRequest)}, "message")
|
||||
|
||||
if matched, _ := regexp.MatchString(`.*400.*`, e.Error()); !matched {
|
||||
t.Fatalf("autorest: Error#String failed to include Status Code -- expected %v, received %v",
|
||||
`.*400.*`, e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetailedErrorConstainsOriginal(t *testing.T) {
|
||||
e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message")
|
||||
|
||||
if matched, _ := regexp.MatchString(`.*original.*`, e.Error()); !matched {
|
||||
t.Fatalf("autorest: Error#String failed to include Original error -- expected %v, received %v",
|
||||
`.*original.*`, e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetailedErrorSkipsOriginal(t *testing.T) {
|
||||
e := NewError("packageType", "method", "message")
|
||||
|
||||
if matched, _ := regexp.MatchString(`.*Original.*`, e.Error()); matched {
|
||||
t.Fatalf("autorest: Error#String included missing Original error -- unexpected %v, received %v",
|
||||
`.*Original.*`, e.Error())
|
||||
}
|
||||
}
|
||||
151
vendor/github.com/Azure/go-autorest/autorest/mocks/helpers.go
generated
vendored
Normal file
151
vendor/github.com/Azure/go-autorest/autorest/mocks/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
package mocks
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// TestAuthorizationHeader is a faux HTTP Authorization header value
|
||||
TestAuthorizationHeader = "BEARER SECRETTOKEN"
|
||||
|
||||
// TestBadURL is a malformed URL
|
||||
TestBadURL = " "
|
||||
|
||||
// TestDelay is the Retry-After delay used in tests.
|
||||
TestDelay = 0 * time.Second
|
||||
|
||||
// TestHeader is the header used in tests.
|
||||
TestHeader = "x-test-header"
|
||||
|
||||
// TestURL is the URL used in tests.
|
||||
TestURL = "https://microsoft.com/a/b/c/"
|
||||
|
||||
// TestAzureAsyncURL is a URL used in Azure asynchronous tests
|
||||
TestAzureAsyncURL = "https://microsoft.com/a/b/c/async"
|
||||
|
||||
// TestLocationURL is a URL used in Azure asynchronous tests
|
||||
TestLocationURL = "https://microsoft.com/a/b/c/location"
|
||||
)
|
||||
|
||||
const (
|
||||
headerLocation = "Location"
|
||||
headerRetryAfter = "Retry-After"
|
||||
)
|
||||
|
||||
// NewRequest instantiates a new request.
|
||||
func NewRequest() *http.Request {
|
||||
return NewRequestWithContent("")
|
||||
}
|
||||
|
||||
// NewRequestWithContent instantiates a new request using the passed string for the body content.
|
||||
func NewRequestWithContent(c string) *http.Request {
|
||||
r, _ := http.NewRequest("GET", "https://microsoft.com/a/b/c/", NewBody(c))
|
||||
return r
|
||||
}
|
||||
|
||||
// NewRequestWithCloseBody instantiates a new request.
|
||||
func NewRequestWithCloseBody() *http.Request {
|
||||
return NewRequestWithCloseBodyContent("request body")
|
||||
}
|
||||
|
||||
// NewRequestWithCloseBodyContent instantiates a new request using the passed string for the body content.
|
||||
func NewRequestWithCloseBodyContent(c string) *http.Request {
|
||||
r, _ := http.NewRequest("GET", "https://microsoft.com/a/b/c/", NewBodyClose(c))
|
||||
return r
|
||||
}
|
||||
|
||||
// NewRequestForURL instantiates a new request using the passed URL.
|
||||
func NewRequestForURL(u string) *http.Request {
|
||||
r, err := http.NewRequest("GET", u, NewBody(""))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("mocks: ERROR (%v) parsing testing URL %s", err, u))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// NewResponse instantiates a new response.
|
||||
func NewResponse() *http.Response {
|
||||
return NewResponseWithContent("")
|
||||
}
|
||||
|
||||
// NewResponseWithContent instantiates a new response with the passed string as the body content.
|
||||
func NewResponseWithContent(c string) *http.Response {
|
||||
return &http.Response{
|
||||
Status: "200 OK",
|
||||
StatusCode: 200,
|
||||
Proto: "HTTP/1.0",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 0,
|
||||
Body: NewBody(c),
|
||||
Request: NewRequest(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewResponseWithStatus instantiates a new response using the passed string and integer as the
|
||||
// status and status code.
|
||||
func NewResponseWithStatus(s string, c int) *http.Response {
|
||||
resp := NewResponse()
|
||||
resp.Status = s
|
||||
resp.StatusCode = c
|
||||
return resp
|
||||
}
|
||||
|
||||
// NewResponseWithBodyAndStatus instantiates a new response using the specified mock body,
|
||||
// status and status code
|
||||
func NewResponseWithBodyAndStatus(body *Body, c int, s string) *http.Response {
|
||||
resp := NewResponse()
|
||||
resp.Body = body
|
||||
resp.Status = s
|
||||
resp.StatusCode = c
|
||||
return resp
|
||||
}
|
||||
|
||||
// SetResponseHeader adds a header to the passed response.
|
||||
func SetResponseHeader(resp *http.Response, h string, v string) {
|
||||
if resp.Header == nil {
|
||||
resp.Header = make(http.Header)
|
||||
}
|
||||
resp.Header.Set(h, v)
|
||||
}
|
||||
|
||||
// SetResponseHeaderValues adds a header containing all the passed string values.
|
||||
func SetResponseHeaderValues(resp *http.Response, h string, values []string) {
|
||||
if resp.Header == nil {
|
||||
resp.Header = make(http.Header)
|
||||
}
|
||||
for _, v := range values {
|
||||
resp.Header.Add(h, v)
|
||||
}
|
||||
}
|
||||
|
||||
// SetAcceptedHeaders adds the headers usually associated with a 202 Accepted response.
|
||||
func SetAcceptedHeaders(resp *http.Response) {
|
||||
SetLocationHeader(resp, TestURL)
|
||||
SetRetryHeader(resp, TestDelay)
|
||||
}
|
||||
|
||||
// SetLocationHeader adds the Location header.
|
||||
func SetLocationHeader(resp *http.Response, location string) {
|
||||
SetResponseHeader(resp, http.CanonicalHeaderKey(headerLocation), location)
|
||||
}
|
||||
|
||||
// SetRetryHeader adds the Retry-After header.
|
||||
func SetRetryHeader(resp *http.Response, delay time.Duration) {
|
||||
SetResponseHeader(resp, http.CanonicalHeaderKey(headerRetryAfter), fmt.Sprintf("%v", delay.Seconds()))
|
||||
}
|
||||
176
vendor/github.com/Azure/go-autorest/autorest/mocks/mocks.go
generated
vendored
Normal file
176
vendor/github.com/Azure/go-autorest/autorest/mocks/mocks.go
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Package mocks provides mocks and helpers used in testing.
|
||||
*/
|
||||
package mocks
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Body implements acceptable body over a string.
|
||||
type Body struct {
|
||||
s string
|
||||
b []byte
|
||||
isOpen bool
|
||||
closeAttempts int
|
||||
}
|
||||
|
||||
// NewBody creates a new instance of Body.
|
||||
func NewBody(s string) *Body {
|
||||
return (&Body{s: s}).reset()
|
||||
}
|
||||
|
||||
// NewBodyClose creates a new instance of Body.
|
||||
func NewBodyClose(s string) *Body {
|
||||
return &Body{s: s}
|
||||
}
|
||||
|
||||
// Read reads into the passed byte slice and returns the bytes read.
|
||||
func (body *Body) Read(b []byte) (n int, err error) {
|
||||
if !body.IsOpen() {
|
||||
return 0, fmt.Errorf("ERROR: Body has been closed")
|
||||
}
|
||||
if len(body.b) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = copy(b, body.b)
|
||||
body.b = body.b[n:]
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Close closes the body.
|
||||
func (body *Body) Close() error {
|
||||
if body.isOpen {
|
||||
body.isOpen = false
|
||||
body.closeAttempts++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseAttempts returns the number of times Close was called.
|
||||
func (body *Body) CloseAttempts() int {
|
||||
return body.closeAttempts
|
||||
}
|
||||
|
||||
// IsOpen returns true if the Body has not been closed, false otherwise.
|
||||
func (body *Body) IsOpen() bool {
|
||||
return body.isOpen
|
||||
}
|
||||
|
||||
func (body *Body) reset() *Body {
|
||||
body.isOpen = true
|
||||
body.b = []byte(body.s)
|
||||
return body
|
||||
}
|
||||
|
||||
// Sender implements a simple null sender.
|
||||
type Sender struct {
|
||||
attempts int
|
||||
responses []*http.Response
|
||||
repeatResponse []int
|
||||
err error
|
||||
repeatError int
|
||||
emitErrorAfter int
|
||||
}
|
||||
|
||||
// NewSender creates a new instance of Sender.
|
||||
func NewSender() *Sender {
|
||||
return &Sender{}
|
||||
}
|
||||
|
||||
// Do accepts the passed request and, based on settings, emits a response and possible error.
|
||||
func (c *Sender) Do(r *http.Request) (resp *http.Response, err error) {
|
||||
c.attempts++
|
||||
|
||||
if len(c.responses) > 0 {
|
||||
resp = c.responses[0]
|
||||
if resp != nil {
|
||||
if b, ok := resp.Body.(*Body); ok {
|
||||
b.reset()
|
||||
}
|
||||
}
|
||||
c.repeatResponse[0]--
|
||||
if c.repeatResponse[0] == 0 {
|
||||
c.responses = c.responses[1:]
|
||||
c.repeatResponse = c.repeatResponse[1:]
|
||||
}
|
||||
} else {
|
||||
resp = NewResponse()
|
||||
}
|
||||
if resp != nil {
|
||||
resp.Request = r
|
||||
}
|
||||
|
||||
if c.emitErrorAfter > 0 {
|
||||
c.emitErrorAfter--
|
||||
} else if c.err != nil {
|
||||
err = c.err
|
||||
c.repeatError--
|
||||
if c.repeatError == 0 {
|
||||
c.err = nil
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AppendResponse adds the passed http.Response to the response stack.
|
||||
func (c *Sender) AppendResponse(resp *http.Response) {
|
||||
c.AppendAndRepeatResponse(resp, 1)
|
||||
}
|
||||
|
||||
// AppendAndRepeatResponse adds the passed http.Response to the response stack along with a
|
||||
// repeat count. A negative repeat count will return the response for all remaining calls to Do.
|
||||
func (c *Sender) AppendAndRepeatResponse(resp *http.Response, repeat int) {
|
||||
if c.responses == nil {
|
||||
c.responses = []*http.Response{resp}
|
||||
c.repeatResponse = []int{repeat}
|
||||
} else {
|
||||
c.responses = append(c.responses, resp)
|
||||
c.repeatResponse = append(c.repeatResponse, repeat)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts returns the number of times Do was called.
|
||||
func (c *Sender) Attempts() int {
|
||||
return c.attempts
|
||||
}
|
||||
|
||||
// SetError sets the error Do should return.
|
||||
func (c *Sender) SetError(err error) {
|
||||
c.SetAndRepeatError(err, 1)
|
||||
}
|
||||
|
||||
// SetAndRepeatError sets the error Do should return and how many calls to Do will return the error.
|
||||
// A negative repeat value will return the error for all remaining calls to Do.
|
||||
func (c *Sender) SetAndRepeatError(err error, repeat int) {
|
||||
c.err = err
|
||||
c.repeatError = repeat
|
||||
}
|
||||
|
||||
// SetEmitErrorAfter sets the number of attempts to be made before errors are emitted.
|
||||
func (c *Sender) SetEmitErrorAfter(ea int) {
|
||||
c.emitErrorAfter = ea
|
||||
}
|
||||
|
||||
// T is a simple testing struct.
|
||||
type T struct {
|
||||
Name string `json:"name" xml:"Name"`
|
||||
Age int `json:"age" xml:"Age"`
|
||||
}
|
||||
442
vendor/github.com/Azure/go-autorest/autorest/preparer.go
generated
vendored
Normal file
442
vendor/github.com/Azure/go-autorest/autorest/preparer.go
generated
vendored
Normal file
@@ -0,0 +1,442 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
mimeTypeJSON = "application/json"
|
||||
mimeTypeFormPost = "application/x-www-form-urlencoded"
|
||||
|
||||
headerAuthorization = "Authorization"
|
||||
headerContentType = "Content-Type"
|
||||
headerUserAgent = "User-Agent"
|
||||
)
|
||||
|
||||
// Preparer is the interface that wraps the Prepare method.
|
||||
//
|
||||
// Prepare accepts and possibly modifies an http.Request (e.g., adding Headers). Implementations
|
||||
// must ensure to not share or hold per-invocation state since Preparers may be shared and re-used.
|
||||
type Preparer interface {
|
||||
Prepare(*http.Request) (*http.Request, error)
|
||||
}
|
||||
|
||||
// PreparerFunc is a method that implements the Preparer interface.
|
||||
type PreparerFunc func(*http.Request) (*http.Request, error)
|
||||
|
||||
// Prepare implements the Preparer interface on PreparerFunc.
|
||||
func (pf PreparerFunc) Prepare(r *http.Request) (*http.Request, error) {
|
||||
return pf(r)
|
||||
}
|
||||
|
||||
// PrepareDecorator takes and possibly decorates, by wrapping, a Preparer. Decorators may affect the
|
||||
// http.Request and pass it along or, first, pass the http.Request along then affect the result.
|
||||
type PrepareDecorator func(Preparer) Preparer
|
||||
|
||||
// CreatePreparer creates, decorates, and returns a Preparer.
|
||||
// Without decorators, the returned Preparer returns the passed http.Request unmodified.
|
||||
// Preparers are safe to share and re-use.
|
||||
func CreatePreparer(decorators ...PrepareDecorator) Preparer {
|
||||
return DecoratePreparer(
|
||||
Preparer(PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, nil })),
|
||||
decorators...)
|
||||
}
|
||||
|
||||
// DecoratePreparer accepts a Preparer and a, possibly empty, set of PrepareDecorators, which it
|
||||
// applies to the Preparer. Decorators are applied in the order received, but their affect upon the
|
||||
// request depends on whether they are a pre-decorator (change the http.Request and then pass it
|
||||
// along) or a post-decorator (pass the http.Request along and alter it on return).
|
||||
func DecoratePreparer(p Preparer, decorators ...PrepareDecorator) Preparer {
|
||||
for _, decorate := range decorators {
|
||||
p = decorate(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Prepare accepts an http.Request and a, possibly empty, set of PrepareDecorators.
|
||||
// It creates a Preparer from the decorators which it then applies to the passed http.Request.
|
||||
func Prepare(r *http.Request, decorators ...PrepareDecorator) (*http.Request, error) {
|
||||
if r == nil {
|
||||
return nil, NewError("autorest", "Prepare", "Invoked without an http.Request")
|
||||
}
|
||||
return CreatePreparer(decorators...).Prepare(r)
|
||||
}
|
||||
|
||||
// WithNothing returns a "do nothing" PrepareDecorator that makes no changes to the passed
|
||||
// http.Request.
|
||||
func WithNothing() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
return p.Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeader returns a PrepareDecorator that sets the specified HTTP header of the http.Request to
|
||||
// the passed value. It canonicalizes the passed header name (via http.CanonicalHeaderKey) before
|
||||
// adding the header.
|
||||
func WithHeader(header string, value string) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
if r.Header == nil {
|
||||
r.Header = make(http.Header)
|
||||
}
|
||||
r.Header.Set(http.CanonicalHeaderKey(header), value)
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
|
||||
// value is "Bearer " followed by the supplied token.
|
||||
func WithBearerAuthorization(token string) PrepareDecorator {
|
||||
return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", token))
|
||||
}
|
||||
|
||||
// AsContentType returns a PrepareDecorator that adds an HTTP Content-Type header whose value
|
||||
// is the passed contentType.
|
||||
func AsContentType(contentType string) PrepareDecorator {
|
||||
return WithHeader(headerContentType, contentType)
|
||||
}
|
||||
|
||||
// WithUserAgent returns a PrepareDecorator that adds an HTTP User-Agent header whose value is the
|
||||
// passed string.
|
||||
func WithUserAgent(ua string) PrepareDecorator {
|
||||
return WithHeader(headerUserAgent, ua)
|
||||
}
|
||||
|
||||
// AsFormURLEncoded returns a PrepareDecorator that adds an HTTP Content-Type header whose value is
|
||||
// "application/x-www-form-urlencoded".
|
||||
func AsFormURLEncoded() PrepareDecorator {
|
||||
return AsContentType(mimeTypeFormPost)
|
||||
}
|
||||
|
||||
// AsJSON returns a PrepareDecorator that adds an HTTP Content-Type header whose value is
|
||||
// "application/json".
|
||||
func AsJSON() PrepareDecorator {
|
||||
return AsContentType(mimeTypeJSON)
|
||||
}
|
||||
|
||||
// WithMethod returns a PrepareDecorator that sets the HTTP method of the passed request. The
|
||||
// decorator does not validate that the passed method string is a known HTTP method.
|
||||
func WithMethod(method string) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r.Method = method
|
||||
return p.Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// AsDelete returns a PrepareDecorator that sets the HTTP method to DELETE.
|
||||
func AsDelete() PrepareDecorator { return WithMethod("DELETE") }
|
||||
|
||||
// AsGet returns a PrepareDecorator that sets the HTTP method to GET.
|
||||
func AsGet() PrepareDecorator { return WithMethod("GET") }
|
||||
|
||||
// AsHead returns a PrepareDecorator that sets the HTTP method to HEAD.
|
||||
func AsHead() PrepareDecorator { return WithMethod("HEAD") }
|
||||
|
||||
// AsOptions returns a PrepareDecorator that sets the HTTP method to OPTIONS.
|
||||
func AsOptions() PrepareDecorator { return WithMethod("OPTIONS") }
|
||||
|
||||
// AsPatch returns a PrepareDecorator that sets the HTTP method to PATCH.
|
||||
func AsPatch() PrepareDecorator { return WithMethod("PATCH") }
|
||||
|
||||
// AsPost returns a PrepareDecorator that sets the HTTP method to POST.
|
||||
func AsPost() PrepareDecorator { return WithMethod("POST") }
|
||||
|
||||
// AsPut returns a PrepareDecorator that sets the HTTP method to PUT.
|
||||
func AsPut() PrepareDecorator { return WithMethod("PUT") }
|
||||
|
||||
// WithBaseURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed
|
||||
// from the supplied baseUrl.
|
||||
func WithBaseURL(baseURL string) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
var u *url.URL
|
||||
if u, err = url.Parse(baseURL); err != nil {
|
||||
return r, err
|
||||
}
|
||||
if u.Scheme == "" {
|
||||
err = fmt.Errorf("autorest: No scheme detected in URL %s", baseURL)
|
||||
}
|
||||
if err == nil {
|
||||
r.URL = u
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the
|
||||
// request base URL (i.e., http.Request.URL) with the corresponding values from the passed map.
|
||||
func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator {
|
||||
parameters := ensureValueStrings(urlParameters)
|
||||
for key, value := range parameters {
|
||||
baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1)
|
||||
}
|
||||
return WithBaseURL(baseURL)
|
||||
}
|
||||
|
||||
// WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the
|
||||
// http.Request body.
|
||||
func WithFormData(v url.Values) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
s := v.Encode()
|
||||
r.ContentLength = int64(len(s))
|
||||
r.Body = ioutil.NopCloser(strings.NewReader(s))
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMultiPartFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) form parameters
|
||||
// into the http.Request body.
|
||||
func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
var body bytes.Buffer
|
||||
writer := multipart.NewWriter(&body)
|
||||
for key, value := range formDataParameters {
|
||||
if rc, ok := value.(io.ReadCloser); ok {
|
||||
var fd io.Writer
|
||||
if fd, err = writer.CreateFormFile(key, key); err != nil {
|
||||
return r, err
|
||||
}
|
||||
if _, err = io.Copy(fd, rc); err != nil {
|
||||
return r, err
|
||||
}
|
||||
} else {
|
||||
if err = writer.WriteField(key, ensureValueString(value)); err != nil {
|
||||
return r, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err = writer.Close(); err != nil {
|
||||
return r, err
|
||||
}
|
||||
if r.Header == nil {
|
||||
r.Header = make(http.Header)
|
||||
}
|
||||
r.Header.Set(http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType())
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
|
||||
r.ContentLength = int64(body.Len())
|
||||
return r, err
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithFile returns a PrepareDecorator that sends file in request body.
|
||||
func WithFile(f io.ReadCloser) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
b, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
r.ContentLength = int64(len(b))
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithBool returns a PrepareDecorator that encodes the passed bool into the body of the request
|
||||
// and sets the Content-Length header.
|
||||
func WithBool(v bool) PrepareDecorator {
|
||||
return WithString(fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
// WithFloat32 returns a PrepareDecorator that encodes the passed float32 into the body of the
|
||||
// request and sets the Content-Length header.
|
||||
func WithFloat32(v float32) PrepareDecorator {
|
||||
return WithString(fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
// WithFloat64 returns a PrepareDecorator that encodes the passed float64 into the body of the
|
||||
// request and sets the Content-Length header.
|
||||
func WithFloat64(v float64) PrepareDecorator {
|
||||
return WithString(fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
// WithInt32 returns a PrepareDecorator that encodes the passed int32 into the body of the request
|
||||
// and sets the Content-Length header.
|
||||
func WithInt32(v int32) PrepareDecorator {
|
||||
return WithString(fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
// WithInt64 returns a PrepareDecorator that encodes the passed int64 into the body of the request
|
||||
// and sets the Content-Length header.
|
||||
func WithInt64(v int64) PrepareDecorator {
|
||||
return WithString(fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
// WithString returns a PrepareDecorator that encodes the passed string into the body of the request
|
||||
// and sets the Content-Length header.
|
||||
func WithString(v string) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
r.ContentLength = int64(len(v))
|
||||
r.Body = ioutil.NopCloser(strings.NewReader(v))
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithJSON returns a PrepareDecorator that encodes the data passed as JSON into the body of the
|
||||
// request and sets the Content-Length header.
|
||||
func WithJSON(v interface{}) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
b, err := json.Marshal(v)
|
||||
if err == nil {
|
||||
r.ContentLength = int64(len(b))
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithPath returns a PrepareDecorator that adds the supplied path to the request URL. If the path
|
||||
// is absolute (that is, it begins with a "/"), it replaces the existing path.
|
||||
func WithPath(path string) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
if r.URL == nil {
|
||||
return r, NewError("autorest", "WithPath", "Invoked with a nil URL")
|
||||
}
|
||||
if r.URL, err = parseURL(r.URL, path); err != nil {
|
||||
return r, err
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithEscapedPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the
|
||||
// request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. The
|
||||
// values will be escaped (aka URL encoded) before insertion into the path.
|
||||
func WithEscapedPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator {
|
||||
parameters := escapeValueStrings(ensureValueStrings(pathParameters))
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
if r.URL == nil {
|
||||
return r, NewError("autorest", "WithEscapedPathParameters", "Invoked with a nil URL")
|
||||
}
|
||||
for key, value := range parameters {
|
||||
path = strings.Replace(path, "{"+key+"}", value, -1)
|
||||
}
|
||||
if r.URL, err = parseURL(r.URL, path); err != nil {
|
||||
return r, err
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the
|
||||
// request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map.
|
||||
func WithPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator {
|
||||
parameters := ensureValueStrings(pathParameters)
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
if r.URL == nil {
|
||||
return r, NewError("autorest", "WithPathParameters", "Invoked with a nil URL")
|
||||
}
|
||||
for key, value := range parameters {
|
||||
path = strings.Replace(path, "{"+key+"}", value, -1)
|
||||
}
|
||||
|
||||
if r.URL, err = parseURL(r.URL, path); err != nil {
|
||||
return r, err
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func parseURL(u *url.URL, path string) (*url.URL, error) {
|
||||
p := strings.TrimRight(u.String(), "/")
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
return url.Parse(p + path)
|
||||
}
|
||||
|
||||
// WithQueryParameters returns a PrepareDecorators that encodes and applies the query parameters
|
||||
// given in the supplied map (i.e., key=value).
|
||||
func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorator {
|
||||
parameters := ensureValueStrings(queryParameters)
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
if r.URL == nil {
|
||||
return r, NewError("autorest", "WithQueryParameters", "Invoked with a nil URL")
|
||||
}
|
||||
v := r.URL.Query()
|
||||
for key, value := range parameters {
|
||||
v.Add(key, value)
|
||||
}
|
||||
r.URL.RawQuery = createQuery(v)
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
766
vendor/github.com/Azure/go-autorest/autorest/preparer_test.go
generated
vendored
Normal file
766
vendor/github.com/Azure/go-autorest/autorest/preparer_test.go
generated
vendored
Normal file
@@ -0,0 +1,766 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
// PrepareDecorators wrap and invoke a Preparer. Most often, the decorator invokes the passed
|
||||
// Preparer and decorates the response.
|
||||
func ExamplePrepareDecorator() {
|
||||
path := "a/b/c/"
|
||||
pd := func() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
if r.URL == nil {
|
||||
return r, fmt.Errorf("ERROR: URL is not set")
|
||||
}
|
||||
r.URL.Path += path
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
r, _ := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
pd())
|
||||
|
||||
fmt.Printf("Path is %s\n", r.URL)
|
||||
// Output: Path is https://microsoft.com/a/b/c/
|
||||
}
|
||||
|
||||
// PrepareDecorators may also modify and then invoke the Preparer.
|
||||
func ExamplePrepareDecorator_pre() {
|
||||
pd := func() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r.Header.Add(http.CanonicalHeaderKey("ContentType"), "application/json")
|
||||
return p.Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
r, _ := Prepare(&http.Request{Header: http.Header{}},
|
||||
pd())
|
||||
|
||||
fmt.Printf("ContentType is %s\n", r.Header.Get("ContentType"))
|
||||
// Output: ContentType is application/json
|
||||
}
|
||||
|
||||
// Create a sequence of three Preparers that build up the URL path.
|
||||
func ExampleCreatePreparer() {
|
||||
p := CreatePreparer(
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
WithPath("a"),
|
||||
WithPath("b"),
|
||||
WithPath("c"))
|
||||
r, err := p.Prepare(&http.Request{})
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(r.URL)
|
||||
}
|
||||
// Output: https://microsoft.com/a/b/c
|
||||
}
|
||||
|
||||
// Create and apply separate Preparers
|
||||
func ExampleCreatePreparer_multiple() {
|
||||
params := map[string]interface{}{
|
||||
"param1": "a",
|
||||
"param2": "c",
|
||||
}
|
||||
|
||||
p1 := CreatePreparer(WithBaseURL("https://microsoft.com/"))
|
||||
p2 := CreatePreparer(WithPathParameters("/{param1}/b/{param2}/", params))
|
||||
|
||||
r, err := p1.Prepare(&http.Request{})
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
}
|
||||
|
||||
r, err = p2.Prepare(r)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(r.URL)
|
||||
}
|
||||
// Output: https://microsoft.com/a/b/c/
|
||||
}
|
||||
|
||||
// Create and chain separate Preparers
|
||||
func ExampleCreatePreparer_chain() {
|
||||
params := map[string]interface{}{
|
||||
"param1": "a",
|
||||
"param2": "c",
|
||||
}
|
||||
|
||||
p := CreatePreparer(WithBaseURL("https://microsoft.com/"))
|
||||
p = DecoratePreparer(p, WithPathParameters("/{param1}/b/{param2}/", params))
|
||||
|
||||
r, err := p.Prepare(&http.Request{})
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(r.URL)
|
||||
}
|
||||
// Output: https://microsoft.com/a/b/c/
|
||||
}
|
||||
|
||||
// Create and prepare an http.Request in one call
|
||||
func ExamplePrepare() {
|
||||
r, err := Prepare(&http.Request{},
|
||||
AsGet(),
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
WithPath("a/b/c/"))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("%s %s", r.Method, r.URL)
|
||||
}
|
||||
// Output: GET https://microsoft.com/a/b/c/
|
||||
}
|
||||
|
||||
// Create a request for a supplied base URL and path
|
||||
func ExampleWithBaseURL() {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/a/b/c/"))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(r.URL)
|
||||
}
|
||||
// Output: https://microsoft.com/a/b/c/
|
||||
}
|
||||
|
||||
func ExampleWithBaseURL_second() {
|
||||
_, err := Prepare(&http.Request{}, WithBaseURL(":"))
|
||||
fmt.Println(err)
|
||||
// Output: parse :: missing protocol scheme
|
||||
}
|
||||
|
||||
func ExampleWithCustomBaseURL() {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithCustomBaseURL("https://{account}.{service}.core.windows.net/",
|
||||
map[string]interface{}{
|
||||
"account": "myaccount",
|
||||
"service": "blob",
|
||||
}))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(r.URL)
|
||||
}
|
||||
// Output: https://myaccount.blob.core.windows.net/
|
||||
}
|
||||
|
||||
func ExampleWithCustomBaseURL_second() {
|
||||
_, err := Prepare(&http.Request{},
|
||||
WithCustomBaseURL(":", map[string]interface{}{}))
|
||||
fmt.Println(err)
|
||||
// Output: parse :: missing protocol scheme
|
||||
}
|
||||
|
||||
// Create a request with a custom HTTP header
|
||||
func ExampleWithHeader() {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/a/b/c/"),
|
||||
WithHeader("x-foo", "bar"))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Header %s=%s\n", "x-foo", r.Header.Get("x-foo"))
|
||||
}
|
||||
// Output: Header x-foo=bar
|
||||
}
|
||||
|
||||
// Create a request whose Body is the JSON encoding of a structure
|
||||
func ExampleWithFormData() {
|
||||
v := url.Values{}
|
||||
v.Add("name", "Rob Pike")
|
||||
v.Add("age", "42")
|
||||
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithFormData(v))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Request Body contains %s\n", string(b))
|
||||
}
|
||||
// Output: Request Body contains age=42&name=Rob+Pike
|
||||
}
|
||||
|
||||
// Create a request whose Body is the JSON encoding of a structure
|
||||
func ExampleWithJSON() {
|
||||
t := mocks.T{Name: "Rob Pike", Age: 42}
|
||||
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithJSON(&t))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Request Body contains %s\n", string(b))
|
||||
}
|
||||
// Output: Request Body contains {"name":"Rob Pike","age":42}
|
||||
}
|
||||
|
||||
// Create a request from a path with escaped parameters
|
||||
func ExampleWithEscapedPathParameters() {
|
||||
params := map[string]interface{}{
|
||||
"param1": "a b c",
|
||||
"param2": "d e f",
|
||||
}
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
WithEscapedPathParameters("/{param1}/b/{param2}/", params))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(r.URL)
|
||||
}
|
||||
// Output: https://microsoft.com/a+b+c/b/d+e+f/
|
||||
}
|
||||
|
||||
// Create a request from a path with parameters
|
||||
func ExampleWithPathParameters() {
|
||||
params := map[string]interface{}{
|
||||
"param1": "a",
|
||||
"param2": "c",
|
||||
}
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
WithPathParameters("/{param1}/b/{param2}/", params))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(r.URL)
|
||||
}
|
||||
// Output: https://microsoft.com/a/b/c/
|
||||
}
|
||||
|
||||
// Create a request with query parameters
|
||||
func ExampleWithQueryParameters() {
|
||||
params := map[string]interface{}{
|
||||
"q1": "value1",
|
||||
"q2": "value2",
|
||||
}
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
WithPath("/a/b/c/"),
|
||||
WithQueryParameters(params))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(r.URL)
|
||||
}
|
||||
// Output: https://microsoft.com/a/b/c/?q1=value1&q2=value2
|
||||
}
|
||||
|
||||
func TestWithCustomBaseURL(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{}, WithCustomBaseURL("https://{account}.{service}.core.windows.net/",
|
||||
map[string]interface{}{
|
||||
"account": "myaccount",
|
||||
"service": "blob",
|
||||
}))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithCustomBaseURL should not fail")
|
||||
}
|
||||
if r.URL.String() != "https://myaccount.blob.core.windows.net/" {
|
||||
t.Fatalf("autorest: WithCustomBaseURL expected https://myaccount.blob.core.windows.net/, got %s", r.URL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithCustomBaseURLwithInvalidURL(t *testing.T) {
|
||||
_, err := Prepare(&http.Request{}, WithCustomBaseURL("hello/{account}.{service}.core.windows.net/",
|
||||
map[string]interface{}{
|
||||
"account": "myaccount",
|
||||
"service": "blob",
|
||||
}))
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: WithCustomBaseURL should fail fo URL parse error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPathWithInvalidPath(t *testing.T) {
|
||||
p := "path%2*end"
|
||||
if _, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPath(p)); err == nil {
|
||||
t.Fatalf("autorest: WithPath should fail for invalid URL escape error for path '%v' ", p)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestWithPathParametersWithInvalidPath(t *testing.T) {
|
||||
p := "path%2*end"
|
||||
m := map[string]interface{}{
|
||||
"path1": p,
|
||||
}
|
||||
if _, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPathParameters("/{path1}/", m)); err == nil {
|
||||
t.Fatalf("autorest: WithPath should fail for invalid URL escape for path '%v' ", p)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCreatePreparerDoesNotModify(t *testing.T) {
|
||||
r1 := &http.Request{}
|
||||
p := CreatePreparer()
|
||||
r2, err := p.Prepare(r1)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: CreatePreparer failed (%v)", err)
|
||||
}
|
||||
if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatalf("autorest: CreatePreparer without decorators modified the request")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePreparerRunsDecoratorsInOrder(t *testing.T) {
|
||||
p := CreatePreparer(WithBaseURL("https://microsoft.com/"), WithPath("1"), WithPath("2"), WithPath("3"))
|
||||
r, err := p.Prepare(&http.Request{})
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: CreatePreparer failed (%v)", err)
|
||||
}
|
||||
if r.URL.String() != "https:/1/2/3" && r.URL.Host != "microsoft.com" {
|
||||
t.Fatalf("autorest: CreatePreparer failed to run decorators in order")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsContentType(t *testing.T) {
|
||||
r, err := Prepare(mocks.NewRequest(), AsContentType("application/text"))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v", err)
|
||||
}
|
||||
if r.Header.Get(headerContentType) != "application/text" {
|
||||
t.Fatalf("autorest: AsContentType failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsFormURLEncoded(t *testing.T) {
|
||||
r, err := Prepare(mocks.NewRequest(), AsFormURLEncoded())
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v", err)
|
||||
}
|
||||
if r.Header.Get(headerContentType) != mimeTypeFormPost {
|
||||
t.Fatalf("autorest: AsFormURLEncoded failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsJSON(t *testing.T) {
|
||||
r, err := Prepare(mocks.NewRequest(), AsJSON())
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v", err)
|
||||
}
|
||||
if r.Header.Get(headerContentType) != mimeTypeJSON {
|
||||
t.Fatalf("autorest: AsJSON failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithNothing(t *testing.T) {
|
||||
r1 := mocks.NewRequest()
|
||||
r2, err := Prepare(r1, WithNothing())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithNothing returned an unexpected error (%v)", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatal("azure: WithNothing modified the passed HTTP Request")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithBearerAuthorization(t *testing.T) {
|
||||
r, err := Prepare(mocks.NewRequest(), WithBearerAuthorization("SOME-TOKEN"))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v", err)
|
||||
}
|
||||
if r.Header.Get(headerAuthorization) != "Bearer SOME-TOKEN" {
|
||||
t.Fatalf("autorest: WithBearerAuthorization failed to add header (%s=%s)", headerAuthorization, r.Header.Get(headerAuthorization))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithUserAgent(t *testing.T) {
|
||||
ua := "User Agent Go"
|
||||
r, err := Prepare(mocks.NewRequest(), WithUserAgent(ua))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %v", err)
|
||||
}
|
||||
if r.UserAgent() != ua || r.Header.Get(headerUserAgent) != ua {
|
||||
t.Fatalf("autorest: WithUserAgent failed to add header (%s=%s)", headerUserAgent, r.Header.Get(headerUserAgent))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithMethod(t *testing.T) {
|
||||
r, _ := Prepare(mocks.NewRequest(), WithMethod("HEAD"))
|
||||
if r.Method != "HEAD" {
|
||||
t.Fatal("autorest: WithMethod failed to set HTTP method header")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsDelete(t *testing.T) {
|
||||
r, _ := Prepare(mocks.NewRequest(), AsDelete())
|
||||
if r.Method != "DELETE" {
|
||||
t.Fatal("autorest: AsDelete failed to set HTTP method header to DELETE")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsGet(t *testing.T) {
|
||||
r, _ := Prepare(mocks.NewRequest(), AsGet())
|
||||
if r.Method != "GET" {
|
||||
t.Fatal("autorest: AsGet failed to set HTTP method header to GET")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsHead(t *testing.T) {
|
||||
r, _ := Prepare(mocks.NewRequest(), AsHead())
|
||||
if r.Method != "HEAD" {
|
||||
t.Fatal("autorest: AsHead failed to set HTTP method header to HEAD")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsOptions(t *testing.T) {
|
||||
r, _ := Prepare(mocks.NewRequest(), AsOptions())
|
||||
if r.Method != "OPTIONS" {
|
||||
t.Fatal("autorest: AsOptions failed to set HTTP method header to OPTIONS")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsPatch(t *testing.T) {
|
||||
r, _ := Prepare(mocks.NewRequest(), AsPatch())
|
||||
if r.Method != "PATCH" {
|
||||
t.Fatal("autorest: AsPatch failed to set HTTP method header to PATCH")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsPost(t *testing.T) {
|
||||
r, _ := Prepare(mocks.NewRequest(), AsPost())
|
||||
if r.Method != "POST" {
|
||||
t.Fatal("autorest: AsPost failed to set HTTP method header to POST")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsPut(t *testing.T) {
|
||||
r, _ := Prepare(mocks.NewRequest(), AsPut())
|
||||
if r.Method != "PUT" {
|
||||
t.Fatal("autorest: AsPut failed to set HTTP method header to PUT")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareWithNullRequest(t *testing.T) {
|
||||
_, err := Prepare(nil)
|
||||
if err == nil {
|
||||
t.Fatal("autorest: Prepare failed to return an error when given a null http.Request")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithFormDataSetsContentLength(t *testing.T) {
|
||||
v := url.Values{}
|
||||
v.Add("name", "Rob Pike")
|
||||
v.Add("age", "42")
|
||||
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithFormData(v))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithFormData failed with error (%v)", err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithFormData failed with error (%v)", err)
|
||||
}
|
||||
|
||||
expected := "name=Rob+Pike&age=42"
|
||||
if !(string(b) == "name=Rob+Pike&age=42" || string(b) == "age=42&name=Rob+Pike") {
|
||||
t.Fatalf("autorest:WithFormData failed to return correct string got (%v), expected (%v)", string(b), expected)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(b)) {
|
||||
t.Fatalf("autorest:WithFormData set Content-Length to %v, expected %v", r.ContentLength, len(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithMultiPartFormDataSetsContentLength(t *testing.T) {
|
||||
v := map[string]interface{}{
|
||||
"file": ioutil.NopCloser(strings.NewReader("Hello Gopher")),
|
||||
"age": "42",
|
||||
}
|
||||
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithMultiPartFormData(v))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithMultiPartFormData failed with error (%v)", err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithMultiPartFormData failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(b)) {
|
||||
t.Fatalf("autorest:WithMultiPartFormData set Content-Length to %v, expected %v", r.ContentLength, len(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithMultiPartFormDataWithNoFile(t *testing.T) {
|
||||
v := map[string]interface{}{
|
||||
"file": "no file",
|
||||
"age": "42",
|
||||
}
|
||||
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithMultiPartFormData(v))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithMultiPartFormData failed with error (%v)", err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithMultiPartFormData failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(b)) {
|
||||
t.Fatalf("autorest:WithMultiPartFormData set Content-Length to %v, expected %v", r.ContentLength, len(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithFile(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithFile(ioutil.NopCloser(strings.NewReader("Hello Gopher"))))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithFile failed with error (%v)", err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithFile failed with error (%v)", err)
|
||||
}
|
||||
if r.ContentLength != int64(len(b)) {
|
||||
t.Fatalf("autorest:WithFile set Content-Length to %v, expected %v", r.ContentLength, len(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithBool_SetsTheBody(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithBool(false))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithBool failed with error (%v)", err)
|
||||
}
|
||||
|
||||
s, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithBool failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(fmt.Sprintf("%v", false))) {
|
||||
t.Fatalf("autorest: WithBool set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", false))))
|
||||
}
|
||||
|
||||
v, err := strconv.ParseBool(string(s))
|
||||
if err != nil || v {
|
||||
t.Fatalf("autorest: WithBool incorrectly encoded the boolean as %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithFloat32_SetsTheBody(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithFloat32(42.0))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithFloat32 failed with error (%v)", err)
|
||||
}
|
||||
|
||||
s, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithFloat32 failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(fmt.Sprintf("%v", 42.0))) {
|
||||
t.Fatalf("autorest: WithFloat32 set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", 42.0))))
|
||||
}
|
||||
|
||||
v, err := strconv.ParseFloat(string(s), 32)
|
||||
if err != nil || float32(v) != float32(42.0) {
|
||||
t.Fatalf("autorest: WithFloat32 incorrectly encoded the boolean as %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithFloat64_SetsTheBody(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithFloat64(42.0))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithFloat64 failed with error (%v)", err)
|
||||
}
|
||||
|
||||
s, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithFloat64 failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(fmt.Sprintf("%v", 42.0))) {
|
||||
t.Fatalf("autorest: WithFloat64 set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", 42.0))))
|
||||
}
|
||||
|
||||
v, err := strconv.ParseFloat(string(s), 64)
|
||||
if err != nil || v != float64(42.0) {
|
||||
t.Fatalf("autorest: WithFloat64 incorrectly encoded the boolean as %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithInt32_SetsTheBody(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithInt32(42))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithInt32 failed with error (%v)", err)
|
||||
}
|
||||
|
||||
s, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithInt32 failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(fmt.Sprintf("%v", 42))) {
|
||||
t.Fatalf("autorest: WithInt32 set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", 42))))
|
||||
}
|
||||
|
||||
v, err := strconv.ParseInt(string(s), 10, 32)
|
||||
if err != nil || int32(v) != int32(42) {
|
||||
t.Fatalf("autorest: WithInt32 incorrectly encoded the boolean as %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithInt64_SetsTheBody(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithInt64(42))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithInt64 failed with error (%v)", err)
|
||||
}
|
||||
|
||||
s, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithInt64 failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(fmt.Sprintf("%v", 42))) {
|
||||
t.Fatalf("autorest: WithInt64 set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", 42))))
|
||||
}
|
||||
|
||||
v, err := strconv.ParseInt(string(s), 10, 64)
|
||||
if err != nil || v != int64(42) {
|
||||
t.Fatalf("autorest: WithInt64 incorrectly encoded the boolean as %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithString_SetsTheBody(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithString("value"))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithString failed with error (%v)", err)
|
||||
}
|
||||
|
||||
s, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithString failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len("value")) {
|
||||
t.Fatalf("autorest: WithString set Content-Length to %v, expected %v", r.ContentLength, int64(len("value")))
|
||||
}
|
||||
|
||||
if string(s) != "value" {
|
||||
t.Fatalf("autorest: WithString incorrectly encoded the string as %v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithJSONSetsContentLength(t *testing.T) {
|
||||
r, err := Prepare(&http.Request{},
|
||||
WithJSON(&mocks.T{Name: "Rob Pike", Age: 42}))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithJSON failed with error (%v)", err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithJSON failed with error (%v)", err)
|
||||
}
|
||||
|
||||
if r.ContentLength != int64(len(b)) {
|
||||
t.Fatalf("autorest:WithJSON set Content-Length to %v, expected %v", r.ContentLength, len(b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithHeaderAllocatesHeaders(t *testing.T) {
|
||||
r, err := Prepare(mocks.NewRequest(), WithHeader("x-foo", "bar"))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithHeader failed (%v)", err)
|
||||
}
|
||||
if r.Header.Get("x-foo") != "bar" {
|
||||
t.Fatalf("autorest: WithHeader failed to add header (%s=%s)", "x-foo", r.Header.Get("x-foo"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPathCatchesNilURL(t *testing.T) {
|
||||
_, err := Prepare(&http.Request{}, WithPath("a"))
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: WithPath failed to catch a nil URL")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithEscapedPathParametersCatchesNilURL(t *testing.T) {
|
||||
_, err := Prepare(&http.Request{}, WithEscapedPathParameters("", map[string]interface{}{"foo": "bar"}))
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: WithEscapedPathParameters failed to catch a nil URL")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPathParametersCatchesNilURL(t *testing.T) {
|
||||
_, err := Prepare(&http.Request{}, WithPathParameters("", map[string]interface{}{"foo": "bar"}))
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: WithPathParameters failed to catch a nil URL")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithQueryParametersCatchesNilURL(t *testing.T) {
|
||||
_, err := Prepare(&http.Request{}, WithQueryParameters(map[string]interface{}{"foo": "bar"}))
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: WithQueryParameters failed to catch a nil URL")
|
||||
}
|
||||
}
|
||||
|
||||
func TestModifyingExistingRequest(t *testing.T) {
|
||||
r, err := Prepare(mocks.NewRequestForURL("https://bing.com"), WithPath("search"), WithQueryParameters(map[string]interface{}{"q": "golang"}))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: Preparing an existing request returned an error (%v)", err)
|
||||
}
|
||||
if r.URL.String() != "https:/search?q=golang" && r.URL.Host != "bing.com" {
|
||||
t.Fatalf("autorest: Preparing an existing request failed (%s)", r.URL)
|
||||
}
|
||||
}
|
||||
250
vendor/github.com/Azure/go-autorest/autorest/responder.go
generated
vendored
Normal file
250
vendor/github.com/Azure/go-autorest/autorest/responder.go
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Responder is the interface that wraps the Respond method.
|
||||
//
|
||||
// Respond accepts and reacts to an http.Response. Implementations must ensure to not share or hold
|
||||
// state since Responders may be shared and re-used.
|
||||
type Responder interface {
|
||||
Respond(*http.Response) error
|
||||
}
|
||||
|
||||
// ResponderFunc is a method that implements the Responder interface.
|
||||
type ResponderFunc func(*http.Response) error
|
||||
|
||||
// Respond implements the Responder interface on ResponderFunc.
|
||||
func (rf ResponderFunc) Respond(r *http.Response) error {
|
||||
return rf(r)
|
||||
}
|
||||
|
||||
// RespondDecorator takes and possibly decorates, by wrapping, a Responder. Decorators may react to
|
||||
// the http.Response and pass it along or, first, pass the http.Response along then react.
|
||||
type RespondDecorator func(Responder) Responder
|
||||
|
||||
// CreateResponder creates, decorates, and returns a Responder. Without decorators, the returned
|
||||
// Responder returns the passed http.Response unmodified. Responders may or may not be safe to share
|
||||
// and re-used: It depends on the applied decorators. For example, a standard decorator that closes
|
||||
// the response body is fine to share whereas a decorator that reads the body into a passed struct
|
||||
// is not.
|
||||
//
|
||||
// To prevent memory leaks, ensure that at least one Responder closes the response body.
|
||||
func CreateResponder(decorators ...RespondDecorator) Responder {
|
||||
return DecorateResponder(
|
||||
Responder(ResponderFunc(func(r *http.Response) error { return nil })),
|
||||
decorators...)
|
||||
}
|
||||
|
||||
// DecorateResponder accepts a Responder and a, possibly empty, set of RespondDecorators, which it
|
||||
// applies to the Responder. Decorators are applied in the order received, but their affect upon the
|
||||
// request depends on whether they are a pre-decorator (react to the http.Response and then pass it
|
||||
// along) or a post-decorator (pass the http.Response along and then react).
|
||||
func DecorateResponder(r Responder, decorators ...RespondDecorator) Responder {
|
||||
for _, decorate := range decorators {
|
||||
r = decorate(r)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Respond accepts an http.Response and a, possibly empty, set of RespondDecorators.
|
||||
// It creates a Responder from the decorators it then applies to the passed http.Response.
|
||||
func Respond(r *http.Response, decorators ...RespondDecorator) error {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return CreateResponder(decorators...).Respond(r)
|
||||
}
|
||||
|
||||
// ByIgnoring returns a RespondDecorator that ignores the passed http.Response passing it unexamined
|
||||
// to the next RespondDecorator.
|
||||
func ByIgnoring() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
return r.Respond(resp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ByCopying copies the contents of the http.Response Body into the passed bytes.Buffer as
|
||||
// the Body is read.
|
||||
func ByCopying(b *bytes.Buffer) RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err == nil && resp != nil && resp.Body != nil {
|
||||
resp.Body = TeeReadCloser(resp.Body, b)
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which
|
||||
// it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed
|
||||
// Responder is invoked prior to discarding the response body, the decorator may occur anywhere
|
||||
// within the set.
|
||||
func ByDiscardingBody() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err == nil && resp != nil && resp.Body != nil {
|
||||
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
|
||||
return fmt.Errorf("Error discarding the response body: %v", err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ByClosing returns a RespondDecorator that first invokes the passed Responder after which it
|
||||
// closes the response body. Since the passed Responder is invoked prior to closing the response
|
||||
// body, the decorator may occur anywhere within the set.
|
||||
func ByClosing() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if resp != nil && resp.Body != nil {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
return fmt.Errorf("Error closing the response body: %v", err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ByClosingIfError returns a RespondDecorator that first invokes the passed Responder after which
|
||||
// it closes the response if the passed Responder returns an error and the response body exists.
|
||||
func ByClosingIfError() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err != nil && resp != nil && resp.Body != nil {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
return fmt.Errorf("Error closing the response body: %v", err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ByUnmarshallingJSON returns a RespondDecorator that decodes a JSON document returned in the
|
||||
// response Body into the value pointed to by v.
|
||||
func ByUnmarshallingJSON(v interface{}) RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err == nil {
|
||||
b, errInner := ioutil.ReadAll(resp.Body)
|
||||
// Some responses might include a BOM, remove for successful unmarshalling
|
||||
b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf"))
|
||||
if errInner != nil {
|
||||
err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
|
||||
} else if len(strings.Trim(string(b), " ")) > 0 {
|
||||
errInner = json.Unmarshal(b, v)
|
||||
if errInner != nil {
|
||||
err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ByUnmarshallingXML returns a RespondDecorator that decodes a XML document returned in the
|
||||
// response Body into the value pointed to by v.
|
||||
func ByUnmarshallingXML(v interface{}) RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err == nil {
|
||||
b, errInner := ioutil.ReadAll(resp.Body)
|
||||
if errInner != nil {
|
||||
err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
|
||||
} else {
|
||||
errInner = xml.Unmarshal(b, v)
|
||||
if errInner != nil {
|
||||
err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response
|
||||
// StatusCode is among the set passed. On error, response body is fully read into a buffer and
|
||||
// presented in the returned error, as well as in the response body.
|
||||
func WithErrorUnlessStatusCode(codes ...int) RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err == nil && !ResponseHasStatusCode(resp, codes...) {
|
||||
derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s",
|
||||
resp.Request.Method,
|
||||
resp.Request.URL,
|
||||
resp.Status)
|
||||
if resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
derr.ServiceError = b
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
}
|
||||
err = derr
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is
|
||||
// anything other than HTTP 200.
|
||||
func WithErrorUnlessOK() RespondDecorator {
|
||||
return WithErrorUnlessStatusCode(http.StatusOK)
|
||||
}
|
||||
|
||||
// ExtractHeader extracts all values of the specified header from the http.Response. It returns an
|
||||
// empty string slice if the passed http.Response is nil or the header does not exist.
|
||||
func ExtractHeader(header string, resp *http.Response) []string {
|
||||
if resp != nil && resp.Header != nil {
|
||||
return resp.Header[http.CanonicalHeaderKey(header)]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractHeaderValue extracts the first value of the specified header from the http.Response. It
|
||||
// returns an empty string if the passed http.Response is nil or the header does not exist.
|
||||
func ExtractHeaderValue(header string, resp *http.Response) string {
|
||||
h := ExtractHeader(header, resp)
|
||||
if len(h) > 0 {
|
||||
return h[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
665
vendor/github.com/Azure/go-autorest/autorest/responder_test.go
generated
vendored
Normal file
665
vendor/github.com/Azure/go-autorest/autorest/responder_test.go
generated
vendored
Normal file
@@ -0,0 +1,665 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
func ExampleWithErrorUnlessOK() {
|
||||
r := mocks.NewResponse()
|
||||
r.Request = mocks.NewRequest()
|
||||
|
||||
// Respond and leave the response body open (for a subsequent responder to close)
|
||||
err := Respond(r,
|
||||
WithErrorUnlessOK(),
|
||||
ByDiscardingBody(),
|
||||
ByClosingIfError())
|
||||
|
||||
if err == nil {
|
||||
fmt.Printf("%s of %s returned HTTP 200", r.Request.Method, r.Request.URL)
|
||||
|
||||
// Complete handling the response and close the body
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
// Output: GET of https://microsoft.com/a/b/c/ returned HTTP 200
|
||||
}
|
||||
|
||||
func ExampleByUnmarshallingJSON() {
|
||||
c := `
|
||||
{
|
||||
"name" : "Rob Pike",
|
||||
"age" : 42
|
||||
}
|
||||
`
|
||||
|
||||
type V struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
|
||||
v := &V{}
|
||||
|
||||
Respond(mocks.NewResponseWithContent(c),
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
|
||||
fmt.Printf("%s is %d years old\n", v.Name, v.Age)
|
||||
// Output: Rob Pike is 42 years old
|
||||
}
|
||||
|
||||
func ExampleByUnmarshallingXML() {
|
||||
c := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Person>
|
||||
<Name>Rob Pike</Name>
|
||||
<Age>42</Age>
|
||||
</Person>`
|
||||
|
||||
type V struct {
|
||||
Name string `xml:"Name"`
|
||||
Age int `xml:"Age"`
|
||||
}
|
||||
|
||||
v := &V{}
|
||||
|
||||
Respond(mocks.NewResponseWithContent(c),
|
||||
ByUnmarshallingXML(v),
|
||||
ByClosing())
|
||||
|
||||
fmt.Printf("%s is %d years old\n", v.Name, v.Age)
|
||||
// Output: Rob Pike is 42 years old
|
||||
}
|
||||
|
||||
func TestCreateResponderDoesNotModify(t *testing.T) {
|
||||
r1 := mocks.NewResponse()
|
||||
r2 := mocks.NewResponse()
|
||||
p := CreateResponder()
|
||||
err := p.Respond(r1)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: CreateResponder failed (%v)", err)
|
||||
}
|
||||
if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatalf("autorest: CreateResponder without decorators modified the response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateResponderRunsDecoratorsInOrder(t *testing.T) {
|
||||
s := ""
|
||||
|
||||
d := func(n int) RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err == nil {
|
||||
s += fmt.Sprintf("%d", n)
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
p := CreateResponder(d(1), d(2), d(3))
|
||||
err := p.Respond(&http.Response{})
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: Respond failed (%v)", err)
|
||||
}
|
||||
|
||||
if s != "123" {
|
||||
t.Fatalf("autorest: CreateResponder invoked decorators in an incorrect order; expected '123', received '%s'", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestByIgnoring(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(r2 *http.Response) error {
|
||||
r1 := mocks.NewResponse()
|
||||
if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatalf("autorest: ByIgnoring modified the HTTP Response -- received %v, expected %v", r2, r1)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByIgnoring(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestByCopying_Copies(t *testing.T) {
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
err := Respond(r,
|
||||
ByCopying(b),
|
||||
ByUnmarshallingJSON(&mocks.T{}),
|
||||
ByClosing())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: ByCopying returned an unexpected error -- %v", err)
|
||||
}
|
||||
if b.String() != jsonT {
|
||||
t.Fatalf("autorest: ByCopying failed to copy the bytes read")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByCopying_ReturnsNestedErrors(t *testing.T) {
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
|
||||
r.Body.Close()
|
||||
err := Respond(r,
|
||||
ByCopying(&bytes.Buffer{}),
|
||||
ByUnmarshallingJSON(&mocks.T{}),
|
||||
ByClosing())
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: ByCopying failed to return the expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByCopying_AcceptsNilReponse(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
resp.Body.Close()
|
||||
r.Respond(nil)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByCopying(&bytes.Buffer{}))
|
||||
}
|
||||
|
||||
func TestByCopying_AcceptsNilBody(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
resp.Body.Close()
|
||||
resp.Body = nil
|
||||
r.Respond(resp)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByCopying(&bytes.Buffer{}))
|
||||
}
|
||||
|
||||
func TestByClosing(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
err := Respond(r, ByClosing())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: ByClosing failed (%v)", err)
|
||||
}
|
||||
if r.Body.(*mocks.Body).IsOpen() {
|
||||
t.Fatalf("autorest: ByClosing did not close the response body")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByClosingAcceptsNilResponse(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
resp.Body.Close()
|
||||
r.Respond(nil)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestByClosingAcceptsNilBody(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
resp.Body.Close()
|
||||
resp.Body = nil
|
||||
r.Respond(resp)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestByClosingClosesEvenAfterErrors(t *testing.T) {
|
||||
var e error
|
||||
|
||||
r := mocks.NewResponse()
|
||||
Respond(r,
|
||||
withErrorRespondDecorator(&e),
|
||||
ByClosing())
|
||||
|
||||
if r.Body.(*mocks.Body).IsOpen() {
|
||||
t.Fatalf("autorest: ByClosing did not close the response body after an error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByClosingClosesReturnsNestedErrors(t *testing.T) {
|
||||
var e error
|
||||
|
||||
r := mocks.NewResponse()
|
||||
err := Respond(r,
|
||||
withErrorRespondDecorator(&e),
|
||||
ByClosing())
|
||||
|
||||
if err == nil || !reflect.DeepEqual(e, err) {
|
||||
t.Fatalf("autorest: ByClosing failed to return a nested error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByClosingIfErrorAcceptsNilResponse(t *testing.T) {
|
||||
var e error
|
||||
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
withErrorRespondDecorator(&e),
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
resp.Body.Close()
|
||||
r.Respond(nil)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByClosingIfError())
|
||||
}
|
||||
|
||||
func TestByClosingIfErrorAcceptsNilBody(t *testing.T) {
|
||||
var e error
|
||||
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
withErrorRespondDecorator(&e),
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
resp.Body.Close()
|
||||
resp.Body = nil
|
||||
r.Respond(resp)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByClosingIfError())
|
||||
}
|
||||
|
||||
func TestByClosingIfErrorClosesIfAnErrorOccurs(t *testing.T) {
|
||||
var e error
|
||||
|
||||
r := mocks.NewResponse()
|
||||
Respond(r,
|
||||
withErrorRespondDecorator(&e),
|
||||
ByClosingIfError())
|
||||
|
||||
if r.Body.(*mocks.Body).IsOpen() {
|
||||
t.Fatalf("autorest: ByClosingIfError did not close the response body after an error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByClosingIfErrorDoesNotClosesIfNoErrorOccurs(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
Respond(r,
|
||||
ByClosingIfError())
|
||||
|
||||
if !r.Body.(*mocks.Body).IsOpen() {
|
||||
t.Fatalf("autorest: ByClosingIfError closed the response body even though no error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByDiscardingBody(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
err := Respond(r,
|
||||
ByDiscardingBody())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: ByDiscardingBody failed (%v)", err)
|
||||
}
|
||||
buf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: Reading result of ByDiscardingBody failed (%v)", err)
|
||||
}
|
||||
|
||||
if len(buf) != 0 {
|
||||
t.Logf("autorest: Body was not empty after calling ByDiscardingBody.")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestByDiscardingBodyAcceptsNilResponse(t *testing.T) {
|
||||
var e error
|
||||
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
withErrorRespondDecorator(&e),
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
resp.Body.Close()
|
||||
r.Respond(nil)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByDiscardingBody())
|
||||
}
|
||||
|
||||
func TestByDiscardingBodyAcceptsNilBody(t *testing.T) {
|
||||
var e error
|
||||
|
||||
r := mocks.NewResponse()
|
||||
|
||||
Respond(r,
|
||||
withErrorRespondDecorator(&e),
|
||||
(func() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
resp.Body.Close()
|
||||
resp.Body = nil
|
||||
r.Respond(resp)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})(),
|
||||
ByDiscardingBody())
|
||||
}
|
||||
|
||||
func TestByUnmarshallingJSON(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
err := Respond(r,
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: ByUnmarshallingJSON failed (%v)", err)
|
||||
}
|
||||
if v.Name != "Rob Pike" || v.Age != 42 {
|
||||
t.Fatalf("autorest: ByUnmarshallingJSON failed to properly unmarshal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByUnmarshallingJSON_HandlesReadErrors(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
r.Body.(*mocks.Body).Close()
|
||||
|
||||
err := Respond(r,
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: ByUnmarshallingJSON failed to receive / respond to read error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByUnmarshallingJSONIncludesJSONInErrors(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
j := jsonT[0 : len(jsonT)-2]
|
||||
r := mocks.NewResponseWithContent(j)
|
||||
err := Respond(r,
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
if err == nil || !strings.Contains(err.Error(), j) {
|
||||
t.Fatalf("autorest: ByUnmarshallingJSON failed to return JSON in error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestByUnmarshallingJSONEmptyInput(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(``)
|
||||
err := Respond(r,
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: ByUnmarshallingJSON failed to return nil in case of empty JSON (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestByUnmarshallingXML(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(xmlT)
|
||||
err := Respond(r,
|
||||
ByUnmarshallingXML(v),
|
||||
ByClosing())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: ByUnmarshallingXML failed (%v)", err)
|
||||
}
|
||||
if v.Name != "Rob Pike" || v.Age != 42 {
|
||||
t.Fatalf("autorest: ByUnmarshallingXML failed to properly unmarshal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByUnmarshallingXML_HandlesReadErrors(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(xmlT)
|
||||
r.Body.(*mocks.Body).Close()
|
||||
|
||||
err := Respond(r,
|
||||
ByUnmarshallingXML(v),
|
||||
ByClosing())
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: ByUnmarshallingXML failed to receive / respond to read error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestByUnmarshallingXMLIncludesXMLInErrors(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
x := xmlT[0 : len(xmlT)-2]
|
||||
r := mocks.NewResponseWithContent(x)
|
||||
err := Respond(r,
|
||||
ByUnmarshallingXML(v),
|
||||
ByClosing())
|
||||
if err == nil || !strings.Contains(err.Error(), x) {
|
||||
t.Fatalf("autorest: ByUnmarshallingXML failed to return XML in error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRespondAcceptsNullResponse(t *testing.T) {
|
||||
err := Respond(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: Respond returned an unexpected error when given a null Response (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessStatusCodeOKResponse(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
err := Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) failed on okay response. (%v)", err)
|
||||
}
|
||||
|
||||
if v.Name != "Rob Pike" || v.Age != 42 {
|
||||
t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) corrupted the response body of okay response.")
|
||||
}
|
||||
}
|
||||
|
||||
func TesWithErrorUnlessStatusCodeErrorResponse(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
e := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
r.Status = "400 BadRequest"
|
||||
r.StatusCode = http.StatusBadRequest
|
||||
|
||||
err := Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK),
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("autorest: WithErrorUnlessStatusCode(http.StatusOK) did not return error, on a response to a bad request.")
|
||||
}
|
||||
|
||||
var errorRespBody []byte
|
||||
if derr, ok := err.(DetailedError); !ok {
|
||||
t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) got wrong error type : %T, expected: DetailedError, on a response to a bad request.", err)
|
||||
} else {
|
||||
errorRespBody = derr.ServiceError
|
||||
}
|
||||
|
||||
if errorRespBody == nil {
|
||||
t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) ServiceError not returned in DetailedError on a response to a bad request.")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(errorRespBody, e)
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) cannot parse error returned in ServiceError into json. %v", err)
|
||||
}
|
||||
|
||||
expected := &mocks.T{Name: "Rob Pike", Age: 42}
|
||||
if e != expected {
|
||||
t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK wrong value from parsed ServiceError: got=%#v expected=%#v", e, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessStatusCode(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
r.Request = mocks.NewRequest()
|
||||
r.Status = "400 BadRequest"
|
||||
r.StatusCode = http.StatusBadRequest
|
||||
|
||||
err := Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError),
|
||||
ByClosingIfError())
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithErrorUnlessStatusCode returned an error (%v) for an acceptable status code (%s)", err, r.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessStatusCodeEmitsErrorForUnacceptableStatusCode(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
r.Request = mocks.NewRequest()
|
||||
r.Status = "400 BadRequest"
|
||||
r.StatusCode = http.StatusBadRequest
|
||||
|
||||
err := Respond(r,
|
||||
WithErrorUnlessStatusCode(http.StatusOK, http.StatusUnauthorized, http.StatusInternalServerError),
|
||||
ByClosingIfError())
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: WithErrorUnlessStatusCode failed to return an error for an unacceptable status code (%s)", r.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessOK(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
r.Request = mocks.NewRequest()
|
||||
|
||||
err := Respond(r,
|
||||
WithErrorUnlessOK(),
|
||||
ByClosingIfError())
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: WithErrorUnlessOK returned an error for OK status code (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithErrorUnlessOKEmitsErrorIfNotOK(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
r.Request = mocks.NewRequest()
|
||||
r.Status = "400 BadRequest"
|
||||
r.StatusCode = http.StatusBadRequest
|
||||
|
||||
err := Respond(r,
|
||||
WithErrorUnlessOK(),
|
||||
ByClosingIfError())
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: WithErrorUnlessOK failed to return an error for a non-OK status code (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractHeader(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
v := []string{"v1", "v2", "v3"}
|
||||
mocks.SetResponseHeaderValues(r, mocks.TestHeader, v)
|
||||
|
||||
if !reflect.DeepEqual(ExtractHeader(mocks.TestHeader, r), v) {
|
||||
t.Fatalf("autorest: ExtractHeader failed to retrieve the expected header -- expected [%s]%v, received [%s]%v",
|
||||
mocks.TestHeader, v, mocks.TestHeader, ExtractHeader(mocks.TestHeader, r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractHeaderHandlesMissingHeader(t *testing.T) {
|
||||
var v []string
|
||||
r := mocks.NewResponse()
|
||||
|
||||
if !reflect.DeepEqual(ExtractHeader(mocks.TestHeader, r), v) {
|
||||
t.Fatalf("autorest: ExtractHeader failed to handle a missing header -- expected %v, received %v",
|
||||
v, ExtractHeader(mocks.TestHeader, r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractHeaderValue(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
v := "v1"
|
||||
mocks.SetResponseHeader(r, mocks.TestHeader, v)
|
||||
|
||||
if ExtractHeaderValue(mocks.TestHeader, r) != v {
|
||||
t.Fatalf("autorest: ExtractHeader failed to retrieve the expected header -- expected [%s]%v, received [%s]%v",
|
||||
mocks.TestHeader, v, mocks.TestHeader, ExtractHeaderValue(mocks.TestHeader, r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractHeaderValueHandlesMissingHeader(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
v := ""
|
||||
|
||||
if ExtractHeaderValue(mocks.TestHeader, r) != v {
|
||||
t.Fatalf("autorest: ExtractHeader failed to retrieve the expected header -- expected [%s]%v, received [%s]%v",
|
||||
mocks.TestHeader, v, mocks.TestHeader, ExtractHeaderValue(mocks.TestHeader, r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractHeaderValueRetrievesFirstValue(t *testing.T) {
|
||||
r := mocks.NewResponse()
|
||||
v := []string{"v1", "v2", "v3"}
|
||||
mocks.SetResponseHeaderValues(r, mocks.TestHeader, v)
|
||||
|
||||
if ExtractHeaderValue(mocks.TestHeader, r) != v[0] {
|
||||
t.Fatalf("autorest: ExtractHeader failed to retrieve the expected header -- expected [%s]%v, received [%s]%v",
|
||||
mocks.TestHeader, v[0], mocks.TestHeader, ExtractHeaderValue(mocks.TestHeader, r))
|
||||
}
|
||||
}
|
||||
52
vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go
generated
vendored
Normal file
52
vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// NewRetriableRequest returns a wrapper around an HTTP request that support retry logic.
|
||||
func NewRetriableRequest(req *http.Request) *RetriableRequest {
|
||||
return &RetriableRequest{req: req}
|
||||
}
|
||||
|
||||
// Request returns the wrapped HTTP request.
|
||||
func (rr *RetriableRequest) Request() *http.Request {
|
||||
return rr.req
|
||||
}
|
||||
|
||||
func (rr *RetriableRequest) prepareFromByteReader() (err error) {
|
||||
// fall back to making a copy (only do this once)
|
||||
b := []byte{}
|
||||
if rr.req.ContentLength > 0 {
|
||||
b = make([]byte, rr.req.ContentLength)
|
||||
_, err = io.ReadFull(rr.req.Body, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
b, err = ioutil.ReadAll(rr.req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
rr.br = bytes.NewReader(b)
|
||||
rr.req.Body = ioutil.NopCloser(rr.br)
|
||||
return err
|
||||
}
|
||||
54
vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go
generated
vendored
Normal file
54
vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// +build !go1.8
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package autorest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RetriableRequest provides facilities for retrying an HTTP request.
|
||||
type RetriableRequest struct {
|
||||
req *http.Request
|
||||
br *bytes.Reader
|
||||
}
|
||||
|
||||
// Prepare signals that the request is about to be sent.
|
||||
func (rr *RetriableRequest) Prepare() (err error) {
|
||||
// preserve the request body; this is to support retry logic as
|
||||
// the underlying transport will always close the reqeust body
|
||||
if rr.req.Body != nil {
|
||||
if rr.br != nil {
|
||||
_, err = rr.br.Seek(0, 0 /*io.SeekStart*/)
|
||||
rr.req.Body = ioutil.NopCloser(rr.br)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rr.br == nil {
|
||||
// fall back to making a copy (only do this once)
|
||||
err = rr.prepareFromByteReader()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func removeRequestBody(req *http.Request) {
|
||||
req.Body = nil
|
||||
req.ContentLength = 0
|
||||
}
|
||||
66
vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go
generated
vendored
Normal file
66
vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// +build go1.8
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package autorest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RetriableRequest provides facilities for retrying an HTTP request.
|
||||
type RetriableRequest struct {
|
||||
req *http.Request
|
||||
rc io.ReadCloser
|
||||
br *bytes.Reader
|
||||
}
|
||||
|
||||
// Prepare signals that the request is about to be sent.
|
||||
func (rr *RetriableRequest) Prepare() (err error) {
|
||||
// preserve the request body; this is to support retry logic as
|
||||
// the underlying transport will always close the reqeust body
|
||||
if rr.req.Body != nil {
|
||||
if rr.rc != nil {
|
||||
rr.req.Body = rr.rc
|
||||
} else if rr.br != nil {
|
||||
_, err = rr.br.Seek(0, io.SeekStart)
|
||||
rr.req.Body = ioutil.NopCloser(rr.br)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rr.req.GetBody != nil {
|
||||
// this will allow us to preserve the body without having to
|
||||
// make a copy. note we need to do this on each iteration
|
||||
rr.rc, err = rr.req.GetBody()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if rr.br == nil {
|
||||
// fall back to making a copy (only do this once)
|
||||
err = rr.prepareFromByteReader()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func removeRequestBody(req *http.Request) {
|
||||
req.Body = nil
|
||||
req.GetBody = nil
|
||||
req.ContentLength = 0
|
||||
}
|
||||
307
vendor/github.com/Azure/go-autorest/autorest/sender.go
generated
vendored
Normal file
307
vendor/github.com/Azure/go-autorest/autorest/sender.go
generated
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Sender is the interface that wraps the Do method to send HTTP requests.
|
||||
//
|
||||
// The standard http.Client conforms to this interface.
|
||||
type Sender interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// SenderFunc is a method that implements the Sender interface.
|
||||
type SenderFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
// Do implements the Sender interface on SenderFunc.
|
||||
func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) {
|
||||
return sf(r)
|
||||
}
|
||||
|
||||
// SendDecorator takes and possibily decorates, by wrapping, a Sender. Decorators may affect the
|
||||
// http.Request and pass it along or, first, pass the http.Request along then react to the
|
||||
// http.Response result.
|
||||
type SendDecorator func(Sender) Sender
|
||||
|
||||
// CreateSender creates, decorates, and returns, as a Sender, the default http.Client.
|
||||
func CreateSender(decorators ...SendDecorator) Sender {
|
||||
return DecorateSender(&http.Client{}, decorators...)
|
||||
}
|
||||
|
||||
// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to
|
||||
// the Sender. Decorators are applied in the order received, but their affect upon the request
|
||||
// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a
|
||||
// post-decorator (pass the http.Request along and react to the results in http.Response).
|
||||
func DecorateSender(s Sender, decorators ...SendDecorator) Sender {
|
||||
for _, decorate := range decorators {
|
||||
s = decorate(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Send sends, by means of the default http.Client, the passed http.Request, returning the
|
||||
// http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which
|
||||
// it will apply the http.Client before invoking the Do method.
|
||||
//
|
||||
// Send is a convenience method and not recommended for production. Advanced users should use
|
||||
// SendWithSender, passing and sharing their own Sender (e.g., instance of http.Client).
|
||||
//
|
||||
// Send will not poll or retry requests.
|
||||
func Send(r *http.Request, decorators ...SendDecorator) (*http.Response, error) {
|
||||
return SendWithSender(&http.Client{}, r, decorators...)
|
||||
}
|
||||
|
||||
// SendWithSender sends the passed http.Request, through the provided Sender, returning the
|
||||
// http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which
|
||||
// it will apply the http.Client before invoking the Do method.
|
||||
//
|
||||
// SendWithSender will not poll or retry requests.
|
||||
func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*http.Response, error) {
|
||||
return DecorateSender(s, decorators...).Do(r)
|
||||
}
|
||||
|
||||
// AfterDelay returns a SendDecorator that delays for the passed time.Duration before
|
||||
// invoking the Sender. The delay may be terminated by closing the optional channel on the
|
||||
// http.Request. If canceled, no further Senders are invoked.
|
||||
func AfterDelay(d time.Duration) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if !DelayForBackoff(d, 0, r.Cancel) {
|
||||
return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay")
|
||||
}
|
||||
return s.Do(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// AsIs returns a SendDecorator that invokes the passed Sender without modifying the http.Request.
|
||||
func AsIs() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return s.Do(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DoCloseIfError returns a SendDecorator that first invokes the passed Sender after which
|
||||
// it closes the response if the passed Sender returns an error and the response body exists.
|
||||
func DoCloseIfError() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := s.Do(r)
|
||||
if err != nil {
|
||||
Respond(resp, ByDiscardingBody(), ByClosing())
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DoErrorIfStatusCode returns a SendDecorator that emits an error if the response StatusCode is
|
||||
// among the set passed. Since these are artificial errors, the response body may still require
|
||||
// closing.
|
||||
func DoErrorIfStatusCode(codes ...int) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := s.Do(r)
|
||||
if err == nil && ResponseHasStatusCode(resp, codes...) {
|
||||
err = NewErrorWithResponse("autorest", "DoErrorIfStatusCode", resp, "%v %v failed with %s",
|
||||
resp.Request.Method,
|
||||
resp.Request.URL,
|
||||
resp.Status)
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DoErrorUnlessStatusCode returns a SendDecorator that emits an error unless the response
|
||||
// StatusCode is among the set passed. Since these are artificial errors, the response body
|
||||
// may still require closing.
|
||||
func DoErrorUnlessStatusCode(codes ...int) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := s.Do(r)
|
||||
if err == nil && !ResponseHasStatusCode(resp, codes...) {
|
||||
err = NewErrorWithResponse("autorest", "DoErrorUnlessStatusCode", resp, "%v %v failed with %s",
|
||||
resp.Request.Method,
|
||||
resp.Request.URL,
|
||||
resp.Status)
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DoPollForStatusCodes returns a SendDecorator that polls if the http.Response contains one of the
|
||||
// passed status codes. It expects the http.Response to contain a Location header providing the
|
||||
// URL at which to poll (using GET) and will poll until the time passed is equal to or greater than
|
||||
// the supplied duration. It will delay between requests for the duration specified in the
|
||||
// RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by
|
||||
// closing the optional channel on the http.Request.
|
||||
func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...int) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
|
||||
resp, err = s.Do(r)
|
||||
|
||||
if err == nil && ResponseHasStatusCode(resp, codes...) {
|
||||
r, err = NewPollingRequest(resp, r.Cancel)
|
||||
|
||||
for err == nil && ResponseHasStatusCode(resp, codes...) {
|
||||
Respond(resp,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
resp, err = SendWithSender(s, r,
|
||||
AfterDelay(GetRetryAfter(resp, delay)))
|
||||
}
|
||||
}
|
||||
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DoRetryForAttempts returns a SendDecorator that retries a failed request for up to the specified
|
||||
// number of attempts, exponentially backing off between requests using the supplied backoff
|
||||
// time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on
|
||||
// the http.Request.
|
||||
func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
|
||||
rr := NewRetriableRequest(r)
|
||||
for attempt := 0; attempt < attempts; attempt++ {
|
||||
err = rr.Prepare()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp, err = s.Do(rr.Request())
|
||||
if err == nil {
|
||||
return resp, err
|
||||
}
|
||||
DelayForBackoff(backoff, attempt, r.Cancel)
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified
|
||||
// number of attempts, exponentially backing off between requests using the supplied backoff
|
||||
// time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on
|
||||
// the http.Request.
|
||||
func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
|
||||
rr := NewRetriableRequest(r)
|
||||
// Increment to add the first call (attempts denotes number of retries)
|
||||
attempts++
|
||||
for attempt := 0; attempt < attempts; attempt++ {
|
||||
err = rr.Prepare()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp, err = s.Do(rr.Request())
|
||||
if err != nil || !ResponseHasStatusCode(resp, codes...) {
|
||||
return resp, err
|
||||
}
|
||||
delayed := DelayWithRetryAfter(resp, r.Cancel)
|
||||
if !delayed {
|
||||
DelayForBackoff(backoff, attempt, r.Cancel)
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DelayWithRetryAfter invokes time.After for the duration specified in the "Retry-After" header in
|
||||
// responses with status code 429
|
||||
func DelayWithRetryAfter(resp *http.Response, cancel <-chan struct{}) bool {
|
||||
retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After"))
|
||||
if resp.StatusCode == http.StatusTooManyRequests && retryAfter > 0 {
|
||||
select {
|
||||
case <-time.After(time.Duration(retryAfter) * time.Second):
|
||||
return true
|
||||
case <-cancel:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DoRetryForDuration returns a SendDecorator that retries the request until the total time is equal
|
||||
// to or greater than the specified duration, exponentially backing off between requests using the
|
||||
// supplied backoff time.Duration (which may be zero). Retrying may be canceled by closing the
|
||||
// optional channel on the http.Request.
|
||||
func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
|
||||
rr := NewRetriableRequest(r)
|
||||
end := time.Now().Add(d)
|
||||
for attempt := 0; time.Now().Before(end); attempt++ {
|
||||
err = rr.Prepare()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp, err = s.Do(rr.Request())
|
||||
if err == nil {
|
||||
return resp, err
|
||||
}
|
||||
DelayForBackoff(backoff, attempt, r.Cancel)
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogging returns a SendDecorator that implements simple before and after logging of the
|
||||
// request.
|
||||
func WithLogging(logger *log.Logger) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
logger.Printf("Sending %s %s", r.Method, r.URL)
|
||||
resp, err := s.Do(r)
|
||||
if err != nil {
|
||||
logger.Printf("%s %s received error '%v'", r.Method, r.URL, err)
|
||||
} else {
|
||||
logger.Printf("%s %s received %s", r.Method, r.URL, resp.Status)
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DelayForBackoff invokes time.After for the supplied backoff duration raised to the power of
|
||||
// passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set
|
||||
// to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early,
|
||||
// returns false.
|
||||
// Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt
|
||||
// count.
|
||||
func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool {
|
||||
select {
|
||||
case <-time.After(time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second):
|
||||
return true
|
||||
case <-cancel:
|
||||
return false
|
||||
}
|
||||
}
|
||||
811
vendor/github.com/Azure/go-autorest/autorest/sender_test.go
generated
vendored
Normal file
811
vendor/github.com/Azure/go-autorest/autorest/sender_test.go
generated
vendored
Normal file
@@ -0,0 +1,811 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
func ExampleSendWithSender() {
|
||||
r := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(r)
|
||||
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(r, 10)
|
||||
|
||||
logger := log.New(os.Stdout, "autorest: ", 0)
|
||||
na := NullAuthorizer{}
|
||||
|
||||
req, _ := Prepare(&http.Request{},
|
||||
AsGet(),
|
||||
WithBaseURL("https://microsoft.com/a/b/c/"),
|
||||
na.WithAuthorization())
|
||||
|
||||
r, _ = SendWithSender(client, req,
|
||||
WithLogging(logger),
|
||||
DoErrorIfStatusCode(http.StatusAccepted),
|
||||
DoCloseIfError(),
|
||||
DoRetryForAttempts(5, time.Duration(0)))
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
// Output:
|
||||
// autorest: Sending GET https://microsoft.com/a/b/c/
|
||||
// autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted
|
||||
// autorest: Sending GET https://microsoft.com/a/b/c/
|
||||
// autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted
|
||||
// autorest: Sending GET https://microsoft.com/a/b/c/
|
||||
// autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted
|
||||
// autorest: Sending GET https://microsoft.com/a/b/c/
|
||||
// autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted
|
||||
// autorest: Sending GET https://microsoft.com/a/b/c/
|
||||
// autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted
|
||||
}
|
||||
|
||||
func ExampleDoRetryForAttempts() {
|
||||
client := mocks.NewSender()
|
||||
client.SetAndRepeatError(fmt.Errorf("Faux Error"), 10)
|
||||
|
||||
// Retry with backoff -- ensure returned Bodies are closed
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoCloseIfError(),
|
||||
DoRetryForAttempts(5, time.Duration(0)))
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
fmt.Printf("Retry stopped after %d attempts", client.Attempts())
|
||||
// Output: Retry stopped after 5 attempts
|
||||
}
|
||||
|
||||
func ExampleDoErrorIfStatusCode() {
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 NoContent", http.StatusNoContent), 10)
|
||||
|
||||
// Chain decorators to retry the request, up to five times, if the status code is 204
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoErrorIfStatusCode(http.StatusNoContent),
|
||||
DoCloseIfError(),
|
||||
DoRetryForAttempts(5, time.Duration(0)))
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
fmt.Printf("Retry stopped after %d attempts with code %s", client.Attempts(), r.Status)
|
||||
// Output: Retry stopped after 5 attempts with code 204 NoContent
|
||||
}
|
||||
|
||||
func TestSendWithSenderRunsDecoratorsInOrder(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
s := ""
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
withMessage(&s, "a"),
|
||||
withMessage(&s, "b"),
|
||||
withMessage(&s, "c"))
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: SendWithSender returned an error (%v)", err)
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
if s != "abc" {
|
||||
t.Fatalf("autorest: SendWithSender invoke decorators out of order; expected 'abc', received '%s'", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSender(t *testing.T) {
|
||||
f := false
|
||||
|
||||
s := CreateSender(
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
f = true
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
s.Do(&http.Request{})
|
||||
|
||||
if !f {
|
||||
t.Fatal("autorest: CreateSender failed to apply supplied decorator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
f := false
|
||||
|
||||
Send(&http.Request{},
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
f = true
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
})())
|
||||
|
||||
if !f {
|
||||
t.Fatal("autorest: Send failed to apply supplied decorator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterDelayWaits(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
|
||||
d := 2 * time.Second
|
||||
|
||||
tt := time.Now()
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
AfterDelay(d))
|
||||
s := time.Since(tt)
|
||||
if s < d {
|
||||
t.Fatal("autorest: AfterDelay failed to wait for at least the specified duration")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestAfterDelay_Cancels(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
cancel := make(chan struct{})
|
||||
delay := 5 * time.Second
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
tt := time.Now()
|
||||
go func() {
|
||||
req := mocks.NewRequest()
|
||||
req.Cancel = cancel
|
||||
wg.Done()
|
||||
SendWithSender(client, req,
|
||||
AfterDelay(delay))
|
||||
}()
|
||||
wg.Wait()
|
||||
close(cancel)
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if time.Since(tt) >= delay {
|
||||
t.Fatal("autorest: AfterDelay failed to cancel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterDelayDoesNotWaitTooLong(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
|
||||
d := 5 * time.Millisecond
|
||||
start := time.Now()
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
AfterDelay(d))
|
||||
|
||||
if time.Since(start) > (5 * d) {
|
||||
t.Fatal("autorest: AfterDelay waited too long (exceeded 5 times specified duration)")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestAsIs(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
|
||||
r1 := mocks.NewResponse()
|
||||
client.AppendResponse(r1)
|
||||
|
||||
r2, err := SendWithSender(client, mocks.NewRequest(),
|
||||
AsIs())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: AsIs returned an unexpected error (%v)", err)
|
||||
} else if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatalf("autorest: AsIs modified the response -- received %v, expected %v", r2, r1)
|
||||
}
|
||||
|
||||
Respond(r1,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
Respond(r2,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoCloseIfError(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest))
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoErrorIfStatusCode(http.StatusBadRequest),
|
||||
DoCloseIfError())
|
||||
|
||||
if r.Body.(*mocks.Body).IsOpen() {
|
||||
t.Fatal("autorest: Expected DoCloseIfError to close response body -- it was left open")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoCloseIfErrorAcceptsNilResponse(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
|
||||
SendWithSender(client, mocks.NewRequest(),
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := s.Do(r)
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
return nil, fmt.Errorf("Faux Error")
|
||||
})
|
||||
}
|
||||
})(),
|
||||
DoCloseIfError())
|
||||
}
|
||||
|
||||
func TestDoCloseIfErrorAcceptsNilBody(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
|
||||
SendWithSender(client, mocks.NewRequest(),
|
||||
(func() SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := s.Do(r)
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
resp.Body = nil
|
||||
return resp, fmt.Errorf("Faux Error")
|
||||
})
|
||||
}
|
||||
})(),
|
||||
DoCloseIfError())
|
||||
}
|
||||
|
||||
func TestDoErrorIfStatusCode(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest))
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoErrorIfStatusCode(http.StatusBadRequest),
|
||||
DoCloseIfError())
|
||||
if err == nil {
|
||||
t.Fatal("autorest: DoErrorIfStatusCode failed to emit an error for passed code")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoErrorIfStatusCodeIgnoresStatusCodes(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendResponse(newAcceptedResponse())
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoErrorIfStatusCode(http.StatusBadRequest),
|
||||
DoCloseIfError())
|
||||
if err != nil {
|
||||
t.Fatal("autorest: DoErrorIfStatusCode failed to ignore a status code")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoErrorUnlessStatusCode(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest))
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoErrorUnlessStatusCode(http.StatusAccepted),
|
||||
DoCloseIfError())
|
||||
if err == nil {
|
||||
t.Fatal("autorest: DoErrorUnlessStatusCode failed to emit an error for an unknown status code")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoErrorUnlessStatusCodeIgnoresStatusCodes(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendResponse(newAcceptedResponse())
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoErrorUnlessStatusCode(http.StatusAccepted),
|
||||
DoCloseIfError())
|
||||
if err != nil {
|
||||
t.Fatal("autorest: DoErrorUnlessStatusCode emitted an error for a knonwn status code")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoRetryForAttemptsStopsAfterSuccess(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForAttempts(5, time.Duration(0)))
|
||||
if client.Attempts() != 1 {
|
||||
t.Fatalf("autorest: DoRetryForAttempts failed to stop after success -- expected attempts %v, actual %v",
|
||||
1, client.Attempts())
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: DoRetryForAttempts returned an unexpected error (%v)", err)
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoRetryForAttemptsStopsAfterAttempts(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.SetAndRepeatError(fmt.Errorf("Faux Error"), 10)
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForAttempts(5, time.Duration(0)),
|
||||
DoCloseIfError())
|
||||
if err == nil {
|
||||
t.Fatal("autorest: Mock client failed to emit errors")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
if client.Attempts() != 5 {
|
||||
t.Fatal("autorest: DoRetryForAttempts failed to stop after specified number of attempts")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoRetryForAttemptsReturnsResponse(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.SetError(fmt.Errorf("Faux Error"))
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForAttempts(1, time.Duration(0)))
|
||||
if err == nil {
|
||||
t.Fatal("autorest: Mock client failed to emit errors")
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
t.Fatal("autorest: DoRetryForAttempts failed to return the underlying response")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoRetryForDurationStopsAfterSuccess(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForDuration(10*time.Millisecond, time.Duration(0)))
|
||||
if client.Attempts() != 1 {
|
||||
t.Fatalf("autorest: DoRetryForDuration failed to stop after success -- expected attempts %v, actual %v",
|
||||
1, client.Attempts())
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: DoRetryForDuration returned an unexpected error (%v)", err)
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoRetryForDurationStopsAfterDuration(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1)
|
||||
|
||||
d := 5 * time.Millisecond
|
||||
start := time.Now()
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForDuration(d, time.Duration(0)),
|
||||
DoCloseIfError())
|
||||
if err == nil {
|
||||
t.Fatal("autorest: Mock client failed to emit errors")
|
||||
}
|
||||
|
||||
if time.Since(start) < d {
|
||||
t.Fatal("autorest: DoRetryForDuration failed stopped too soon")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoRetryForDurationStopsWithinReason(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1)
|
||||
|
||||
d := 5 * time.Second
|
||||
start := time.Now()
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForDuration(d, time.Duration(0)),
|
||||
DoCloseIfError())
|
||||
if err == nil {
|
||||
t.Fatal("autorest: Mock client failed to emit errors")
|
||||
}
|
||||
|
||||
if time.Since(start) > (5 * d) {
|
||||
t.Fatal("autorest: DoRetryForDuration failed stopped soon enough (exceeded 5 times specified duration)")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoRetryForDurationReturnsResponse(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1)
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForDuration(10*time.Millisecond, time.Duration(0)),
|
||||
DoCloseIfError())
|
||||
if err == nil {
|
||||
t.Fatal("autorest: Mock client failed to emit errors")
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
t.Fatal("autorest: DoRetryForDuration failed to return the underlying response")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDelayForBackoff(t *testing.T) {
|
||||
d := 2 * time.Second
|
||||
start := time.Now()
|
||||
DelayForBackoff(d, 0, nil)
|
||||
if time.Since(start) < d {
|
||||
t.Fatal("autorest: DelayForBackoff did not delay as long as expected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelayForBackoff_Cancels(t *testing.T) {
|
||||
cancel := make(chan struct{})
|
||||
delay := 5 * time.Second
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
start := time.Now()
|
||||
go func() {
|
||||
wg.Done()
|
||||
DelayForBackoff(delay, 0, cancel)
|
||||
}()
|
||||
wg.Wait()
|
||||
close(cancel)
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if time.Since(start) >= delay {
|
||||
t.Fatal("autorest: DelayForBackoff failed to cancel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelayForBackoffWithinReason(t *testing.T) {
|
||||
d := 5 * time.Second
|
||||
maxCoefficient := 2
|
||||
start := time.Now()
|
||||
DelayForBackoff(d, 0, nil)
|
||||
if time.Since(start) > (time.Duration(maxCoefficient) * d) {
|
||||
|
||||
t.Fatalf("autorest: DelayForBackoff delayed too long (exceeded %d times the specified duration)", maxCoefficient)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoPollForStatusCodes_IgnoresUnspecifiedStatusCodes(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoPollForStatusCodes(time.Duration(0), time.Duration(0)))
|
||||
|
||||
if client.Attempts() != 1 {
|
||||
t.Fatalf("autorest: Sender#DoPollForStatusCodes polled for unspecified status code")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoPollForStatusCodes_PollsForSpecifiedStatusCodes(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendResponse(newAcceptedResponse())
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted))
|
||||
|
||||
if client.Attempts() != 2 {
|
||||
t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to poll for specified status code")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoPollForStatusCodes_CanBeCanceled(t *testing.T) {
|
||||
cancel := make(chan struct{})
|
||||
delay := 5 * time.Second
|
||||
|
||||
r := mocks.NewResponse()
|
||||
mocks.SetAcceptedHeaders(r)
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(r, 100)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
start := time.Now()
|
||||
go func() {
|
||||
wg.Done()
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted))
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}()
|
||||
wg.Wait()
|
||||
close(cancel)
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if time.Since(start) >= delay {
|
||||
t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to cancel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoPollForStatusCodes_ClosesAllNonreturnedResponseBodiesWhenPolling(t *testing.T) {
|
||||
resp := newAcceptedResponse()
|
||||
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(resp, 2)
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted))
|
||||
|
||||
if resp.Body.(*mocks.Body).IsOpen() || resp.Body.(*mocks.Body).CloseAttempts() < 2 {
|
||||
t.Fatalf("autorest: Sender#DoPollForStatusCodes did not close unreturned response bodies")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoPollForStatusCodes_LeavesLastResponseBodyOpen(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendResponse(newAcceptedResponse())
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted))
|
||||
|
||||
if !r.Body.(*mocks.Body).IsOpen() {
|
||||
t.Fatalf("autorest: Sender#DoPollForStatusCodes did not leave open the body of the last response")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoPollForStatusCodes_StopsPollingAfterAnError(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(newAcceptedResponse(), 5)
|
||||
client.SetError(fmt.Errorf("Faux Error"))
|
||||
client.SetEmitErrorAfter(1)
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted))
|
||||
|
||||
if client.Attempts() > 2 {
|
||||
t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to stop polling after receiving an error")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoPollForStatusCodes_ReturnsPollingError(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(newAcceptedResponse(), 5)
|
||||
client.SetError(fmt.Errorf("Faux Error"))
|
||||
client.SetEmitErrorAfter(1)
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequest(),
|
||||
DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted))
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to return error from polling")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestWithLogging_Logs(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.New(buf, "autorest: ", 0)
|
||||
client := mocks.NewSender()
|
||||
|
||||
r, _ := SendWithSender(client, &http.Request{},
|
||||
WithLogging(logger))
|
||||
|
||||
if buf.String() == "" {
|
||||
t.Fatal("autorest: Sender#WithLogging failed to log the request")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestWithLogging_HandlesMissingResponse(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
logger := log.New(buf, "autorest: ", 0)
|
||||
client := mocks.NewSender()
|
||||
client.AppendResponse(nil)
|
||||
client.SetError(fmt.Errorf("Faux Error"))
|
||||
|
||||
r, err := SendWithSender(client, &http.Request{},
|
||||
WithLogging(logger))
|
||||
|
||||
if r != nil || err == nil {
|
||||
t.Fatal("autorest: Sender#WithLogging returned a valid response -- expecting nil")
|
||||
}
|
||||
if buf.String() == "" {
|
||||
t.Fatal("autorest: Sender#WithLogging failed to log the request for a nil response")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
}
|
||||
|
||||
func TestDoRetryForStatusCodesWithSuccess(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("408 Request Timeout", http.StatusRequestTimeout), 2)
|
||||
client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK))
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForStatusCodes(5, time.Duration(2*time.Second), http.StatusRequestTimeout),
|
||||
)
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
if client.Attempts() != 3 {
|
||||
t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: StatusCode %v in %v attempts; Want: StatusCode 200 OK in 2 attempts -- ",
|
||||
r.Status, client.Attempts()-1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoRetryForStatusCodesWithNoSuccess(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("504 Gateway Timeout", http.StatusGatewayTimeout), 5)
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForStatusCodes(2, time.Duration(2*time.Second), http.StatusGatewayTimeout),
|
||||
)
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
if client.Attempts() != 3 {
|
||||
t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: failed stop after %v retry attempts; Want: Stop after 2 retry attempts",
|
||||
client.Attempts()-1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoRetryForStatusCodes_CodeNotInRetryList(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 No Content", http.StatusNoContent), 1)
|
||||
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForStatusCodes(6, time.Duration(2*time.Second), http.StatusGatewayTimeout),
|
||||
)
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
if client.Attempts() != 1 || r.Status != "204 No Content" {
|
||||
t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: Retry attempts %v for StatusCode %v; Want: 0 attempts for StatusCode 204",
|
||||
client.Attempts(), r.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoRetryForStatusCodes_RequestBodyReadError(t *testing.T) {
|
||||
client := mocks.NewSender()
|
||||
client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 No Content", http.StatusNoContent), 2)
|
||||
|
||||
r, err := SendWithSender(client, mocks.NewRequestWithCloseBody(),
|
||||
DoRetryForStatusCodes(6, time.Duration(2*time.Second), http.StatusGatewayTimeout),
|
||||
)
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
if err == nil || client.Attempts() != 0 {
|
||||
t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: Not failed for request body read error; Want: Failed for body read error - %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func newAcceptedResponse() *http.Response {
|
||||
resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted)
|
||||
mocks.SetAcceptedHeaders(resp)
|
||||
return resp
|
||||
}
|
||||
|
||||
func TestDelayWithRetryAfterWithSuccess(t *testing.T) {
|
||||
after, retries := 5, 2
|
||||
totalSecs := after * retries
|
||||
|
||||
client := mocks.NewSender()
|
||||
resp := mocks.NewResponseWithStatus("429 Too many requests", http.StatusTooManyRequests)
|
||||
mocks.SetResponseHeader(resp, "Retry-After", fmt.Sprintf("%v", after))
|
||||
client.AppendAndRepeatResponse(resp, retries)
|
||||
client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK))
|
||||
|
||||
d := time.Second * time.Duration(totalSecs)
|
||||
start := time.Now()
|
||||
r, _ := SendWithSender(client, mocks.NewRequest(),
|
||||
DoRetryForStatusCodes(5, time.Duration(time.Second), http.StatusTooManyRequests),
|
||||
)
|
||||
|
||||
if time.Since(start) < d {
|
||||
t.Fatal("autorest: DelayWithRetryAfter failed stopped too soon")
|
||||
}
|
||||
|
||||
Respond(r,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
|
||||
if client.Attempts() != 3 {
|
||||
t.Fatalf("autorest: Sender#DelayWithRetryAfter -- Got: StatusCode %v in %v attempts; Want: StatusCode 200 OK in 2 attempts -- ",
|
||||
r.Status, client.Attempts()-1)
|
||||
}
|
||||
}
|
||||
147
vendor/github.com/Azure/go-autorest/autorest/to/convert.go
generated
vendored
Normal file
147
vendor/github.com/Azure/go-autorest/autorest/to/convert.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Package to provides helpers to ease working with pointer values of marshalled structures.
|
||||
*/
|
||||
package to
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// String returns a string value for the passed string pointer. It returns the empty string if the
|
||||
// pointer is nil.
|
||||
func String(s *string) string {
|
||||
if s != nil {
|
||||
return *s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// StringPtr returns a pointer to the passed string.
|
||||
func StringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
// StringSlice returns a string slice value for the passed string slice pointer. It returns a nil
|
||||
// slice if the pointer is nil.
|
||||
func StringSlice(s *[]string) []string {
|
||||
if s != nil {
|
||||
return *s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringSlicePtr returns a pointer to the passed string slice.
|
||||
func StringSlicePtr(s []string) *[]string {
|
||||
return &s
|
||||
}
|
||||
|
||||
// StringMap returns a map of strings built from the map of string pointers. The empty string is
|
||||
// used for nil pointers.
|
||||
func StringMap(msp map[string]*string) map[string]string {
|
||||
ms := make(map[string]string, len(msp))
|
||||
for k, sp := range msp {
|
||||
if sp != nil {
|
||||
ms[k] = *sp
|
||||
} else {
|
||||
ms[k] = ""
|
||||
}
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
// StringMapPtr returns a pointer to a map of string pointers built from the passed map of strings.
|
||||
func StringMapPtr(ms map[string]string) *map[string]*string {
|
||||
msp := make(map[string]*string, len(ms))
|
||||
for k, s := range ms {
|
||||
msp[k] = StringPtr(s)
|
||||
}
|
||||
return &msp
|
||||
}
|
||||
|
||||
// Bool returns a bool value for the passed bool pointer. It returns false if the pointer is nil.
|
||||
func Bool(b *bool) bool {
|
||||
if b != nil {
|
||||
return *b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BoolPtr returns a pointer to the passed bool.
|
||||
func BoolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
// Int returns an int value for the passed int pointer. It returns 0 if the pointer is nil.
|
||||
func Int(i *int) int {
|
||||
if i != nil {
|
||||
return *i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IntPtr returns a pointer to the passed int.
|
||||
func IntPtr(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Int32 returns an int value for the passed int pointer. It returns 0 if the pointer is nil.
|
||||
func Int32(i *int32) int32 {
|
||||
if i != nil {
|
||||
return *i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Int32Ptr returns a pointer to the passed int32.
|
||||
func Int32Ptr(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Int64 returns an int value for the passed int pointer. It returns 0 if the pointer is nil.
|
||||
func Int64(i *int64) int64 {
|
||||
if i != nil {
|
||||
return *i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Int64Ptr returns a pointer to the passed int64.
|
||||
func Int64Ptr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Float32 returns an int value for the passed int pointer. It returns 0.0 if the pointer is nil.
|
||||
func Float32(i *float32) float32 {
|
||||
if i != nil {
|
||||
return *i
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
// Float32Ptr returns a pointer to the passed float32.
|
||||
func Float32Ptr(i float32) *float32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Float64 returns an int value for the passed int pointer. It returns 0.0 if the pointer is nil.
|
||||
func Float64(i *float64) float64 {
|
||||
if i != nil {
|
||||
return *i
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
// Float64Ptr returns a pointer to the passed float64.
|
||||
func Float64Ptr(i float64) *float64 {
|
||||
return &i
|
||||
}
|
||||
234
vendor/github.com/Azure/go-autorest/autorest/to/convert_test.go
generated
vendored
Normal file
234
vendor/github.com/Azure/go-autorest/autorest/to/convert_test.go
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
package to
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
v := ""
|
||||
if String(&v) != v {
|
||||
t.Fatalf("to: String failed to return the correct string -- expected %v, received %v",
|
||||
v, String(&v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringHandlesNil(t *testing.T) {
|
||||
if String(nil) != "" {
|
||||
t.Fatalf("to: String failed to correctly convert nil -- expected %v, received %v",
|
||||
"", String(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringPtr(t *testing.T) {
|
||||
v := ""
|
||||
if *StringPtr(v) != v {
|
||||
t.Fatalf("to: StringPtr failed to return the correct string -- expected %v, received %v",
|
||||
v, *StringPtr(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSlice(t *testing.T) {
|
||||
v := []string{}
|
||||
if out := StringSlice(&v); !reflect.DeepEqual(out, v) {
|
||||
t.Fatalf("to: StringSlice failed to return the correct slice -- expected %v, received %v",
|
||||
v, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceHandlesNil(t *testing.T) {
|
||||
if out := StringSlice(nil); out != nil {
|
||||
t.Fatalf("to: StringSlice failed to correctly convert nil -- expected %v, received %v",
|
||||
nil, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSlicePtr(t *testing.T) {
|
||||
v := []string{"a", "b"}
|
||||
if out := StringSlicePtr(v); !reflect.DeepEqual(*out, v) {
|
||||
t.Fatalf("to: StringSlicePtr failed to return the correct slice -- expected %v, received %v",
|
||||
v, *out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringMap(t *testing.T) {
|
||||
msp := map[string]*string{"foo": StringPtr("foo"), "bar": StringPtr("bar"), "baz": StringPtr("baz")}
|
||||
for k, v := range StringMap(msp) {
|
||||
if *msp[k] != v {
|
||||
t.Fatalf("to: StringMap incorrectly converted an entry -- expected [%s]%v, received[%s]%v",
|
||||
k, v, k, *msp[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringMapHandlesNil(t *testing.T) {
|
||||
msp := map[string]*string{"foo": StringPtr("foo"), "bar": nil, "baz": StringPtr("baz")}
|
||||
for k, v := range StringMap(msp) {
|
||||
if msp[k] == nil && v != "" {
|
||||
t.Fatalf("to: StringMap incorrectly converted a nil entry -- expected [%s]%v, received[%s]%v",
|
||||
k, v, k, *msp[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringMapPtr(t *testing.T) {
|
||||
ms := map[string]string{"foo": "foo", "bar": "bar", "baz": "baz"}
|
||||
for k, msp := range *StringMapPtr(ms) {
|
||||
if ms[k] != *msp {
|
||||
t.Fatalf("to: StringMapPtr incorrectly converted an entry -- expected [%s]%v, received[%s]%v",
|
||||
k, ms[k], k, *msp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBool(t *testing.T) {
|
||||
v := false
|
||||
if Bool(&v) != v {
|
||||
t.Fatalf("to: Bool failed to return the correct string -- expected %v, received %v",
|
||||
v, Bool(&v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoolHandlesNil(t *testing.T) {
|
||||
if Bool(nil) != false {
|
||||
t.Fatalf("to: Bool failed to correctly convert nil -- expected %v, received %v",
|
||||
false, Bool(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoolPtr(t *testing.T) {
|
||||
v := false
|
||||
if *BoolPtr(v) != v {
|
||||
t.Fatalf("to: BoolPtr failed to return the correct string -- expected %v, received %v",
|
||||
v, *BoolPtr(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
v := 0
|
||||
if Int(&v) != v {
|
||||
t.Fatalf("to: Int failed to return the correct string -- expected %v, received %v",
|
||||
v, Int(&v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntHandlesNil(t *testing.T) {
|
||||
if Int(nil) != 0 {
|
||||
t.Fatalf("to: Int failed to correctly convert nil -- expected %v, received %v",
|
||||
0, Int(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntPtr(t *testing.T) {
|
||||
v := 0
|
||||
if *IntPtr(v) != v {
|
||||
t.Fatalf("to: IntPtr failed to return the correct string -- expected %v, received %v",
|
||||
v, *IntPtr(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32(t *testing.T) {
|
||||
v := int32(0)
|
||||
if Int32(&v) != v {
|
||||
t.Fatalf("to: Int32 failed to return the correct string -- expected %v, received %v",
|
||||
v, Int32(&v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32HandlesNil(t *testing.T) {
|
||||
if Int32(nil) != int32(0) {
|
||||
t.Fatalf("to: Int32 failed to correctly convert nil -- expected %v, received %v",
|
||||
0, Int32(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32Ptr(t *testing.T) {
|
||||
v := int32(0)
|
||||
if *Int32Ptr(v) != v {
|
||||
t.Fatalf("to: Int32Ptr failed to return the correct string -- expected %v, received %v",
|
||||
v, *Int32Ptr(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
v := int64(0)
|
||||
if Int64(&v) != v {
|
||||
t.Fatalf("to: Int64 failed to return the correct string -- expected %v, received %v",
|
||||
v, Int64(&v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64HandlesNil(t *testing.T) {
|
||||
if Int64(nil) != int64(0) {
|
||||
t.Fatalf("to: Int64 failed to correctly convert nil -- expected %v, received %v",
|
||||
0, Int64(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64Ptr(t *testing.T) {
|
||||
v := int64(0)
|
||||
if *Int64Ptr(v) != v {
|
||||
t.Fatalf("to: Int64Ptr failed to return the correct string -- expected %v, received %v",
|
||||
v, *Int64Ptr(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat32(t *testing.T) {
|
||||
v := float32(0)
|
||||
if Float32(&v) != v {
|
||||
t.Fatalf("to: Float32 failed to return the correct string -- expected %v, received %v",
|
||||
v, Float32(&v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat32HandlesNil(t *testing.T) {
|
||||
if Float32(nil) != float32(0) {
|
||||
t.Fatalf("to: Float32 failed to correctly convert nil -- expected %v, received %v",
|
||||
0, Float32(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat32Ptr(t *testing.T) {
|
||||
v := float32(0)
|
||||
if *Float32Ptr(v) != v {
|
||||
t.Fatalf("to: Float32Ptr failed to return the correct string -- expected %v, received %v",
|
||||
v, *Float32Ptr(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat64(t *testing.T) {
|
||||
v := float64(0)
|
||||
if Float64(&v) != v {
|
||||
t.Fatalf("to: Float64 failed to return the correct string -- expected %v, received %v",
|
||||
v, Float64(&v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat64HandlesNil(t *testing.T) {
|
||||
if Float64(nil) != float64(0) {
|
||||
t.Fatalf("to: Float64 failed to correctly convert nil -- expected %v, received %v",
|
||||
0, Float64(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat64Ptr(t *testing.T) {
|
||||
v := float64(0)
|
||||
if *Float64Ptr(v) != v {
|
||||
t.Fatalf("to: Float64Ptr failed to return the correct string -- expected %v, received %v",
|
||||
v, *Float64Ptr(v))
|
||||
}
|
||||
}
|
||||
192
vendor/github.com/Azure/go-autorest/autorest/utility.go
generated
vendored
Normal file
192
vendor/github.com/Azure/go-autorest/autorest/utility.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EncodedAs is a series of constants specifying various data encodings
|
||||
type EncodedAs string
|
||||
|
||||
const (
|
||||
// EncodedAsJSON states that data is encoded as JSON
|
||||
EncodedAsJSON EncodedAs = "JSON"
|
||||
|
||||
// EncodedAsXML states that data is encoded as Xml
|
||||
EncodedAsXML EncodedAs = "XML"
|
||||
)
|
||||
|
||||
// Decoder defines the decoding method json.Decoder and xml.Decoder share
|
||||
type Decoder interface {
|
||||
Decode(v interface{}) error
|
||||
}
|
||||
|
||||
// NewDecoder creates a new decoder appropriate to the passed encoding.
|
||||
// encodedAs specifies the type of encoding and r supplies the io.Reader containing the
|
||||
// encoded data.
|
||||
func NewDecoder(encodedAs EncodedAs, r io.Reader) Decoder {
|
||||
if encodedAs == EncodedAsJSON {
|
||||
return json.NewDecoder(r)
|
||||
} else if encodedAs == EncodedAsXML {
|
||||
return xml.NewDecoder(r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyAndDecode decodes the data from the passed io.Reader while making a copy. Having a copy
|
||||
// is especially useful if there is a chance the data will fail to decode.
|
||||
// encodedAs specifies the expected encoding, r provides the io.Reader to the data, and v
|
||||
// is the decoding destination.
|
||||
func CopyAndDecode(encodedAs EncodedAs, r io.Reader, v interface{}) (bytes.Buffer, error) {
|
||||
b := bytes.Buffer{}
|
||||
return b, NewDecoder(encodedAs, io.TeeReader(r, &b)).Decode(v)
|
||||
}
|
||||
|
||||
// TeeReadCloser returns a ReadCloser that writes to w what it reads from rc.
|
||||
// It utilizes io.TeeReader to copy the data read and has the same behavior when reading.
|
||||
// Further, when it is closed, it ensures that rc is closed as well.
|
||||
func TeeReadCloser(rc io.ReadCloser, w io.Writer) io.ReadCloser {
|
||||
return &teeReadCloser{rc, io.TeeReader(rc, w)}
|
||||
}
|
||||
|
||||
type teeReadCloser struct {
|
||||
rc io.ReadCloser
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (t *teeReadCloser) Read(p []byte) (int, error) {
|
||||
return t.r.Read(p)
|
||||
}
|
||||
|
||||
func (t *teeReadCloser) Close() error {
|
||||
return t.rc.Close()
|
||||
}
|
||||
|
||||
func containsInt(ints []int, n int) bool {
|
||||
for _, i := range ints {
|
||||
if i == n {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func escapeValueStrings(m map[string]string) map[string]string {
|
||||
for key, value := range m {
|
||||
m[key] = url.QueryEscape(value)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func ensureValueStrings(mapOfInterface map[string]interface{}) map[string]string {
|
||||
mapOfStrings := make(map[string]string)
|
||||
for key, value := range mapOfInterface {
|
||||
mapOfStrings[key] = ensureValueString(value)
|
||||
}
|
||||
return mapOfStrings
|
||||
}
|
||||
|
||||
func ensureValueString(value interface{}) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return v
|
||||
case []byte:
|
||||
return string(v)
|
||||
default:
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
// MapToValues method converts map[string]interface{} to url.Values.
|
||||
func MapToValues(m map[string]interface{}) url.Values {
|
||||
v := url.Values{}
|
||||
for key, value := range m {
|
||||
x := reflect.ValueOf(value)
|
||||
if x.Kind() == reflect.Array || x.Kind() == reflect.Slice {
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
v.Add(key, ensureValueString(x.Index(i)))
|
||||
}
|
||||
} else {
|
||||
v.Add(key, ensureValueString(value))
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// String method converts interface v to string. If interface is a list, it
|
||||
// joins list elements using separator.
|
||||
func String(v interface{}, sep ...string) string {
|
||||
if len(sep) > 0 {
|
||||
return ensureValueString(strings.Join(v.([]string), sep[0]))
|
||||
}
|
||||
return ensureValueString(v)
|
||||
}
|
||||
|
||||
// Encode method encodes url path and query parameters.
|
||||
func Encode(location string, v interface{}, sep ...string) string {
|
||||
s := String(v, sep...)
|
||||
switch strings.ToLower(location) {
|
||||
case "path":
|
||||
return pathEscape(s)
|
||||
case "query":
|
||||
return queryEscape(s)
|
||||
default:
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func pathEscape(s string) string {
|
||||
return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
|
||||
}
|
||||
|
||||
func queryEscape(s string) string {
|
||||
return url.QueryEscape(s)
|
||||
}
|
||||
|
||||
// This method is same as Encode() method of "net/url" go package,
|
||||
// except it does not encode the query parameters because they
|
||||
// already come encoded. It formats values map in query format (bar=foo&a=b).
|
||||
func createQuery(v url.Values) string {
|
||||
var buf bytes.Buffer
|
||||
keys := make([]string, 0, len(v))
|
||||
for k := range v {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
vs := v[k]
|
||||
prefix := url.QueryEscape(k) + "="
|
||||
for _, v := range vs {
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
buf.WriteString(prefix)
|
||||
buf.WriteString(v)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
382
vendor/github.com/Azure/go-autorest/autorest/utility_test.go
generated
vendored
Normal file
382
vendor/github.com/Azure/go-autorest/autorest/utility_test.go
generated
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/mocks"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonT = `
|
||||
{
|
||||
"name":"Rob Pike",
|
||||
"age":42
|
||||
}`
|
||||
xmlT = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Person>
|
||||
<Name>Rob Pike</Name>
|
||||
<Age>42</Age>
|
||||
</Person>`
|
||||
)
|
||||
|
||||
func TestNewDecoderCreatesJSONDecoder(t *testing.T) {
|
||||
d := NewDecoder(EncodedAsJSON, strings.NewReader(jsonT))
|
||||
_, ok := d.(*json.Decoder)
|
||||
if d == nil || !ok {
|
||||
t.Fatal("autorest: NewDecoder failed to create a JSON decoder when requested")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDecoderCreatesXMLDecoder(t *testing.T) {
|
||||
d := NewDecoder(EncodedAsXML, strings.NewReader(xmlT))
|
||||
_, ok := d.(*xml.Decoder)
|
||||
if d == nil || !ok {
|
||||
t.Fatal("autorest: NewDecoder failed to create an XML decoder when requested")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDecoderReturnsNilForUnknownEncoding(t *testing.T) {
|
||||
d := NewDecoder("unknown", strings.NewReader(xmlT))
|
||||
if d != nil {
|
||||
t.Fatal("autorest: NewDecoder created a decoder for an unknown encoding")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyAndDecodeDecodesJSON(t *testing.T) {
|
||||
_, err := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT), &mocks.T{})
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: CopyAndDecode returned an error with valid JSON - %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyAndDecodeDecodesXML(t *testing.T) {
|
||||
_, err := CopyAndDecode(EncodedAsXML, strings.NewReader(xmlT), &mocks.T{})
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: CopyAndDecode returned an error with valid XML - %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyAndDecodeReturnsJSONDecodingErrors(t *testing.T) {
|
||||
_, err := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT[0:len(jsonT)-2]), &mocks.T{})
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: CopyAndDecode failed to return an error with invalid JSON")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyAndDecodeReturnsXMLDecodingErrors(t *testing.T) {
|
||||
_, err := CopyAndDecode(EncodedAsXML, strings.NewReader(xmlT[0:len(xmlT)-2]), &mocks.T{})
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: CopyAndDecode failed to return an error with invalid XML")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyAndDecodeAlwaysReturnsACopy(t *testing.T) {
|
||||
b, _ := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT), &mocks.T{})
|
||||
if b.String() != jsonT {
|
||||
t.Fatalf("autorest: CopyAndDecode failed to return a valid copy of the data - %v", b.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeeReadCloser_Copies(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
r.Body = TeeReadCloser(r.Body, b)
|
||||
|
||||
err := Respond(r,
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: TeeReadCloser returned an unexpected error -- %v", err)
|
||||
}
|
||||
if b.String() != jsonT {
|
||||
t.Fatalf("autorest: TeeReadCloser failed to copy the bytes read")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeeReadCloser_PassesReadErrors(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
|
||||
r.Body.(*mocks.Body).Close()
|
||||
r.Body = TeeReadCloser(r.Body, &bytes.Buffer{})
|
||||
|
||||
err := Respond(r,
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
if err == nil {
|
||||
t.Fatalf("autorest: TeeReadCloser failed to return the expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeeReadCloser_ClosesWrappedReader(t *testing.T) {
|
||||
v := &mocks.T{}
|
||||
r := mocks.NewResponseWithContent(jsonT)
|
||||
|
||||
b := r.Body.(*mocks.Body)
|
||||
r.Body = TeeReadCloser(r.Body, &bytes.Buffer{})
|
||||
err := Respond(r,
|
||||
ByUnmarshallingJSON(v),
|
||||
ByClosing())
|
||||
if err != nil {
|
||||
t.Fatalf("autorest: TeeReadCloser returned an unexpected error -- %v", err)
|
||||
}
|
||||
if b.IsOpen() {
|
||||
t.Fatalf("autorest: TeeReadCloser failed to close the nested io.ReadCloser")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsIntFindsValue(t *testing.T) {
|
||||
ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
v := 5
|
||||
if !containsInt(ints, v) {
|
||||
t.Fatalf("autorest: containsInt failed to find %v in %v", v, ints)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsIntDoesNotFindValue(t *testing.T) {
|
||||
ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
v := 42
|
||||
if containsInt(ints, v) {
|
||||
t.Fatalf("autorest: containsInt unexpectedly found %v in %v", v, ints)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsIntAcceptsEmptyList(t *testing.T) {
|
||||
ints := make([]int, 10)
|
||||
if containsInt(ints, 42) {
|
||||
t.Fatalf("autorest: containsInt failed to handle an empty list")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsIntAcceptsNilList(t *testing.T) {
|
||||
var ints []int
|
||||
if containsInt(ints, 42) {
|
||||
t.Fatalf("autorest: containsInt failed to handle an nil list")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapeStrings(t *testing.T) {
|
||||
m := map[string]string{
|
||||
"string": "a long string with = odd characters",
|
||||
"int": "42",
|
||||
"nil": "",
|
||||
}
|
||||
r := map[string]string{
|
||||
"string": "a+long+string+with+%3D+odd+characters",
|
||||
"int": "42",
|
||||
"nil": "",
|
||||
}
|
||||
v := escapeValueStrings(m)
|
||||
if !reflect.DeepEqual(v, r) {
|
||||
t.Fatalf("autorest: ensureValueStrings returned %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureStrings(t *testing.T) {
|
||||
m := map[string]interface{}{
|
||||
"string": "string",
|
||||
"int": 42,
|
||||
"nil": nil,
|
||||
"bytes": []byte{255, 254, 253},
|
||||
}
|
||||
r := map[string]string{
|
||||
"string": "string",
|
||||
"int": "42",
|
||||
"nil": "",
|
||||
"bytes": string([]byte{255, 254, 253}),
|
||||
}
|
||||
v := ensureValueStrings(m)
|
||||
if !reflect.DeepEqual(v, r) {
|
||||
t.Fatalf("autorest: ensureValueStrings returned %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleString() {
|
||||
m := []string{
|
||||
"string1",
|
||||
"string2",
|
||||
"string3",
|
||||
}
|
||||
|
||||
fmt.Println(String(m, ","))
|
||||
// Output: string1,string2,string3
|
||||
}
|
||||
|
||||
func TestStringWithValidString(t *testing.T) {
|
||||
i := 123
|
||||
if String(i) != "123" {
|
||||
t.Fatal("autorest: String method failed to convert integer 123 to string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeWithValidPath(t *testing.T) {
|
||||
s := Encode("Path", "Hello Gopher")
|
||||
if s != "Hello%20Gopher" {
|
||||
t.Fatalf("autorest: Encode method failed for valid path encoding. Got: %v; Want: %v", s, "Hello%20Gopher")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeWithValidQuery(t *testing.T) {
|
||||
s := Encode("Query", "Hello Gopher")
|
||||
if s != "Hello+Gopher" {
|
||||
t.Fatalf("autorest: Encode method failed for valid query encoding. Got: '%v'; Want: 'Hello+Gopher'", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeWithValidNotPathQuery(t *testing.T) {
|
||||
s := Encode("Host", "Hello Gopher")
|
||||
if s != "Hello Gopher" {
|
||||
t.Fatalf("autorest: Encode method failed for parameter not query or path. Got: '%v'; Want: 'Hello Gopher'", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapToValues(t *testing.T) {
|
||||
m := map[string]interface{}{
|
||||
"a": "a",
|
||||
"b": 2,
|
||||
}
|
||||
v := url.Values{}
|
||||
v.Add("a", "a")
|
||||
v.Add("b", "2")
|
||||
if !isEqual(v, MapToValues(m)) {
|
||||
t.Fatalf("autorest: MapToValues method failed to return correct values - expected(%v) got(%v)", v, MapToValues(m))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapToValuesWithArrayValues(t *testing.T) {
|
||||
m := map[string]interface{}{
|
||||
"a": []string{"a", "b"},
|
||||
"b": 2,
|
||||
"c": []int{3, 4},
|
||||
}
|
||||
v := url.Values{}
|
||||
v.Add("a", "a")
|
||||
v.Add("a", "b")
|
||||
v.Add("b", "2")
|
||||
v.Add("c", "3")
|
||||
v.Add("c", "4")
|
||||
|
||||
if !isEqual(v, MapToValues(m)) {
|
||||
t.Fatalf("autorest: MapToValues method failed to return correct values - expected(%v) got(%v)", v, MapToValues(m))
|
||||
}
|
||||
}
|
||||
|
||||
func isEqual(v, u url.Values) bool {
|
||||
for key, value := range v {
|
||||
if len(u[key]) == 0 {
|
||||
return false
|
||||
}
|
||||
sort.Strings(value)
|
||||
sort.Strings(u[key])
|
||||
for i := range value {
|
||||
if value[i] != u[key][i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
u.Del(key)
|
||||
}
|
||||
if len(u) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func doEnsureBodyClosed(t *testing.T) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := s.Do(r)
|
||||
if resp != nil && resp.Body != nil && resp.Body.(*mocks.Body).IsOpen() {
|
||||
t.Fatal("autorest: Expected Body to be closed -- it was left open")
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockAuthorizer struct{}
|
||||
|
||||
func (ma mockAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return WithHeader(headerAuthorization, mocks.TestAuthorizationHeader)
|
||||
}
|
||||
|
||||
type mockFailingAuthorizer struct{}
|
||||
|
||||
func (mfa mockFailingAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
return r, fmt.Errorf("ERROR: mockFailingAuthorizer returned expected error")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockInspector struct {
|
||||
wasInvoked bool
|
||||
}
|
||||
|
||||
func (mi *mockInspector) WithInspection() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
mi.wasInvoked = true
|
||||
return p.Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (mi *mockInspector) ByInspecting() RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
mi.wasInvoked = true
|
||||
return r.Respond(resp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func withMessage(output *string, msg string) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
resp, err := s.Do(r)
|
||||
if err == nil {
|
||||
*output += msg
|
||||
}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func withErrorRespondDecorator(e *error) RespondDecorator {
|
||||
return func(r Responder) Responder {
|
||||
return ResponderFunc(func(resp *http.Response) error {
|
||||
err := r.Respond(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = fmt.Errorf("autorest: Faux Respond Error")
|
||||
return *e
|
||||
})
|
||||
}
|
||||
}
|
||||
56
vendor/github.com/Azure/go-autorest/autorest/utils/auth.go
generated
vendored
Normal file
56
vendor/github.com/Azure/go-autorest/autorest/utils/auth.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package utils
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
)
|
||||
|
||||
// GetAuthorizer gets an Azure Service Principal authorizer.
|
||||
// This func assumes "AZURE_TENANT_ID", "AZURE_CLIENT_ID",
|
||||
// "AZURE_CLIENT_SECRET" are set as environment variables.
|
||||
func GetAuthorizer(env azure.Environment) (*autorest.BearerAuthorizer, error) {
|
||||
tenantID := GetEnvVarOrExit("AZURE_TENANT_ID")
|
||||
|
||||
oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientID := GetEnvVarOrExit("AZURE_CLIENT_ID")
|
||||
clientSecret := GetEnvVarOrExit("AZURE_CLIENT_SECRET")
|
||||
|
||||
spToken, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ResourceManagerEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return autorest.NewBearerAuthorizer(spToken), nil
|
||||
}
|
||||
|
||||
// GetEnvVarOrExit returns the value of specified environment variable or terminates if it's not defined.
|
||||
func GetEnvVarOrExit(varName string) string {
|
||||
value := os.Getenv(varName)
|
||||
if value == "" {
|
||||
fmt.Printf("Missing environment variable %s\n", varName)
|
||||
os.Exit(1)
|
||||
}
|
||||
return value
|
||||
}
|
||||
32
vendor/github.com/Azure/go-autorest/autorest/utils/commit.go
generated
vendored
Normal file
32
vendor/github.com/Azure/go-autorest/autorest/utils/commit.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package utils
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// GetCommit returns git HEAD (short)
|
||||
func GetCommit() string {
|
||||
cmd := exec.Command("git", "rev-parse", "HEAD")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(out.Bytes()[:7])
|
||||
}
|
||||
395
vendor/github.com/Azure/go-autorest/autorest/validation/validation.go
generated
vendored
Normal file
395
vendor/github.com/Azure/go-autorest/autorest/validation/validation.go
generated
vendored
Normal file
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
Package validation provides methods for validating parameter value using reflection.
|
||||
*/
|
||||
package validation
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Constraint stores constraint name, target field name
|
||||
// Rule and chain validations.
|
||||
type Constraint struct {
|
||||
|
||||
// Target field name for validation.
|
||||
Target string
|
||||
|
||||
// Constraint name e.g. minLength, MaxLength, Pattern, etc.
|
||||
Name string
|
||||
|
||||
// Rule for constraint e.g. greater than 10, less than 5 etc.
|
||||
Rule interface{}
|
||||
|
||||
// Chain Validations for struct type
|
||||
Chain []Constraint
|
||||
}
|
||||
|
||||
// Validation stores parameter-wise validation.
|
||||
type Validation struct {
|
||||
TargetValue interface{}
|
||||
Constraints []Constraint
|
||||
}
|
||||
|
||||
// Constraint list
|
||||
const (
|
||||
Empty = "Empty"
|
||||
Null = "Null"
|
||||
ReadOnly = "ReadOnly"
|
||||
Pattern = "Pattern"
|
||||
MaxLength = "MaxLength"
|
||||
MinLength = "MinLength"
|
||||
MaxItems = "MaxItems"
|
||||
MinItems = "MinItems"
|
||||
MultipleOf = "MultipleOf"
|
||||
UniqueItems = "UniqueItems"
|
||||
InclusiveMaximum = "InclusiveMaximum"
|
||||
ExclusiveMaximum = "ExclusiveMaximum"
|
||||
ExclusiveMinimum = "ExclusiveMinimum"
|
||||
InclusiveMinimum = "InclusiveMinimum"
|
||||
)
|
||||
|
||||
// Validate method validates constraints on parameter
|
||||
// passed in validation array.
|
||||
func Validate(m []Validation) error {
|
||||
for _, item := range m {
|
||||
v := reflect.ValueOf(item.TargetValue)
|
||||
for _, constraint := range item.Constraints {
|
||||
var err error
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
err = validatePtr(v, constraint)
|
||||
case reflect.String:
|
||||
err = validateString(v, constraint)
|
||||
case reflect.Struct:
|
||||
err = validateStruct(v, constraint)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
err = validateInt(v, constraint)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
err = validateFloat(v, constraint)
|
||||
case reflect.Array, reflect.Slice, reflect.Map:
|
||||
err = validateArrayMap(v, constraint)
|
||||
default:
|
||||
err = createError(v, constraint, fmt.Sprintf("unknown type %v", v.Kind()))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateStruct(x reflect.Value, v Constraint, name ...string) error {
|
||||
//Get field name from target name which is in format a.b.c
|
||||
s := strings.Split(v.Target, ".")
|
||||
f := x.FieldByName(s[len(s)-1])
|
||||
if isZero(f) {
|
||||
return createError(x, v, fmt.Sprintf("field %q doesn't exist", v.Target))
|
||||
}
|
||||
|
||||
return Validate([]Validation{
|
||||
{
|
||||
TargetValue: getInterfaceValue(f),
|
||||
Constraints: []Constraint{v},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func validatePtr(x reflect.Value, v Constraint) error {
|
||||
if v.Name == ReadOnly {
|
||||
if !x.IsNil() {
|
||||
return createError(x.Elem(), v, "readonly parameter; must send as nil or empty in request")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if x.IsNil() {
|
||||
return checkNil(x, v)
|
||||
}
|
||||
if v.Chain != nil {
|
||||
return Validate([]Validation{
|
||||
{
|
||||
TargetValue: getInterfaceValue(x.Elem()),
|
||||
Constraints: v.Chain,
|
||||
},
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateInt(x reflect.Value, v Constraint) error {
|
||||
i := x.Int()
|
||||
r, ok := v.Rule.(int)
|
||||
if !ok {
|
||||
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
|
||||
}
|
||||
switch v.Name {
|
||||
case MultipleOf:
|
||||
if i%int64(r) != 0 {
|
||||
return createError(x, v, fmt.Sprintf("value must be a multiple of %v", r))
|
||||
}
|
||||
case ExclusiveMinimum:
|
||||
if i <= int64(r) {
|
||||
return createError(x, v, fmt.Sprintf("value must be greater than %v", r))
|
||||
}
|
||||
case ExclusiveMaximum:
|
||||
if i >= int64(r) {
|
||||
return createError(x, v, fmt.Sprintf("value must be less than %v", r))
|
||||
}
|
||||
case InclusiveMinimum:
|
||||
if i < int64(r) {
|
||||
return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r))
|
||||
}
|
||||
case InclusiveMaximum:
|
||||
if i > int64(r) {
|
||||
return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r))
|
||||
}
|
||||
default:
|
||||
return createError(x, v, fmt.Sprintf("constraint %v is not applicable for type integer", v.Name))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateFloat(x reflect.Value, v Constraint) error {
|
||||
f := x.Float()
|
||||
r, ok := v.Rule.(float64)
|
||||
if !ok {
|
||||
return createError(x, v, fmt.Sprintf("rule must be float value for %v constraint; got: %v", v.Name, v.Rule))
|
||||
}
|
||||
switch v.Name {
|
||||
case ExclusiveMinimum:
|
||||
if f <= r {
|
||||
return createError(x, v, fmt.Sprintf("value must be greater than %v", r))
|
||||
}
|
||||
case ExclusiveMaximum:
|
||||
if f >= r {
|
||||
return createError(x, v, fmt.Sprintf("value must be less than %v", r))
|
||||
}
|
||||
case InclusiveMinimum:
|
||||
if f < r {
|
||||
return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r))
|
||||
}
|
||||
case InclusiveMaximum:
|
||||
if f > r {
|
||||
return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r))
|
||||
}
|
||||
default:
|
||||
return createError(x, v, fmt.Sprintf("constraint %s is not applicable for type float", v.Name))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateString(x reflect.Value, v Constraint) error {
|
||||
s := x.String()
|
||||
switch v.Name {
|
||||
case Empty:
|
||||
if len(s) == 0 {
|
||||
return checkEmpty(x, v)
|
||||
}
|
||||
case Pattern:
|
||||
reg, err := regexp.Compile(v.Rule.(string))
|
||||
if err != nil {
|
||||
return createError(x, v, err.Error())
|
||||
}
|
||||
if !reg.MatchString(s) {
|
||||
return createError(x, v, fmt.Sprintf("value doesn't match pattern %v", v.Rule))
|
||||
}
|
||||
case MaxLength:
|
||||
if _, ok := v.Rule.(int); !ok {
|
||||
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
|
||||
}
|
||||
if len(s) > v.Rule.(int) {
|
||||
return createError(x, v, fmt.Sprintf("value length must be less than or equal to %v", v.Rule))
|
||||
}
|
||||
case MinLength:
|
||||
if _, ok := v.Rule.(int); !ok {
|
||||
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
|
||||
}
|
||||
if len(s) < v.Rule.(int) {
|
||||
return createError(x, v, fmt.Sprintf("value length must be greater than or equal to %v", v.Rule))
|
||||
}
|
||||
case ReadOnly:
|
||||
if len(s) > 0 {
|
||||
return createError(reflect.ValueOf(s), v, "readonly parameter; must send as nil or empty in request")
|
||||
}
|
||||
default:
|
||||
return createError(x, v, fmt.Sprintf("constraint %s is not applicable to string type", v.Name))
|
||||
}
|
||||
|
||||
if v.Chain != nil {
|
||||
return Validate([]Validation{
|
||||
{
|
||||
TargetValue: getInterfaceValue(x),
|
||||
Constraints: v.Chain,
|
||||
},
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateArrayMap(x reflect.Value, v Constraint) error {
|
||||
switch v.Name {
|
||||
case Null:
|
||||
if x.IsNil() {
|
||||
return checkNil(x, v)
|
||||
}
|
||||
case Empty:
|
||||
if x.IsNil() || x.Len() == 0 {
|
||||
return checkEmpty(x, v)
|
||||
}
|
||||
case MaxItems:
|
||||
if _, ok := v.Rule.(int); !ok {
|
||||
return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule))
|
||||
}
|
||||
if x.Len() > v.Rule.(int) {
|
||||
return createError(x, v, fmt.Sprintf("maximum item limit is %v; got: %v", v.Rule, x.Len()))
|
||||
}
|
||||
case MinItems:
|
||||
if _, ok := v.Rule.(int); !ok {
|
||||
return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule))
|
||||
}
|
||||
if x.Len() < v.Rule.(int) {
|
||||
return createError(x, v, fmt.Sprintf("minimum item limit is %v; got: %v", v.Rule, x.Len()))
|
||||
}
|
||||
case UniqueItems:
|
||||
if x.Kind() == reflect.Array || x.Kind() == reflect.Slice {
|
||||
if !checkForUniqueInArray(x) {
|
||||
return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x))
|
||||
}
|
||||
} else if x.Kind() == reflect.Map {
|
||||
if !checkForUniqueInMap(x) {
|
||||
return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x))
|
||||
}
|
||||
} else {
|
||||
return createError(x, v, fmt.Sprintf("type must be array, slice or map for constraint %v; got: %v", v.Name, x.Kind()))
|
||||
}
|
||||
case ReadOnly:
|
||||
if x.Len() != 0 {
|
||||
return createError(x, v, "readonly parameter; must send as nil or empty in request")
|
||||
}
|
||||
case Pattern:
|
||||
reg, err := regexp.Compile(v.Rule.(string))
|
||||
if err != nil {
|
||||
return createError(x, v, err.Error())
|
||||
}
|
||||
keys := x.MapKeys()
|
||||
for _, k := range keys {
|
||||
if !reg.MatchString(k.String()) {
|
||||
return createError(k, v, fmt.Sprintf("map key doesn't match pattern %v", v.Rule))
|
||||
}
|
||||
}
|
||||
default:
|
||||
return createError(x, v, fmt.Sprintf("constraint %v is not applicable to array, slice and map type", v.Name))
|
||||
}
|
||||
|
||||
if v.Chain != nil {
|
||||
return Validate([]Validation{
|
||||
{
|
||||
TargetValue: getInterfaceValue(x),
|
||||
Constraints: v.Chain,
|
||||
},
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkNil(x reflect.Value, v Constraint) error {
|
||||
if _, ok := v.Rule.(bool); !ok {
|
||||
return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule))
|
||||
}
|
||||
if v.Rule.(bool) {
|
||||
return createError(x, v, "value can not be null; required parameter")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkEmpty(x reflect.Value, v Constraint) error {
|
||||
if _, ok := v.Rule.(bool); !ok {
|
||||
return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule))
|
||||
}
|
||||
|
||||
if v.Rule.(bool) {
|
||||
return createError(x, v, "value can not be null or empty; required parameter")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkForUniqueInArray(x reflect.Value) bool {
|
||||
if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
arrOfInterface := make([]interface{}, x.Len())
|
||||
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
arrOfInterface[i] = x.Index(i).Interface()
|
||||
}
|
||||
|
||||
m := make(map[interface{}]bool)
|
||||
for _, val := range arrOfInterface {
|
||||
if m[val] {
|
||||
return false
|
||||
}
|
||||
m[val] = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkForUniqueInMap(x reflect.Value) bool {
|
||||
if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
mapOfInterface := make(map[interface{}]interface{}, x.Len())
|
||||
|
||||
keys := x.MapKeys()
|
||||
for _, k := range keys {
|
||||
mapOfInterface[k.Interface()] = x.MapIndex(k).Interface()
|
||||
}
|
||||
|
||||
m := make(map[interface{}]bool)
|
||||
for _, val := range mapOfInterface {
|
||||
if m[val] {
|
||||
return false
|
||||
}
|
||||
m[val] = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getInterfaceValue(x reflect.Value) interface{} {
|
||||
if x.Kind() == reflect.Invalid {
|
||||
return nil
|
||||
}
|
||||
return x.Interface()
|
||||
}
|
||||
|
||||
func isZero(x interface{}) bool {
|
||||
return x == reflect.Zero(reflect.TypeOf(x)).Interface()
|
||||
}
|
||||
|
||||
func createError(x reflect.Value, v Constraint, err string) error {
|
||||
return fmt.Errorf("autorest/validation: validation failed: parameter=%s constraint=%s value=%#v details: %s",
|
||||
v.Target, v.Name, getInterfaceValue(x), err)
|
||||
}
|
||||
|
||||
// NewErrorWithValidationError appends package type and method name in
|
||||
// validation error.
|
||||
func NewErrorWithValidationError(err error, packageType, method string) error {
|
||||
return fmt.Errorf("%s#%s: Invalid input: %v", packageType, method, err)
|
||||
}
|
||||
2433
vendor/github.com/Azure/go-autorest/autorest/validation/validation_test.go
generated
vendored
Normal file
2433
vendor/github.com/Azure/go-autorest/autorest/validation/validation_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
49
vendor/github.com/Azure/go-autorest/autorest/version.go
generated
vendored
Normal file
49
vendor/github.com/Azure/go-autorest/autorest/version.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
major = 8
|
||||
minor = 0
|
||||
patch = 0
|
||||
tag = ""
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
var version string
|
||||
|
||||
// Version returns the semantic version (see http://semver.org).
|
||||
func Version() string {
|
||||
once.Do(func() {
|
||||
semver := fmt.Sprintf("%d.%d.%d", major, minor, patch)
|
||||
verBuilder := bytes.NewBufferString(semver)
|
||||
if tag != "" && tag != "-" {
|
||||
updated := strings.TrimPrefix(tag, "-")
|
||||
_, err := verBuilder.WriteString("-" + updated)
|
||||
if err == nil {
|
||||
verBuilder = bytes.NewBufferString(semver)
|
||||
}
|
||||
}
|
||||
version = verBuilder.String()
|
||||
})
|
||||
return version
|
||||
}
|
||||
44
vendor/github.com/Azure/go-autorest/glide.lock
generated
vendored
Normal file
44
vendor/github.com/Azure/go-autorest/glide.lock
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
hash: 6e0121d946623e7e609280b1b18627e1c8a767fdece54cb97c4447c1167cbc46
|
||||
updated: 2017-08-31T13:58:01.034822883+01:00
|
||||
imports:
|
||||
- name: github.com/dgrijalva/jwt-go
|
||||
version: 2268707a8f0843315e2004ee4f1d021dc08baedf
|
||||
subpackages:
|
||||
- .
|
||||
- name: github.com/dimchansky/utfbom
|
||||
version: 6c6132ff69f0f6c088739067407b5d32c52e1d0f
|
||||
- name: github.com/mitchellh/go-homedir
|
||||
version: b8bc1bf767474819792c23f32d8286a45736f1c6
|
||||
- name: golang.org/x/crypto
|
||||
version: 81e90905daefcd6fd217b62423c0908922eadb30
|
||||
repo: https://github.com/golang/crypto.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- pkcs12
|
||||
- pkcs12/internal/rc2
|
||||
- name: golang.org/x/net
|
||||
version: 66aacef3dd8a676686c7ae3716979581e8b03c47
|
||||
repo: https://github.com/golang/net.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- .
|
||||
- name: golang.org/x/text
|
||||
version: 21e35d45962262c8ee80f6cb048dcf95ad0e9d79
|
||||
repo: https://github.com/golang/text.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- .
|
||||
testImports:
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/stretchr/testify
|
||||
version: 890a5c3458b43e6104ff5da8dfa139d013d77544
|
||||
subpackages:
|
||||
- assert
|
||||
- require
|
||||
22
vendor/github.com/Azure/go-autorest/glide.yaml
generated
vendored
Normal file
22
vendor/github.com/Azure/go-autorest/glide.yaml
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package: github.com/Azure/go-autorest
|
||||
import:
|
||||
- package: github.com/dgrijalva/jwt-go
|
||||
subpackages:
|
||||
- .
|
||||
- package: golang.org/x/crypto
|
||||
repo: https://github.com/golang/crypto.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- pkcs12
|
||||
- package: golang.org/x/net
|
||||
repo: https://github.com/golang/net.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- .
|
||||
- package: golang.org/x/text
|
||||
repo: https://github.com/golang/text.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- .
|
||||
- package: github.com/mitchellh/go-homedir
|
||||
- package: github.com/dimchansky/utfbom
|
||||
BIN
vendor/github.com/Azure/go-autorest/testdata/credsutf16be.json
generated
vendored
Normal file
BIN
vendor/github.com/Azure/go-autorest/testdata/credsutf16be.json
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/github.com/Azure/go-autorest/testdata/credsutf16le.json
generated
vendored
Normal file
BIN
vendor/github.com/Azure/go-autorest/testdata/credsutf16le.json
generated
vendored
Normal file
Binary file not shown.
12
vendor/github.com/Azure/go-autorest/testdata/credsutf8.json
generated
vendored
Normal file
12
vendor/github.com/Azure/go-autorest/testdata/credsutf8.json
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"clientId": "client-id-123",
|
||||
"clientSecret": "client-secret-456",
|
||||
"subscriptionId": "sub-id-789",
|
||||
"tenantId": "tenant-id-123",
|
||||
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
|
||||
"resourceManagerEndpointUrl": "https://management.azure.com/",
|
||||
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
|
||||
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
|
||||
"galleryEndpointUrl": "https://gallery.azure.com/",
|
||||
"managementEndpointUrl": "https://management.core.windows.net/"
|
||||
}
|
||||
Reference in New Issue
Block a user