Update github.com/xenolf/lego to 0.4.1

This commit is contained in:
Tait Clarridge 2017-10-31 05:42:03 -04:00 committed by Traefiker
parent 5042c5bf40
commit e8d63b2a3b
105 changed files with 4299 additions and 2075 deletions

5
vendor/github.com/Azure/azure-sdk-for-go/NOTICE generated vendored Normal file
View file

@ -0,0 +1,5 @@
Microsoft Azure-SDK-for-Go
Copyright 2014-2017 Microsoft
This product includes software developed at
the Microsoft Corporation (https://www.microsoft.com).

View file

@ -17,9 +17,8 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
// Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/Azure/go-autorest/autorest"

View file

@ -14,9 +14,8 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
// Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/Azure/go-autorest/autorest"
@ -42,8 +41,7 @@ const (
Continue HTTPStatusCode = "Continue"
// Created specifies the created state for http status code.
Created HTTPStatusCode = "Created"
// ExpectationFailed specifies the expectation failed state for http status
// code.
// ExpectationFailed specifies the expectation failed state for http status code.
ExpectationFailed HTTPStatusCode = "ExpectationFailed"
// Forbidden specifies the forbidden state for http status code.
Forbidden HTTPStatusCode = "Forbidden"
@ -53,29 +51,23 @@ const (
GatewayTimeout HTTPStatusCode = "GatewayTimeout"
// Gone specifies the gone state for http status code.
Gone HTTPStatusCode = "Gone"
// HTTPVersionNotSupported specifies the http version not supported state
// for http status code.
// HTTPVersionNotSupported specifies the http version not supported state for http status code.
HTTPVersionNotSupported HTTPStatusCode = "HttpVersionNotSupported"
// InternalServerError specifies the internal server error state for http
// status code.
// InternalServerError specifies the internal server error state for http status code.
InternalServerError HTTPStatusCode = "InternalServerError"
// LengthRequired specifies the length required state for http status code.
LengthRequired HTTPStatusCode = "LengthRequired"
// MethodNotAllowed specifies the method not allowed state for http status
// code.
// MethodNotAllowed specifies the method not allowed state for http status code.
MethodNotAllowed HTTPStatusCode = "MethodNotAllowed"
// Moved specifies the moved state for http status code.
Moved HTTPStatusCode = "Moved"
// MovedPermanently specifies the moved permanently state for http status
// code.
// MovedPermanently specifies the moved permanently state for http status code.
MovedPermanently HTTPStatusCode = "MovedPermanently"
// MultipleChoices specifies the multiple choices state for http status
// code.
// MultipleChoices specifies the multiple choices state for http status code.
MultipleChoices HTTPStatusCode = "MultipleChoices"
// NoContent specifies the no content state for http status code.
NoContent HTTPStatusCode = "NoContent"
// NonAuthoritativeInformation specifies the non authoritative information
// state for http status code.
// NonAuthoritativeInformation specifies the non authoritative information state for http status code.
NonAuthoritativeInformation HTTPStatusCode = "NonAuthoritativeInformation"
// NotAcceptable specifies the not acceptable state for http status code.
NotAcceptable HTTPStatusCode = "NotAcceptable"
@ -89,55 +81,43 @@ const (
OK HTTPStatusCode = "OK"
// PartialContent specifies the partial content state for http status code.
PartialContent HTTPStatusCode = "PartialContent"
// PaymentRequired specifies the payment required state for http status
// code.
// PaymentRequired specifies the payment required state for http status code.
PaymentRequired HTTPStatusCode = "PaymentRequired"
// PreconditionFailed specifies the precondition failed state for http
// status code.
// PreconditionFailed specifies the precondition failed state for http status code.
PreconditionFailed HTTPStatusCode = "PreconditionFailed"
// ProxyAuthenticationRequired specifies the proxy authentication required
// state for http status code.
// ProxyAuthenticationRequired specifies the proxy authentication required state for http status code.
ProxyAuthenticationRequired HTTPStatusCode = "ProxyAuthenticationRequired"
// Redirect specifies the redirect state for http status code.
Redirect HTTPStatusCode = "Redirect"
// RedirectKeepVerb specifies the redirect keep verb state for http status
// code.
// RedirectKeepVerb specifies the redirect keep verb state for http status code.
RedirectKeepVerb HTTPStatusCode = "RedirectKeepVerb"
// RedirectMethod specifies the redirect method state for http status code.
RedirectMethod HTTPStatusCode = "RedirectMethod"
// RequestedRangeNotSatisfiable specifies the requested range not
// satisfiable state for http status code.
// RequestedRangeNotSatisfiable specifies the requested range not satisfiable state for http status code.
RequestedRangeNotSatisfiable HTTPStatusCode = "RequestedRangeNotSatisfiable"
// RequestEntityTooLarge specifies the request entity too large state for
// http status code.
// RequestEntityTooLarge specifies the request entity too large state for http status code.
RequestEntityTooLarge HTTPStatusCode = "RequestEntityTooLarge"
// RequestTimeout specifies the request timeout state for http status code.
RequestTimeout HTTPStatusCode = "RequestTimeout"
// RequestURITooLong specifies the request uri too long state for http
// status code.
// RequestURITooLong specifies the request uri too long state for http status code.
RequestURITooLong HTTPStatusCode = "RequestUriTooLong"
// ResetContent specifies the reset content state for http status code.
ResetContent HTTPStatusCode = "ResetContent"
// SeeOther specifies the see other state for http status code.
SeeOther HTTPStatusCode = "SeeOther"
// ServiceUnavailable specifies the service unavailable state for http
// status code.
// ServiceUnavailable specifies the service unavailable state for http status code.
ServiceUnavailable HTTPStatusCode = "ServiceUnavailable"
// SwitchingProtocols specifies the switching protocols state for http
// status code.
// SwitchingProtocols specifies the switching protocols state for http status code.
SwitchingProtocols HTTPStatusCode = "SwitchingProtocols"
// TemporaryRedirect specifies the temporary redirect state for http status
// code.
// TemporaryRedirect specifies the temporary redirect state for http status code.
TemporaryRedirect HTTPStatusCode = "TemporaryRedirect"
// Unauthorized specifies the unauthorized state for http status code.
Unauthorized HTTPStatusCode = "Unauthorized"
// UnsupportedMediaType specifies the unsupported media type state for http
// status code.
// UnsupportedMediaType specifies the unsupported media type state for http status code.
UnsupportedMediaType HTTPStatusCode = "UnsupportedMediaType"
// Unused specifies the unused state for http status code.
Unused HTTPStatusCode = "Unused"
// UpgradeRequired specifies the upgrade required state for http status
// code.
// UpgradeRequired specifies the upgrade required state for http status code.
UpgradeRequired HTTPStatusCode = "UpgradeRequired"
// UseProxy specifies the use proxy state for http status code.
UseProxy HTTPStatusCode = "UseProxy"
@ -223,8 +203,7 @@ type PtrRecord struct {
Ptrdname *string `json:"ptrdname,omitempty"`
}
// RecordSet is describes a DNS record set (a collection of DNS records with
// the same name and type).
// RecordSet is describes a DNS record set (a collection of DNS records with the same name and type).
type RecordSet struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
@ -253,8 +232,7 @@ func (client RecordSetListResult) RecordSetListResultPreparer() (*http.Request,
autorest.WithBaseURL(to.String(client.NextLink)))
}
// RecordSetProperties is represents the properties of the records in the
// record set.
// RecordSetProperties is represents the properties of the records in the record set.
type RecordSetProperties struct {
Metadata *map[string]*string `json:"metadata,omitempty"`
TTL *int64 `json:"TTL,omitempty"`

View file

@ -14,9 +14,8 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
// Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/Azure/go-autorest/autorest"
@ -34,35 +33,32 @@ func NewRecordSetsClient(subscriptionID string) RecordSetsClient {
return NewRecordSetsClientWithBaseURI(DefaultBaseURI, subscriptionID)
}
// NewRecordSetsClientWithBaseURI creates an instance of the RecordSetsClient
// client.
// NewRecordSetsClientWithBaseURI creates an instance of the RecordSetsClient client.
func NewRecordSetsClientWithBaseURI(baseURI string, subscriptionID string) RecordSetsClient {
return RecordSetsClient{NewWithBaseURI(baseURI, subscriptionID)}
}
// CreateOrUpdate creates or updates a record set within a DNS zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). relativeRecordSetName is the name
// of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. Record sets of type SOA can be updated but
// not created (they are created when the DNS zone is created). parameters is
// parameters supplied to the CreateOrUpdate operation. ifMatch is the etag of
// the record set. Omit this value to always overwrite the current record set.
// Specify the last-seen etag value to prevent accidentally overwritting any
// concurrent changes. ifNoneMatch is set to '*' to allow a new record set to
// be created, but to prevent updating an existing record set. Other values
// will be ignored.
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot). relativeRecordSetName is the name of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. Record sets of type SOA can be updated but not created (they are created when the
// DNS zone is created). parameters is parameters supplied to the CreateOrUpdate operation. ifMatch is the etag of the
// record set. Omit this value to always overwrite the current record set. Specify the last-seen etag value to prevent
// accidentally overwritting any concurrent changes. ifNoneMatch is set to '*' to allow a new record set to be created,
// but to prevent updating an existing record set. Other values will be ignored.
func (client RecordSetsClient) CreateOrUpdate(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, parameters RecordSet, ifMatch string, ifNoneMatch string) (result RecordSet, err error) {
req, err := client.CreateOrUpdatePreparer(resourceGroupName, zoneName, relativeRecordSetName, recordType, parameters, ifMatch, ifNoneMatch)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "CreateOrUpdate", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "CreateOrUpdate", nil, "Failure preparing request")
return
}
resp, err := client.CreateOrUpdateSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "CreateOrUpdate", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "CreateOrUpdate", resp, "Failure sending request")
return
}
result, err = client.CreateOrUpdateResponder(resp)
@ -125,27 +121,25 @@ func (client RecordSetsClient) CreateOrUpdateResponder(resp *http.Response) (res
return
}
// Delete deletes a record set from a DNS zone. This operation cannot be
// undone.
// Delete deletes a record set from a DNS zone. This operation cannot be undone.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). relativeRecordSetName is the name
// of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. Record sets of type SOA cannot be deleted
// (they are deleted when the DNS zone is deleted). ifMatch is the etag of the
// record set. Omit this value to always delete the current record set. Specify
// the last-seen etag value to prevent accidentally deleting any concurrent
// changes.
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot). relativeRecordSetName is the name of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. Record sets of type SOA cannot be deleted (they are deleted when the DNS zone is
// deleted). ifMatch is the etag of the record set. Omit this value to always delete the current record set. Specify
// the last-seen etag value to prevent accidentally deleting any concurrent changes.
func (client RecordSetsClient) Delete(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, ifMatch string) (result autorest.Response, err error) {
req, err := client.DeletePreparer(resourceGroupName, zoneName, relativeRecordSetName, recordType, ifMatch)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Delete", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Delete", nil, "Failure preparing request")
return
}
resp, err := client.DeleteSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Delete", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Delete", resp, "Failure sending request")
return
}
result, err = client.DeleteResponder(resp)
@ -203,20 +197,21 @@ func (client RecordSetsClient) DeleteResponder(resp *http.Response) (result auto
// Get gets a record set.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). relativeRecordSetName is the name
// of the record set, relative to the name of the zone. recordType is the type
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot). relativeRecordSetName is the name of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set.
func (client RecordSetsClient) Get(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType) (result RecordSet, err error) {
req, err := client.GetPreparer(resourceGroupName, zoneName, relativeRecordSetName, recordType)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Get", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Get", nil, "Failure preparing request")
return
}
resp, err := client.GetSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Get", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Get", resp, "Failure sending request")
return
}
result, err = client.GetResponder(resp)
@ -271,19 +266,23 @@ func (client RecordSetsClient) GetResponder(resp *http.Response) (result RecordS
// ListByDNSZone lists all record sets in a DNS zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). top is the maximum number of
// record sets to return. If not specified, returns up to 100 record sets.
func (client RecordSetsClient) ListByDNSZone(resourceGroupName string, zoneName string, top *int32) (result RecordSetListResult, err error) {
req, err := client.ListByDNSZonePreparer(resourceGroupName, zoneName, top)
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot). top is the maximum number of record sets to return. If not specified, returns up to 100 record sets.
// recordsetnamesuffix is the suffix label of the record set name that has to be used to filter the record set
// enumerations. If this parameter is specified, Enumeration will return only records that end with
// .<recordSetNameSuffix>
func (client RecordSetsClient) ListByDNSZone(resourceGroupName string, zoneName string, top *int32, recordsetnamesuffix string) (result RecordSetListResult, err error) {
req, err := client.ListByDNSZonePreparer(resourceGroupName, zoneName, top, recordsetnamesuffix)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "ListByDNSZone", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "ListByDNSZone", nil, "Failure preparing request")
return
}
resp, err := client.ListByDNSZoneSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "ListByDNSZone", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "ListByDNSZone", resp, "Failure sending request")
return
}
result, err = client.ListByDNSZoneResponder(resp)
@ -295,7 +294,7 @@ func (client RecordSetsClient) ListByDNSZone(resourceGroupName string, zoneName
}
// ListByDNSZonePreparer prepares the ListByDNSZone request.
func (client RecordSetsClient) ListByDNSZonePreparer(resourceGroupName string, zoneName string, top *int32) (*http.Request, error) {
func (client RecordSetsClient) ListByDNSZonePreparer(resourceGroupName string, zoneName string, top *int32, recordsetnamesuffix string) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
@ -309,6 +308,9 @@ func (client RecordSetsClient) ListByDNSZonePreparer(resourceGroupName string, z
if top != nil {
queryParameters["$top"] = autorest.Encode("query", *top)
}
if len(recordsetnamesuffix) > 0 {
queryParameters["$recordsetnamesuffix"] = autorest.Encode("query", recordsetnamesuffix)
}
preparer := autorest.CreatePreparer(
autorest.AsGet(),
@ -361,22 +363,70 @@ func (client RecordSetsClient) ListByDNSZoneNextResults(lastResults RecordSetLis
return
}
// ListByDNSZoneComplete gets all elements from the list without paging.
func (client RecordSetsClient) ListByDNSZoneComplete(resourceGroupName string, zoneName string, top *int32, recordsetnamesuffix string, cancel <-chan struct{}) (<-chan RecordSet, <-chan error) {
resultChan := make(chan RecordSet)
errChan := make(chan error, 1)
go func() {
defer func() {
close(resultChan)
close(errChan)
}()
list, err := client.ListByDNSZone(resourceGroupName, zoneName, top, recordsetnamesuffix)
if err != nil {
errChan <- err
return
}
if list.Value != nil {
for _, item := range *list.Value {
select {
case <-cancel:
return
case resultChan <- item:
// Intentionally left blank
}
}
}
for list.NextLink != nil {
list, err = client.ListByDNSZoneNextResults(list)
if err != nil {
errChan <- err
return
}
if list.Value != nil {
for _, item := range *list.Value {
select {
case <-cancel:
return
case resultChan <- item:
// Intentionally left blank
}
}
}
}
}()
return resultChan, errChan
}
// ListByType lists the record sets of a specified type in a DNS zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). recordType is the type of record
// sets to enumerate. top is the maximum number of record sets to return. If
// not specified, returns up to 100 record sets.
func (client RecordSetsClient) ListByType(resourceGroupName string, zoneName string, recordType RecordType, top *int32) (result RecordSetListResult, err error) {
req, err := client.ListByTypePreparer(resourceGroupName, zoneName, recordType, top)
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot). recordType is the type of record sets to enumerate. top is the maximum number of record sets to return. If not
// specified, returns up to 100 record sets. recordsetnamesuffix is the suffix label of the record set name that has to
// be used to filter the record set enumerations. If this parameter is specified, Enumeration will return only records
// that end with .<recordSetNameSuffix>
func (client RecordSetsClient) ListByType(resourceGroupName string, zoneName string, recordType RecordType, top *int32, recordsetnamesuffix string) (result RecordSetListResult, err error) {
req, err := client.ListByTypePreparer(resourceGroupName, zoneName, recordType, top, recordsetnamesuffix)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "ListByType", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "ListByType", nil, "Failure preparing request")
return
}
resp, err := client.ListByTypeSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "ListByType", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "ListByType", resp, "Failure sending request")
return
}
result, err = client.ListByTypeResponder(resp)
@ -388,7 +438,7 @@ func (client RecordSetsClient) ListByType(resourceGroupName string, zoneName str
}
// ListByTypePreparer prepares the ListByType request.
func (client RecordSetsClient) ListByTypePreparer(resourceGroupName string, zoneName string, recordType RecordType, top *int32) (*http.Request, error) {
func (client RecordSetsClient) ListByTypePreparer(resourceGroupName string, zoneName string, recordType RecordType, top *int32, recordsetnamesuffix string) (*http.Request, error) {
pathParameters := map[string]interface{}{
"recordType": autorest.Encode("path", recordType),
"resourceGroupName": autorest.Encode("path", resourceGroupName),
@ -403,6 +453,9 @@ func (client RecordSetsClient) ListByTypePreparer(resourceGroupName string, zone
if top != nil {
queryParameters["$top"] = autorest.Encode("query", *top)
}
if len(recordsetnamesuffix) > 0 {
queryParameters["$recordsetnamesuffix"] = autorest.Encode("query", recordsetnamesuffix)
}
preparer := autorest.CreatePreparer(
autorest.AsGet(),
@ -455,25 +508,70 @@ func (client RecordSetsClient) ListByTypeNextResults(lastResults RecordSetListRe
return
}
// ListByTypeComplete gets all elements from the list without paging.
func (client RecordSetsClient) ListByTypeComplete(resourceGroupName string, zoneName string, recordType RecordType, top *int32, recordsetnamesuffix string, cancel <-chan struct{}) (<-chan RecordSet, <-chan error) {
resultChan := make(chan RecordSet)
errChan := make(chan error, 1)
go func() {
defer func() {
close(resultChan)
close(errChan)
}()
list, err := client.ListByType(resourceGroupName, zoneName, recordType, top, recordsetnamesuffix)
if err != nil {
errChan <- err
return
}
if list.Value != nil {
for _, item := range *list.Value {
select {
case <-cancel:
return
case resultChan <- item:
// Intentionally left blank
}
}
}
for list.NextLink != nil {
list, err = client.ListByTypeNextResults(list)
if err != nil {
errChan <- err
return
}
if list.Value != nil {
for _, item := range *list.Value {
select {
case <-cancel:
return
case resultChan <- item:
// Intentionally left blank
}
}
}
}
}()
return resultChan, errChan
}
// Update updates a record set within a DNS zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). relativeRecordSetName is the name
// of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. parameters is parameters supplied to the
// Update operation. ifMatch is the etag of the record set. Omit this value to
// always overwrite the current record set. Specify the last-seen etag value to
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot). relativeRecordSetName is the name of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. parameters is parameters supplied to the Update operation. ifMatch is the etag of
// the record set. Omit this value to always overwrite the current record set. Specify the last-seen etag value to
// prevent accidentally overwritting concurrent changes.
func (client RecordSetsClient) Update(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, parameters RecordSet, ifMatch string) (result RecordSet, err error) {
req, err := client.UpdatePreparer(resourceGroupName, zoneName, relativeRecordSetName, recordType, parameters, ifMatch)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Update", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Update", nil, "Failure preparing request")
return
}
resp, err := client.UpdateSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Update", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.RecordSetsClient", "Update", resp, "Failure sending request")
return
}
result, err = client.UpdateResponder(resp)

View file

@ -14,16 +14,15 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
// Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// UserAgent returns the UserAgent string to use when sending http.Requests.
func UserAgent() string {
return "Azure-SDK-For-Go/v9.0.0-beta arm-dns/2016-04-01"
return "Azure-SDK-For-Go/v11.0.0-beta arm-dns/2016-04-01"
}
// Version returns the semantic version (see http://semver.org) of the client.
func Version() string {
return "v9.0.0-beta"
return "v11.0.0-beta"
}

View file

@ -14,9 +14,8 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
// Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/Azure/go-autorest/autorest"
@ -39,26 +38,25 @@ func NewZonesClientWithBaseURI(baseURI string, subscriptionID string) ZonesClien
return ZonesClient{NewWithBaseURI(baseURI, subscriptionID)}
}
// CreateOrUpdate creates or updates a DNS zone. Does not modify DNS records
// within the zone.
// CreateOrUpdate creates or updates a DNS zone. Does not modify DNS records within the zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). parameters is parameters supplied
// to the CreateOrUpdate operation. ifMatch is the etag of the DNS zone. Omit
// this value to always overwrite the current zone. Specify the last-seen etag
// value to prevent accidentally overwritting any concurrent changes.
// ifNoneMatch is set to '*' to allow a new DNS zone to be created, but to
// prevent updating an existing zone. Other values will be ignored.
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot). parameters is parameters supplied to the CreateOrUpdate operation. ifMatch is the etag of the DNS zone. Omit
// this value to always overwrite the current zone. Specify the last-seen etag value to prevent accidentally
// overwritting any concurrent changes. ifNoneMatch is set to '*' to allow a new DNS zone to be created, but to prevent
// updating an existing zone. Other values will be ignored.
func (client ZonesClient) CreateOrUpdate(resourceGroupName string, zoneName string, parameters Zone, ifMatch string, ifNoneMatch string) (result Zone, err error) {
req, err := client.CreateOrUpdatePreparer(resourceGroupName, zoneName, parameters, ifMatch, ifNoneMatch)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "CreateOrUpdate", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "CreateOrUpdate", nil, "Failure preparing request")
return
}
resp, err := client.CreateOrUpdateSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "CreateOrUpdate", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "CreateOrUpdate", resp, "Failure sending request")
return
}
result, err = client.CreateOrUpdateResponder(resp)
@ -119,35 +117,46 @@ func (client ZonesClient) CreateOrUpdateResponder(resp *http.Response) (result Z
return
}
// Delete deletes a DNS zone. WARNING: All DNS records in the zone will also be
// deleted. This operation cannot be undone. This method may poll for
// completion. Polling can be canceled by passing the cancel channel argument.
// The channel will be used to cancel polling and any outstanding HTTP
// requests.
// Delete deletes a DNS zone. WARNING: All DNS records in the zone will also be deleted. This operation cannot be
// undone. This method may poll for completion. Polling can be canceled by passing the cancel channel argument. The
// channel will be used to cancel polling and any outstanding HTTP requests.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). ifMatch is the etag of the DNS
// zone. Omit this value to always delete the current zone. Specify the
// last-seen etag value to prevent accidentally deleting any concurrent
// changes.
func (client ZonesClient) Delete(resourceGroupName string, zoneName string, ifMatch string, cancel <-chan struct{}) (result autorest.Response, err error) {
req, err := client.DeletePreparer(resourceGroupName, zoneName, ifMatch, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "Delete", nil, "Failure preparing request")
}
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot). ifMatch is the etag of the DNS zone. Omit this value to always delete the current zone. Specify the last-seen
// etag value to prevent accidentally deleting any concurrent changes.
func (client ZonesClient) Delete(resourceGroupName string, zoneName string, ifMatch string, cancel <-chan struct{}) (<-chan ZoneDeleteResult, <-chan error) {
resultChan := make(chan ZoneDeleteResult, 1)
errChan := make(chan error, 1)
go func() {
var err error
var result ZoneDeleteResult
defer func() {
if err != nil {
errChan <- err
}
resultChan <- result
close(resultChan)
close(errChan)
}()
req, err := client.DeletePreparer(resourceGroupName, zoneName, ifMatch, cancel)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Delete", nil, "Failure preparing request")
return
}
resp, err := client.DeleteSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "Delete", resp, "Failure sending request")
}
resp, err := client.DeleteSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Delete", resp, "Failure sending request")
return
}
result, err = client.DeleteResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Delete", resp, "Failure responding to request")
}
return
result, err = client.DeleteResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Delete", resp, "Failure responding to request")
}
}()
return resultChan, errChan
}
// DeletePreparer prepares the Delete request.
@ -185,31 +194,33 @@ func (client ZonesClient) DeleteSender(req *http.Request) (*http.Response, error
// DeleteResponder handles the response to the Delete request. The method always
// closes the http.Response Body.
func (client ZonesClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) {
func (client ZonesClient) DeleteResponder(resp *http.Response) (result ZoneDeleteResult, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusNoContent, http.StatusAccepted, http.StatusOK),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = resp
result.Response = autorest.Response{Response: resp}
return
}
// Get gets a DNS zone. Retrieves the zone properties, but not the record sets
// within the zone.
// Get gets a DNS zone. Retrieves the zone properties, but not the record sets within the zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot).
// resourceGroupName is the name of the resource group. zoneName is the name of the DNS zone (without a terminating
// dot).
func (client ZonesClient) Get(resourceGroupName string, zoneName string) (result Zone, err error) {
req, err := client.GetPreparer(resourceGroupName, zoneName)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "Get", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Get", nil, "Failure preparing request")
return
}
resp, err := client.GetSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "Get", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "Get", resp, "Failure sending request")
return
}
result, err = client.GetResponder(resp)
@ -262,18 +273,19 @@ func (client ZonesClient) GetResponder(resp *http.Response) (result Zone, err er
// List lists the DNS zones in all resource groups in a subscription.
//
// top is the maximum number of DNS zones to return. If not specified, returns
// up to 100 zones.
// top is the maximum number of DNS zones to return. If not specified, returns up to 100 zones.
func (client ZonesClient) List(top *int32) (result ZoneListResult, err error) {
req, err := client.ListPreparer(top)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "List", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "List", nil, "Failure preparing request")
return
}
resp, err := client.ListSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "List", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "List", resp, "Failure sending request")
return
}
result, err = client.ListResponder(resp)
@ -349,21 +361,67 @@ func (client ZonesClient) ListNextResults(lastResults ZoneListResult) (result Zo
return
}
// ListComplete gets all elements from the list without paging.
func (client ZonesClient) ListComplete(top *int32, cancel <-chan struct{}) (<-chan Zone, <-chan error) {
resultChan := make(chan Zone)
errChan := make(chan error, 1)
go func() {
defer func() {
close(resultChan)
close(errChan)
}()
list, err := client.List(top)
if err != nil {
errChan <- err
return
}
if list.Value != nil {
for _, item := range *list.Value {
select {
case <-cancel:
return
case resultChan <- item:
// Intentionally left blank
}
}
}
for list.NextLink != nil {
list, err = client.ListNextResults(list)
if err != nil {
errChan <- err
return
}
if list.Value != nil {
for _, item := range *list.Value {
select {
case <-cancel:
return
case resultChan <- item:
// Intentionally left blank
}
}
}
}
}()
return resultChan, errChan
}
// ListByResourceGroup lists the DNS zones within a resource group.
//
// resourceGroupName is the name of the resource group. top is the maximum
// number of record sets to return. If not specified, returns up to 100 record
// sets.
// resourceGroupName is the name of the resource group. top is the maximum number of record sets to return. If not
// specified, returns up to 100 record sets.
func (client ZonesClient) ListByResourceGroup(resourceGroupName string, top *int32) (result ZoneListResult, err error) {
req, err := client.ListByResourceGroupPreparer(resourceGroupName, top)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "ListByResourceGroup", nil, "Failure preparing request")
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "ListByResourceGroup", nil, "Failure preparing request")
return
}
resp, err := client.ListByResourceGroupSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "ListByResourceGroup", resp, "Failure sending request")
err = autorest.NewErrorWithError(err, "dns.ZonesClient", "ListByResourceGroup", resp, "Failure sending request")
return
}
result, err = client.ListByResourceGroupResponder(resp)
@ -439,3 +497,48 @@ func (client ZonesClient) ListByResourceGroupNextResults(lastResults ZoneListRes
return
}
// ListByResourceGroupComplete gets all elements from the list without paging.
func (client ZonesClient) ListByResourceGroupComplete(resourceGroupName string, top *int32, cancel <-chan struct{}) (<-chan Zone, <-chan error) {
resultChan := make(chan Zone)
errChan := make(chan error, 1)
go func() {
defer func() {
close(resultChan)
close(errChan)
}()
list, err := client.ListByResourceGroup(resourceGroupName, top)
if err != nil {
errChan <- err
return
}
if list.Value != nil {
for _, item := range *list.Value {
select {
case <-cancel:
return
case resultChan <- item:
// Intentionally left blank
}
}
}
for list.NextLink != nil {
list, err = client.ListByResourceGroupNextResults(list)
if err != nil {
errChan <- err
return
}
if list.Value != nil {
for _, item := range *list.Value {
select {
case <-cancel:
return
case resultChan <- item:
// Intentionally left blank
}
}
}
}
}()
return resultChan, errChan
}

View file

@ -5,7 +5,7 @@ type csiEntryState struct {
}
func (csiState csiEntryState) Handle(b byte) (s state, e error) {
logger.Infof("CsiEntry::Handle %#x", b)
csiState.parser.logf("CsiEntry::Handle %#x", b)
nextState, err := csiState.baseState.Handle(b)
if nextState != nil || err != nil {
@ -25,7 +25,7 @@ func (csiState csiEntryState) Handle(b byte) (s state, e error) {
}
func (csiState csiEntryState) Transition(s state) error {
logger.Infof("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name())
csiState.parser.logf("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name())
csiState.baseState.Transition(s)
switch s {

View file

@ -5,7 +5,7 @@ type csiParamState struct {
}
func (csiState csiParamState) Handle(b byte) (s state, e error) {
logger.Infof("CsiParam::Handle %#x", b)
csiState.parser.logf("CsiParam::Handle %#x", b)
nextState, err := csiState.baseState.Handle(b)
if nextState != nil || err != nil {
@ -26,7 +26,7 @@ func (csiState csiParamState) Handle(b byte) (s state, e error) {
}
func (csiState csiParamState) Transition(s state) error {
logger.Infof("CsiParam::Transition %s --> %s", csiState.Name(), s.Name())
csiState.parser.logf("CsiParam::Transition %s --> %s", csiState.Name(), s.Name())
csiState.baseState.Transition(s)
switch s {

View file

@ -5,7 +5,7 @@ type escapeIntermediateState struct {
}
func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
logger.Infof("escapeIntermediateState::Handle %#x", b)
escState.parser.logf("escapeIntermediateState::Handle %#x", b)
nextState, err := escState.baseState.Handle(b)
if nextState != nil || err != nil {
return nextState, err
@ -24,7 +24,7 @@ func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
}
func (escState escapeIntermediateState) Transition(s state) error {
logger.Infof("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name())
escState.parser.logf("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name())
escState.baseState.Transition(s)
switch s {

View file

@ -5,7 +5,7 @@ type escapeState struct {
}
func (escState escapeState) Handle(b byte) (s state, e error) {
logger.Infof("escapeState::Handle %#x", b)
escState.parser.logf("escapeState::Handle %#x", b)
nextState, err := escState.baseState.Handle(b)
if nextState != nil || err != nil {
return nextState, err
@ -28,7 +28,7 @@ func (escState escapeState) Handle(b byte) (s state, e error) {
}
func (escState escapeState) Transition(s state) error {
logger.Infof("Escape::Transition %s --> %s", escState.Name(), s.Name())
escState.parser.logf("Escape::Transition %s --> %s", escState.Name(), s.Name())
escState.baseState.Transition(s)
switch s {

View file

@ -5,7 +5,7 @@ type oscStringState struct {
}
func (oscState oscStringState) Handle(b byte) (s state, e error) {
logger.Infof("OscString::Handle %#x", b)
oscState.parser.logf("OscString::Handle %#x", b)
nextState, err := oscState.baseState.Handle(b)
if nextState != nil || err != nil {
return nextState, err

View file

@ -2,14 +2,10 @@ package ansiterm
import (
"errors"
"io/ioutil"
"log"
"os"
"github.com/sirupsen/logrus"
)
var logger *logrus.Logger
type AnsiParser struct {
currState state
eventHandler AnsiEventHandler
@ -23,50 +19,69 @@ type AnsiParser struct {
ground state
oscString state
stateMap []state
logf func(string, ...interface{})
}
func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser {
logFile := ioutil.Discard
type Option func(*AnsiParser)
if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
logFile, _ = os.Create("ansiParser.log")
func WithLogf(f func(string, ...interface{})) Option {
return func(ap *AnsiParser) {
ap.logf = f
}
}
logger = &logrus.Logger{
Out: logFile,
Formatter: new(logrus.TextFormatter),
Level: logrus.InfoLevel,
}
parser := &AnsiParser{
func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser {
ap := &AnsiParser{
eventHandler: evtHandler,
context: &ansiContext{},
}
parser.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: parser}}
parser.csiParam = csiParamState{baseState{name: "CsiParam", parser: parser}}
parser.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: parser}}
parser.escape = escapeState{baseState{name: "Escape", parser: parser}}
parser.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: parser}}
parser.error = errorState{baseState{name: "Error", parser: parser}}
parser.ground = groundState{baseState{name: "Ground", parser: parser}}
parser.oscString = oscStringState{baseState{name: "OscString", parser: parser}}
parser.stateMap = []state{
parser.csiEntry,
parser.csiParam,
parser.dcsEntry,
parser.escape,
parser.escapeIntermediate,
parser.error,
parser.ground,
parser.oscString,
for _, o := range opts {
o(ap)
}
parser.currState = getState(initialState, parser.stateMap)
if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
logFile, _ := os.Create("ansiParser.log")
logger := log.New(logFile, "", log.LstdFlags)
if ap.logf != nil {
l := ap.logf
ap.logf = func(s string, v ...interface{}) {
l(s, v...)
logger.Printf(s, v...)
}
} else {
ap.logf = logger.Printf
}
}
logger.Infof("CreateParser: parser %p", parser)
return parser
if ap.logf == nil {
ap.logf = func(string, ...interface{}) {}
}
ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}}
ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}}
ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}}
ap.escape = escapeState{baseState{name: "Escape", parser: ap}}
ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}}
ap.error = errorState{baseState{name: "Error", parser: ap}}
ap.ground = groundState{baseState{name: "Ground", parser: ap}}
ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}}
ap.stateMap = []state{
ap.csiEntry,
ap.csiParam,
ap.dcsEntry,
ap.escape,
ap.escapeIntermediate,
ap.error,
ap.ground,
ap.oscString,
}
ap.currState = getState(initialState, ap.stateMap)
ap.logf("CreateParser: parser %p", ap)
return ap
}
func getState(name string, states []state) state {
@ -97,7 +112,7 @@ func (ap *AnsiParser) handle(b byte) error {
}
if newState == nil {
logger.Warning("newState is nil")
ap.logf("WARNING: newState is nil")
return errors.New("New state of 'nil' is invalid.")
}
@ -111,23 +126,23 @@ func (ap *AnsiParser) handle(b byte) error {
}
func (ap *AnsiParser) changeState(newState state) error {
logger.Infof("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
// Exit old state
if err := ap.currState.Exit(); err != nil {
logger.Infof("Exit state '%s' failed with : '%v'", ap.currState.Name(), err)
ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err)
return err
}
// Perform transition action
if err := ap.currState.Transition(newState); err != nil {
logger.Infof("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err)
ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err)
return err
}
// Enter new state
if err := newState.Enter(); err != nil {
logger.Infof("Enter state '%s' failed with: '%v'", newState.Name(), err)
ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err)
return err
}

View file

@ -27,7 +27,6 @@ func parseParams(bytes []byte) ([]string, error) {
params = append(params, s)
}
logger.Infof("Parsed params: %v with length: %d", params, len(params))
return params, nil
}
@ -37,7 +36,6 @@ func parseCmd(context ansiContext) (string, error) {
func getInt(params []string, dflt int) int {
i := getInts(params, 1, dflt)[0]
logger.Infof("getInt: %v", i)
return i
}
@ -60,8 +58,6 @@ func getInts(params []string, minCount int, dflt int) []int {
}
}
logger.Infof("getInts: %v", ints)
return ints
}

View file

@ -1,19 +1,15 @@
package ansiterm
import (
"fmt"
)
func (ap *AnsiParser) collectParam() error {
currChar := ap.context.currentChar
logger.Infof("collectParam %#x", currChar)
ap.logf("collectParam %#x", currChar)
ap.context.paramBuffer = append(ap.context.paramBuffer, currChar)
return nil
}
func (ap *AnsiParser) collectInter() error {
currChar := ap.context.currentChar
logger.Infof("collectInter %#x", currChar)
ap.logf("collectInter %#x", currChar)
ap.context.paramBuffer = append(ap.context.interBuffer, currChar)
return nil
}
@ -21,8 +17,8 @@ func (ap *AnsiParser) collectInter() error {
func (ap *AnsiParser) escDispatch() error {
cmd, _ := parseCmd(*ap.context)
intermeds := ap.context.interBuffer
logger.Infof("escDispatch currentChar: %#x", ap.context.currentChar)
logger.Infof("escDispatch: %v(%v)", cmd, intermeds)
ap.logf("escDispatch currentChar: %#x", ap.context.currentChar)
ap.logf("escDispatch: %v(%v)", cmd, intermeds)
switch cmd {
case "D": // IND
@ -43,8 +39,9 @@ func (ap *AnsiParser) escDispatch() error {
func (ap *AnsiParser) csiDispatch() error {
cmd, _ := parseCmd(*ap.context)
params, _ := parseParams(ap.context.paramBuffer)
ap.logf("Parsed params: %v with length: %d", params, len(params))
logger.Infof("csiDispatch: %v(%v)", cmd, params)
ap.logf("csiDispatch: %v(%v)", cmd, params)
switch cmd {
case "@":
@ -102,7 +99,7 @@ func (ap *AnsiParser) csiDispatch() error {
top, bottom := ints[0], ints[1]
return ap.eventHandler.DECSTBM(top, bottom)
default:
logger.Errorf(fmt.Sprintf("Unsupported CSI command: '%s', with full context: %v", cmd, ap.context))
ap.logf("ERROR: Unsupported CSI command: '%s', with full context: %v", cmd, ap.context)
return nil
}

View file

@ -175,7 +175,7 @@ func GetStdFile(nFile int) (*os.File, uintptr) {
fd, err := syscall.GetStdHandle(nFile)
if err != nil {
panic(fmt.Errorf("Invalid standard handle indentifier: %v -- %v", nFile, err))
panic(fmt.Errorf("Invalid standard handle identifier: %v -- %v", nFile, err))
}
return file, uintptr(fd)

View file

@ -49,17 +49,22 @@ var (
const (
// Console modes
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
ENABLE_PROCESSED_INPUT = 0x0001
ENABLE_LINE_INPUT = 0x0002
ENABLE_ECHO_INPUT = 0x0004
ENABLE_WINDOW_INPUT = 0x0008
ENABLE_MOUSE_INPUT = 0x0010
ENABLE_INSERT_MODE = 0x0020
ENABLE_QUICK_EDIT_MODE = 0x0040
ENABLE_EXTENDED_FLAGS = 0x0080
ENABLE_PROCESSED_INPUT = 0x0001
ENABLE_LINE_INPUT = 0x0002
ENABLE_ECHO_INPUT = 0x0004
ENABLE_WINDOW_INPUT = 0x0008
ENABLE_MOUSE_INPUT = 0x0010
ENABLE_INSERT_MODE = 0x0020
ENABLE_QUICK_EDIT_MODE = 0x0040
ENABLE_EXTENDED_FLAGS = 0x0080
ENABLE_AUTO_POSITION = 0x0100
ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200
ENABLE_PROCESSED_OUTPUT = 0x0001
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
ENABLE_PROCESSED_OUTPUT = 0x0001
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
DISABLE_NEWLINE_AUTO_RETURN = 0x0008
ENABLE_LVB_GRID_WORLDWIDE = 0x0010
// Character attributes
// Note:

View file

@ -34,7 +34,7 @@ func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL
if err != nil {
return err
}
logger.Infof("Cursor position set: (%d, %d)", position.X, position.Y)
h.logf("Cursor position set: (%d, %d)", position.X, position.Y)
return err
}

View file

@ -50,8 +50,8 @@ func (h *windowsAnsiEventHandler) insertLines(param int) error {
// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates.
func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error {
logger.Infof("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom)
logger.Infof("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
h.logf("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom)
h.logf("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
// Copy from and clip to the scroll region (full buffer width)
scrollRect := SMALL_RECT{

View file

@ -4,16 +4,13 @@ package winterm
import (
"bytes"
"io/ioutil"
"log"
"os"
"strconv"
"github.com/Azure/go-ansiterm"
"github.com/sirupsen/logrus"
)
var logger *logrus.Logger
type windowsAnsiEventHandler struct {
fd uintptr
file *os.File
@ -28,32 +25,52 @@ type windowsAnsiEventHandler struct {
marginByte byte
curInfo *CONSOLE_SCREEN_BUFFER_INFO
curPos COORD
logf func(string, ...interface{})
}
func CreateWinEventHandler(fd uintptr, file *os.File) ansiterm.AnsiEventHandler {
logFile := ioutil.Discard
type Option func(*windowsAnsiEventHandler)
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
logFile, _ = os.Create("winEventHandler.log")
}
logger = &logrus.Logger{
Out: logFile,
Formatter: new(logrus.TextFormatter),
Level: logrus.DebugLevel,
func WithLogf(f func(string, ...interface{})) Option {
return func(w *windowsAnsiEventHandler) {
w.logf = f
}
}
func CreateWinEventHandler(fd uintptr, file *os.File, opts ...Option) ansiterm.AnsiEventHandler {
infoReset, err := GetConsoleScreenBufferInfo(fd)
if err != nil {
return nil
}
return &windowsAnsiEventHandler{
h := &windowsAnsiEventHandler{
fd: fd,
file: file,
infoReset: infoReset,
attributes: infoReset.Attributes,
}
for _, o := range opts {
o(h)
}
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
logFile, _ := os.Create("winEventHandler.log")
logger := log.New(logFile, "", log.LstdFlags)
if h.logf != nil {
l := h.logf
h.logf = func(s string, v ...interface{}) {
l(s, v...)
logger.Printf(s, v...)
}
} else {
h.logf = logger.Printf
}
}
if h.logf == nil {
h.logf = func(string, ...interface{}) {}
}
return h
}
type scrollRegion struct {
@ -96,7 +113,7 @@ func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
if err := h.Flush(); err != nil {
return false, err
}
logger.Info("Simulating LF inside scroll region")
h.logf("Simulating LF inside scroll region")
if err := h.scrollUp(1); err != nil {
return false, err
}
@ -119,7 +136,7 @@ func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
} else {
// The cursor is at the bottom of the screen but outside the scroll
// region. Skip the LF.
logger.Info("Simulating LF outside scroll region")
h.logf("Simulating LF outside scroll region")
if includeCR {
if err := h.Flush(); err != nil {
return false, err
@ -151,7 +168,7 @@ func (h *windowsAnsiEventHandler) executeLF() error {
if err := h.Flush(); err != nil {
return err
}
logger.Info("Resetting cursor position for LF without CR")
h.logf("Resetting cursor position for LF without CR")
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
return err
}
@ -186,7 +203,7 @@ func (h *windowsAnsiEventHandler) Print(b byte) error {
func (h *windowsAnsiEventHandler) Execute(b byte) error {
switch b {
case ansiterm.ANSI_TAB:
logger.Info("Execute(TAB)")
h.logf("Execute(TAB)")
// Move to the next tab stop, but preserve auto-wrap if already set.
if !h.wrapNext {
pos, info, err := h.getCurrentInfo()
@ -269,7 +286,7 @@ func (h *windowsAnsiEventHandler) CUU(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("CUU: [%v]", []string{strconv.Itoa(param)})
h.logf("CUU: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.moveCursorVertical(-param)
}
@ -278,7 +295,7 @@ func (h *windowsAnsiEventHandler) CUD(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("CUD: [%v]", []string{strconv.Itoa(param)})
h.logf("CUD: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.moveCursorVertical(param)
}
@ -287,7 +304,7 @@ func (h *windowsAnsiEventHandler) CUF(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("CUF: [%v]", []string{strconv.Itoa(param)})
h.logf("CUF: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.moveCursorHorizontal(param)
}
@ -296,7 +313,7 @@ func (h *windowsAnsiEventHandler) CUB(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("CUB: [%v]", []string{strconv.Itoa(param)})
h.logf("CUB: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.moveCursorHorizontal(-param)
}
@ -305,7 +322,7 @@ func (h *windowsAnsiEventHandler) CNL(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("CNL: [%v]", []string{strconv.Itoa(param)})
h.logf("CNL: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.moveCursorLine(param)
}
@ -314,7 +331,7 @@ func (h *windowsAnsiEventHandler) CPL(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("CPL: [%v]", []string{strconv.Itoa(param)})
h.logf("CPL: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.moveCursorLine(-param)
}
@ -323,7 +340,7 @@ func (h *windowsAnsiEventHandler) CHA(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("CHA: [%v]", []string{strconv.Itoa(param)})
h.logf("CHA: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.moveCursorColumn(param)
}
@ -332,7 +349,7 @@ func (h *windowsAnsiEventHandler) VPA(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("VPA: [[%d]]", param)
h.logf("VPA: [[%d]]", param)
h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil {
@ -348,7 +365,7 @@ func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("CUP: [[%d %d]]", row, col)
h.logf("CUP: [[%d %d]]", row, col)
h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil {
@ -364,7 +381,7 @@ func (h *windowsAnsiEventHandler) HVP(row int, col int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("HVP: [[%d %d]]", row, col)
h.logf("HVP: [[%d %d]]", row, col)
h.clearWrap()
return h.CUP(row, col)
}
@ -373,7 +390,7 @@ func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
h.logf("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
h.clearWrap()
return nil
}
@ -382,7 +399,7 @@ func (h *windowsAnsiEventHandler) DECOM(enable bool) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("DECOM: [%v]", []string{strconv.FormatBool(enable)})
h.logf("DECOM: [%v]", []string{strconv.FormatBool(enable)})
h.clearWrap()
h.originMode = enable
return h.CUP(1, 1)
@ -392,7 +409,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
h.logf("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
h.clearWrap()
if err := h.ED(2); err != nil {
return err
@ -407,7 +424,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
}
if info.Size.X < targetWidth {
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
logger.Info("set buffer failed:", err)
h.logf("set buffer failed: %v", err)
return err
}
}
@ -415,12 +432,12 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
window.Left = 0
window.Right = targetWidth - 1
if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
logger.Info("set window failed:", err)
h.logf("set window failed: %v", err)
return err
}
if info.Size.X > targetWidth {
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
logger.Info("set buffer failed:", err)
h.logf("set buffer failed: %v", err)
return err
}
}
@ -431,7 +448,7 @@ func (h *windowsAnsiEventHandler) ED(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("ED: [%v]", []string{strconv.Itoa(param)})
h.logf("ED: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
// [J -- Erases from the cursor to the end of the screen, including the cursor position.
@ -490,7 +507,7 @@ func (h *windowsAnsiEventHandler) EL(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("EL: [%v]", strconv.Itoa(param))
h.logf("EL: [%v]", strconv.Itoa(param))
h.clearWrap()
// [K -- Erases from the cursor to the end of the line, including the cursor position.
@ -531,7 +548,7 @@ func (h *windowsAnsiEventHandler) IL(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("IL: [%v]", strconv.Itoa(param))
h.logf("IL: [%v]", strconv.Itoa(param))
h.clearWrap()
return h.insertLines(param)
}
@ -540,7 +557,7 @@ func (h *windowsAnsiEventHandler) DL(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("DL: [%v]", strconv.Itoa(param))
h.logf("DL: [%v]", strconv.Itoa(param))
h.clearWrap()
return h.deleteLines(param)
}
@ -549,7 +566,7 @@ func (h *windowsAnsiEventHandler) ICH(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("ICH: [%v]", strconv.Itoa(param))
h.logf("ICH: [%v]", strconv.Itoa(param))
h.clearWrap()
return h.insertCharacters(param)
}
@ -558,7 +575,7 @@ func (h *windowsAnsiEventHandler) DCH(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("DCH: [%v]", strconv.Itoa(param))
h.logf("DCH: [%v]", strconv.Itoa(param))
h.clearWrap()
return h.deleteCharacters(param)
}
@ -572,7 +589,7 @@ func (h *windowsAnsiEventHandler) SGR(params []int) error {
strings = append(strings, strconv.Itoa(v))
}
logger.Infof("SGR: [%v]", strings)
h.logf("SGR: [%v]", strings)
if len(params) <= 0 {
h.attributes = h.infoReset.Attributes
@ -606,7 +623,7 @@ func (h *windowsAnsiEventHandler) SU(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("SU: [%v]", []string{strconv.Itoa(param)})
h.logf("SU: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.scrollUp(param)
}
@ -615,13 +632,13 @@ func (h *windowsAnsiEventHandler) SD(param int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("SD: [%v]", []string{strconv.Itoa(param)})
h.logf("SD: [%v]", []string{strconv.Itoa(param)})
h.clearWrap()
return h.scrollDown(param)
}
func (h *windowsAnsiEventHandler) DA(params []string) error {
logger.Infof("DA: [%v]", params)
h.logf("DA: [%v]", params)
// DA cannot be implemented because it must send data on the VT100 input stream,
// which is not available to go-ansiterm.
return nil
@ -631,7 +648,7 @@ func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
if err := h.Flush(); err != nil {
return err
}
logger.Infof("DECSTBM: [%d, %d]", top, bottom)
h.logf("DECSTBM: [%d, %d]", top, bottom)
// Windows is 0 indexed, Linux is 1 indexed
h.sr.top = int16(top - 1)
@ -646,7 +663,7 @@ func (h *windowsAnsiEventHandler) RI() error {
if err := h.Flush(); err != nil {
return err
}
logger.Info("RI: []")
h.logf("RI: []")
h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd)
@ -663,21 +680,21 @@ func (h *windowsAnsiEventHandler) RI() error {
}
func (h *windowsAnsiEventHandler) IND() error {
logger.Info("IND: []")
h.logf("IND: []")
return h.executeLF()
}
func (h *windowsAnsiEventHandler) Flush() error {
h.curInfo = nil
if h.buffer.Len() > 0 {
logger.Infof("Flush: [%s]", h.buffer.Bytes())
h.logf("Flush: [%s]", h.buffer.Bytes())
if _, err := h.buffer.WriteTo(h.file); err != nil {
return err
}
}
if h.wrapNext && !h.drewMarginByte {
logger.Infof("Flush: drawing margin byte '%c'", h.marginByte)
h.logf("Flush: drawing margin byte '%c'", h.marginByte)
info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil {

View file

@ -0,0 +1,51 @@
package adal
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
}

View file

@ -1,4 +1,4 @@
package azure
package adal
/*
This file is largely based on rjw57/oauth2device's code, with the follow differences:
@ -10,16 +10,17 @@ package azure
*/
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/Azure/go-autorest/autorest"
)
const (
logPrefix = "autorest/azure/devicetoken:"
logPrefix = "autorest/adal/devicetoken:"
)
var (
@ -38,10 +39,17 @@ var (
// 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
@ -79,31 +87,45 @@ type deviceToken struct {
// InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode
// that can be used with CheckForUserCompletion or WaitForUserCompletion.
func InitiateDeviceAuth(client *autorest.Client, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) {
req, _ := autorest.Prepare(
&http.Request{},
autorest.AsPost(),
autorest.AsFormURLEncoded(),
autorest.WithBaseURL(oauthConfig.DeviceCodeEndpoint.String()),
autorest.WithFormData(url.Values{
"client_id": []string{clientID},
"resource": []string{resource},
}),
)
func InitiateDeviceAuth(sender Sender, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) {
v := url.Values{
"client_id": []string{clientID},
"resource": []string{resource},
}
resp, err := autorest.SendWithSender(client, req)
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)
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 = autorest.Respond(
resp,
autorest.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&code),
autorest.ByClosing())
err = json.Unmarshal(rb, &code)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err)
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
}
code.ClientID = clientID
@ -115,33 +137,46 @@ func InitiateDeviceAuth(client *autorest.Client, oauthConfig OAuthConfig, client
// 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(client *autorest.Client, code *DeviceCode) (*Token, error) {
req, _ := autorest.Prepare(
&http.Request{},
autorest.AsPost(),
autorest.AsFormURLEncoded(),
autorest.WithBaseURL(code.OAuthConfig.TokenEndpoint.String()),
autorest.WithFormData(url.Values{
"client_id": []string{code.ClientID},
"code": []string{*code.DeviceCode},
"grant_type": []string{OAuthGrantTypeDeviceCode},
"resource": []string{code.Resource},
}),
)
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},
}
resp, err := autorest.SendWithSender(client, req)
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)
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 = autorest.Respond(
resp,
autorest.WithErrorUnlessStatusCode(http.StatusOK, http.StatusBadRequest),
autorest.ByUnmarshallingJSON(&token),
autorest.ByClosing())
err = json.Unmarshal(rb, &token)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err)
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
}
if token.Error == nil {
@ -164,12 +199,12 @@ func CheckForUserCompletion(client *autorest.Client, code *DeviceCode) (*Token,
// 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(client *autorest.Client, code *DeviceCode) (*Token, error) {
func WaitForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
intervalDuration := time.Duration(*code.Interval) * time.Second
waitDuration := intervalDuration
for {
token, err := CheckForUserCompletion(client, code)
token, err := CheckForUserCompletion(sender, code)
if err == nil {
return token, nil

View file

@ -0,0 +1,6 @@
// +build !windows
package adal
// msiPath is the path to the MSI Extension settings file (to discover the endpoint)
var msiPath = "/var/lib/waagent/ManagedIdentity-Settings"

View file

@ -0,0 +1,11 @@
// +build windows
package adal
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"}, "/")

View file

@ -1,4 +1,4 @@
package azure
package adal
import (
"encoding/json"

View file

@ -0,0 +1,46 @@
package adal
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
}

View file

@ -1,4 +1,4 @@
package azure
package adal
import (
"crypto/rand"
@ -6,19 +6,21 @@ import (
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/date"
"github.com/dgrijalva/jwt-go"
)
const (
defaultRefresh = 5 * time.Minute
tokenBaseDate = "1970-01-01T00:00:00Z"
// OAuthGrantTypeDeviceCode is the "grant_type" identifier used in device flow
OAuthGrantTypeDeviceCode = "device_code"
@ -28,12 +30,21 @@ const (
// OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows
OAuthGrantTypeRefreshToken = "refresh_token"
// metadataHeader is the header required by MSI extension
metadataHeader = "Metadata"
)
var expirationBase time.Time
// OAuthTokenProvider is an interface which should be implemented by an access token retriever
type OAuthTokenProvider interface {
OAuthToken() string
}
func init() {
expirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate)
// 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
@ -59,7 +70,10 @@ func (t Token) Expires() time.Time {
if err != nil {
s = -3600
}
return expirationBase.Add(time.Duration(s) * time.Second).UTC()
expiration := date.NewUnixTimeFromSeconds(float64(s))
return time.Time(expiration).UTC()
}
// IsExpired returns true if the Token is expired, false otherwise.
@ -73,14 +87,9 @@ func (t Token) WillExpireIn(d time.Duration) bool {
return !t.Expires().After(time.Now().Add(d))
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
// value is "Bearer " followed by the AccessToken of the Token.
func (t *Token) WithAuthorization() autorest.PrepareDecorator {
return func(p autorest.Preparer) autorest.Preparer {
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
return (autorest.WithBearerAuthorization(t.AccessToken)(p)).Prepare(r)
})
}
//OAuthToken return the current access token
func (t *Token) OAuthToken() string {
return t.AccessToken
}
// ServicePrincipalNoSecret represents a secret type that contains no secret
@ -118,6 +127,15 @@ type ServicePrincipalCertificateSecret struct {
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()
@ -173,7 +191,7 @@ type ServicePrincipalToken struct {
resource string
autoRefresh bool
refreshWithin time.Duration
sender autorest.Sender
sender Sender
refreshCallbacks []TokenRefreshCallback
}
@ -238,10 +256,58 @@ func NewServicePrincipalTokenFromCertificate(oauthConfig OAuthConfig, clientID s
)
}
// 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).
// RefreshWithin) and autoRefresh flag is on.
func (spt *ServicePrincipalToken) EnsureFresh() error {
if spt.WillExpireIn(spt.refreshWithin) {
if spt.autoRefresh && spt.WillExpireIn(spt.refreshWithin) {
return spt.Refresh()
}
return nil
@ -253,8 +319,7 @@ func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error {
for _, callback := range spt.refreshCallbacks {
err := callback(spt.Token)
if err != nil {
return autorest.NewErrorWithError(err,
"azure.ServicePrincipalToken", "InvokeRefreshCallbacks", nil, "A TokenRefreshCallback handler returned an error")
return fmt.Errorf("adal: TokenRefreshCallback handler failed. Error = '%v'", err)
}
}
}
@ -287,39 +352,48 @@ func (spt *ServicePrincipalToken) refreshInternal(resource string) error {
}
}
req, _ := autorest.Prepare(&http.Request{},
autorest.AsPost(),
autorest.AsFormURLEncoded(),
autorest.WithBaseURL(spt.oauthConfig.TokenEndpoint.String()),
autorest.WithFormData(v))
resp, err := autorest.SendWithSender(spt.sender, req)
s := v.Encode()
body := ioutil.NopCloser(strings.NewReader(s))
req, err := http.NewRequest(http.MethodPost, spt.oauthConfig.TokenEndpoint.String(), body)
if err != nil {
return autorest.NewErrorWithError(err,
"azure.ServicePrincipalToken", "Refresh", resp, "Failure sending request for Service Principal %s",
spt.clientID)
return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err)
}
var newToken Token
err = autorest.Respond(resp,
autorest.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&newToken),
autorest.ByClosing())
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 autorest.NewErrorWithError(err,
"azure.ServicePrincipalToken", "Refresh", resp, "Failure handling response to Service Principal %s request",
spt.clientID)
return fmt.Errorf("adal: Failed to execute the refresh request. Error = '%v'", err)
}
spt.Token = newToken
defer resp.Body.Close()
rb, err := ioutil.ReadAll(resp.Body)
err = spt.InvokeRefreshCallbacks(newToken)
if err != nil {
// its already wrapped inside InvokeRefreshCallbacks
return err
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))
}
return nil
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.
@ -334,30 +408,6 @@ func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) {
return
}
// SetSender sets the autorest.Sender used when obtaining the Service Principal token. An
// 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 autorest.Sender) {
spt.sender = s
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
// value is "Bearer " followed by the AccessToken of the ServicePrincipalToken.
//
// By default, the token will automatically refresh if nearly expired (as determined by the
// RefreshWithin interval). Use the AutoRefresh method to enable or disable automatically refreshing
// tokens.
func (spt *ServicePrincipalToken) WithAuthorization() autorest.PrepareDecorator {
return func(p autorest.Preparer) autorest.Preparer {
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
if spt.autoRefresh {
err := spt.EnsureFresh()
if err != nil {
return r, autorest.NewErrorWithError(err,
"azure.ServicePrincipalToken", "WithAuthorization", nil, "Failed to refresh Service Principal Token for request to %s",
r.URL)
}
}
return (autorest.WithBearerAuthorization(spt.AccessToken)(p)).Prepare(r)
})
}
}
func (spt *ServicePrincipalToken) SetSender(s Sender) { spt.sender = s }

View file

@ -0,0 +1,167 @@
package autorest
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
}

View file

@ -17,12 +17,6 @@ const (
)
const (
methodDelete = "DELETE"
methodPatch = "PATCH"
methodPost = "POST"
methodPut = "PUT"
methodGet = "GET"
operationInProgress string = "InProgress"
operationCanceled string = "Canceled"
operationFailed string = "Failed"
@ -226,7 +220,7 @@ func updatePollingState(resp *http.Response, ps *pollingState) error {
// Lastly, requests against an existing resource, use the last request URI
if ps.uri == "" {
m := strings.ToUpper(req.Method)
if m == methodPatch || m == methodPut || m == methodGet {
if m == http.MethodPatch || m == http.MethodPut || m == http.MethodGet {
ps.uri = req.URL.String()
}
}

View file

@ -1,13 +0,0 @@
package azure
import (
"net/url"
)
// OAuthConfig represents the endpoints needed
// in OAuth operations
type OAuthConfig struct {
AuthorizeEndpoint url.URL
TokenEndpoint url.URL
DeviceCodeEndpoint url.URL
}

View file

@ -2,14 +2,9 @@ package azure
import (
"fmt"
"net/url"
"strings"
)
const (
activeDirectoryAPIVersion = "1.0"
)
var environments = map[string]Environment{
"AZURECHINACLOUD": ChinaCloud,
"AZUREGERMANCLOUD": GermanCloud,
@ -133,35 +128,3 @@ func EnvironmentFromName(name string) (Environment, error) {
}
return env, nil
}
// OAuthConfigForTenant returns an OAuthConfig with tenant specific urls
func (env Environment) OAuthConfigForTenant(tenantID string) (*OAuthConfig, error) {
return OAuthConfigForTenant(env.ActiveDirectoryEndpoint, tenantID)
}
// OAuthConfigForTenant returns an OAuthConfig with tenant specific urls for target cloud auth endpoint
func OAuthConfigForTenant(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) {
template := "%s/oauth2/%s?api-version=%s"
u, err := url.Parse(activeDirectoryEndpoint)
if err != nil {
return nil, err
}
authorizeURL, err := u.Parse(fmt.Sprintf(template, tenantID, "authorize", activeDirectoryAPIVersion))
if err != nil {
return nil, err
}
tokenURL, err := u.Parse(fmt.Sprintf(template, tenantID, "token", activeDirectoryAPIVersion))
if err != nil {
return nil, err
}
deviceCodeURL, err := u.Parse(fmt.Sprintf(template, tenantID, "devicecode", activeDirectoryAPIVersion))
if err != nil {
return nil, err
}
return &OAuthConfig{
AuthorizeEndpoint: *authorizeURL,
TokenEndpoint: *tokenURL,
DeviceCodeEndpoint: *deviceCodeURL,
}, nil
}

View file

@ -35,6 +35,7 @@ var (
statusCodesForRetry = []int{
http.StatusRequestTimeout, // 408
http.StatusTooManyRequests, // 429
http.StatusInternalServerError, // 500
http.StatusBadGateway, // 502
http.StatusServiceUnavailable, // 503

View file

@ -0,0 +1,109 @@
package date
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
}

View file

@ -31,6 +31,9 @@ type DetailedError struct {
// 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
@ -67,6 +70,7 @@ func NewErrorWithError(original error, packageType string, method string, resp *
Method: method,
StatusCode: statusCode,
Message: fmt.Sprintf(message, args...),
Response: resp,
}
}

View file

@ -426,18 +426,3 @@ func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorato
})
}
}
// 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()
}

View file

@ -0,0 +1,38 @@
package autorest
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
}

View file

@ -0,0 +1,44 @@
// +build !go1.8
package autorest
import (
"bytes"
"net/http"
)
// RetriableRequest provides facilities for retrying an HTTP request.
type RetriableRequest struct {
req *http.Request
br *bytes.Reader
reset bool
}
// 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.reset {
if rr.br != nil {
_, err = rr.br.Seek(0, 0 /*io.SeekStart*/)
}
rr.reset = false
if err != nil {
return err
}
}
if rr.br == nil {
// fall back to making a copy (only do this once)
err = rr.prepareFromByteReader()
}
// indicates that the request body needs to be reset
rr.reset = true
}
return err
}
func removeRequestBody(req *http.Request) {
req.Body = nil
req.ContentLength = 0
}

View file

@ -0,0 +1,56 @@
// +build go1.8
package autorest
import (
"bytes"
"io"
"net/http"
)
// RetriableRequest provides facilities for retrying an HTTP request.
type RetriableRequest struct {
req *http.Request
rc io.ReadCloser
br *bytes.Reader
reset bool
}
// 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.reset {
if rr.rc != nil {
rr.req.Body = rr.rc
} else if rr.br != nil {
_, err = rr.br.Seek(0, io.SeekStart)
}
rr.reset = false
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()
}
// indicates that the request body needs to be reset
rr.reset = true
}
return err
}
func removeRequestBody(req *http.Request) {
req.Body = nil
req.GetBody = nil
req.ContentLength = 0
}

View file

@ -1,12 +1,11 @@
package autorest
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"math"
"net/http"
"strconv"
"time"
)
@ -175,8 +174,13 @@ func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...
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++ {
resp, err = s.Do(r)
err = rr.Prepare()
if err != nil {
return resp, err
}
resp, err = s.Do(rr.Request())
if err == nil {
return resp, err
}
@ -194,29 +198,43 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
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) {
b := []byte{}
if r.Body != nil {
b, err = ioutil.ReadAll(r.Body)
if err != nil {
return resp, err
}
}
rr := NewRetriableRequest(r)
// Increment to add the first call (attempts denotes number of retries)
attempts++
for attempt := 0; attempt < attempts; attempt++ {
r.Body = ioutil.NopCloser(bytes.NewBuffer(b))
resp, err = s.Do(r)
err = rr.Prepare()
if err != nil {
return resp, err
}
resp, err = s.Do(rr.Request())
if err != nil || !ResponseHasStatusCode(resp, codes...) {
return resp, err
}
DelayForBackoff(backoff, attempt, r.Cancel)
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
@ -224,9 +242,14 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se
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++ {
resp, err = s.Do(r)
err = rr.Prepare()
if err != nil {
return resp, err
}
resp, err = s.Do(rr.Request())
if err == nil {
return resp, err
}

View file

@ -1,29 +1,35 @@
package autorest
import (
"bytes"
"fmt"
"strings"
"sync"
)
const (
major = 7
minor = 3
patch = 1
major = 8
minor = 0
patch = 0
tag = ""
)
var versionLock sync.Once
var once sync.Once
var version string
// Version returns the semantic version (see http://semver.org).
func Version() string {
versionLock.Do(func() {
version = fmt.Sprintf("v%d.%d.%d", major, minor, patch)
if trimmed := strings.TrimPrefix(tag, "-"); trimmed != "" {
version = fmt.Sprintf("%s-%s", version, trimmed)
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
}