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

38
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: 90d53da9a6eaba85d524d95410051ff7f12b1f76181df2ad4796b2439f3a2a37 hash: 70e3ed39d2777d36b679ff55b8b0f2eb8d0b239fdfb8108f41b8b7239bc7920c
updated: 2017-10-24T14:08:11.364720581+02:00 updated: 2017-10-25T10:18:20.438135-04:00
imports: imports:
- name: cloud.google.com/go - name: cloud.google.com/go
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
@ -56,13 +56,14 @@ imports:
- service/route53 - service/route53
- service/sts - service/sts
- name: github.com/Azure/azure-sdk-for-go - name: github.com/Azure/azure-sdk-for-go
version: 088007b3b08cc02b27f2eadfdcd870958460ce7e version: f7bb4db3ea4c73dc58bd284c38ea644a79324be0
subpackages: subpackages:
- arm/dns - arm/dns
- name: github.com/Azure/go-autorest - name: github.com/Azure/go-autorest
version: a2fdd780c9a50455cecd249b00bdc3eb73a78e31 version: f6be1abbb5abd0517522f850dd785990d373da7e
subpackages: subpackages:
- autorest - autorest
- autorest/adal
- autorest/azure - autorest/azure
- autorest/date - autorest/date
- autorest/to - autorest/to
@ -121,11 +122,11 @@ imports:
subpackages: subpackages:
- spew - spew
- name: github.com/decker502/dnspod-go - name: github.com/decker502/dnspod-go
version: 68650ee11e182e30773781d391c66a0c80ccf9f2 version: f33a2c6040fc2550a631de7b3a53bddccdcd73fb
- name: github.com/dgrijalva/jwt-go - name: github.com/dgrijalva/jwt-go
version: d2709f9f1f31ebcda9651b03077758c1f3a0018c version: d2709f9f1f31ebcda9651b03077758c1f3a0018c
- name: github.com/dnsimple/dnsimple-go - name: github.com/dnsimple/dnsimple-go
version: 5a5b427618a76f9eed5ede0f3e6306fbd9311d2e version: f2d9b723cc9547d182e24ac2e527ae25d25fc93f
subpackages: subpackages:
- dnsimple - dnsimple
- name: github.com/docker/distribution - name: github.com/docker/distribution
@ -219,7 +220,7 @@ imports:
- name: github.com/eapache/queue - name: github.com/eapache/queue
version: 44cc805cf13205b55f69e14bcb69867d1ae92f98 version: 44cc805cf13205b55f69e14bcb69867d1ae92f98
- name: github.com/edeckers/auroradnsclient - name: github.com/edeckers/auroradnsclient
version: 8b777c170cfd377aa16bb4368f093017dddef3f9 version: 398f53855ba258191157e20fabfaccca5e13cea9
subpackages: subpackages:
- records - records
- requests - requests
@ -233,6 +234,8 @@ imports:
subpackages: subpackages:
- log - log
- swagger - swagger
- name: github.com/exoscale/egoscale
version: 325740036187ddae3a5b74be00fbbc70011c4d96
- name: github.com/fatih/color - name: github.com/fatih/color
version: 62e9147c64a1ed519147b62a56a14e83e2be02c1 version: 62e9147c64a1ed519147b62a56a14e83e2be02c1
- name: github.com/gambol99/go-marathon - name: github.com/gambol99/go-marathon
@ -240,7 +243,7 @@ imports:
- name: github.com/ghodss/yaml - name: github.com/ghodss/yaml
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
- name: github.com/go-ini/ini - name: github.com/go-ini/ini
version: e7fea39b01aea8d5671f6858f0532f56e8bff3a5 version: f384f410798cbe7cdce40eec40b79ed32bb4f1ad
- name: github.com/go-kit/kit - name: github.com/go-kit/kit
version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41 version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41
subpackages: subpackages:
@ -309,7 +312,7 @@ imports:
- name: github.com/imdario/mergo - name: github.com/imdario/mergo
version: 3e95a51e0639b4cf372f2ccf74c86749d747fbdc version: 3e95a51e0639b4cf372f2ccf74c86749d747fbdc
- name: github.com/JamesClonk/vultr - name: github.com/JamesClonk/vultr
version: 0f156dd232bc4ebf8a32ba83fec57c0e4c9db69f version: 2fd0705ce648e602e6c9c57329a174270a4f6688
subpackages: subpackages:
- lib - lib
- name: github.com/jmespath/go-jmespath - name: github.com/jmespath/go-jmespath
@ -398,7 +401,7 @@ imports:
- specs-go - specs-go
- specs-go/v1 - specs-go/v1
- name: github.com/ovh/go-ovh - name: github.com/ovh/go-ovh
version: d2207178e10e4527e8f222fd8707982df8c3af17 version: 4b1fea467323b74c5f462f0947f402b428ca0626
subpackages: subpackages:
- ovh - ovh
- name: github.com/pborman/uuid - name: github.com/pborman/uuid
@ -432,10 +435,6 @@ imports:
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
- name: github.com/PuerkitoBio/urlesc - name: github.com/PuerkitoBio/urlesc
version: 5bd2802263f21d8788851d5305584c82a5c75d7e version: 5bd2802263f21d8788851d5305584c82a5c75d7e
- name: github.com/pyr/egoscale
version: 987e683a7552f34ee586217d1cc8507d52e80ab9
subpackages:
- src/egoscale
- name: github.com/rancher/go-rancher - name: github.com/rancher/go-rancher
version: 5b8f6cc26b355ba03d7611fce3844155b7baf05b version: 5b8f6cc26b355ba03d7611fce3844155b7baf05b
subpackages: subpackages:
@ -509,7 +508,7 @@ imports:
- plugin/rewrite - plugin/rewrite
- router - router
- name: github.com/xenolf/lego - name: github.com/xenolf/lego
version: 5dfe609afb1ebe9da97c9846d97a55415e5a5ccd version: 67c86d860a797ce2483f50d9174d4ed24984bef2
subpackages: subpackages:
- acme - acme
- providers/dns - providers/dns
@ -527,6 +526,7 @@ imports:
- providers/dns/linode - providers/dns/linode
- providers/dns/namecheap - providers/dns/namecheap
- providers/dns/ns1 - providers/dns/ns1
- providers/dns/otc
- providers/dns/ovh - providers/dns/ovh
- providers/dns/pdns - providers/dns/pdns
- providers/dns/rackspace - providers/dns/rackspace
@ -585,7 +585,7 @@ imports:
subpackages: subpackages:
- rate - rate
- name: google.golang.org/api - name: google.golang.org/api
version: 9bf6e6e569ff057f75d9604a46c52928f17d2b54 version: 1575df15c1bb8b18ad4d9bc5ca495cc85b0764fe
subpackages: subpackages:
- dns/v1 - dns/v1
- gensupport - gensupport
@ -622,9 +622,9 @@ imports:
- name: gopkg.in/inf.v0 - name: gopkg.in/inf.v0
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
- name: gopkg.in/ini.v1 - name: gopkg.in/ini.v1
version: e7fea39b01aea8d5671f6858f0532f56e8bff3a5 version: 5b3e00af70a9484542169a976dcab8d03e601a17
- name: gopkg.in/ns1/ns1-go.v2 - name: gopkg.in/ns1/ns1-go.v2
version: 2abc76c60bf88ba33b15d1d87a13f624d8dff956 version: c563826f4cbef9c11bebeb9f20a3f7afe9c1e2f4
subpackages: subpackages:
- rest - rest
- rest/model/account - rest/model/account
@ -751,7 +751,7 @@ imports:
- transport - transport
testImports: testImports:
- name: github.com/Azure/go-ansiterm - name: github.com/Azure/go-ansiterm
version: 19f72df4d05d31cbe1c56bfc8045c96babff6c7e version: d6e3b3328b783f23731bc4d058875b0371ff8109
subpackages: subpackages:
- winterm - winterm
- name: github.com/docker/cli - name: github.com/docker/cli

View file

@ -59,7 +59,7 @@ import:
- package: github.com/vulcand/predicate - package: github.com/vulcand/predicate
version: 19b9dde14240d94c804ae5736ad0e1de10bf8fe6 version: 19b9dde14240d94c804ae5736ad0e1de10bf8fe6
- package: github.com/xenolf/lego - package: github.com/xenolf/lego
version: 5dfe609afb1ebe9da97c9846d97a55415e5a5ccd version: 67c86d860a797ce2483f50d9174d4ed24984bef2
subpackages: subpackages:
- acme - acme
- package: gopkg.in/fsnotify.v1 - package: gopkg.in/fsnotify.v1

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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 // Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is // Changes may cause incorrect behavior and will be lost if the code is regenerated.
// regenerated.
import ( import (
"github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest"

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ type csiParamState struct {
} }
func (csiState csiParamState) Handle(b byte) (s state, e error) { 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) nextState, err := csiState.baseState.Handle(b)
if nextState != nil || err != nil { 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 { 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) csiState.baseState.Transition(s)
switch s { switch s {

View file

@ -5,7 +5,7 @@ type escapeIntermediateState struct {
} }
func (escState escapeIntermediateState) Handle(b byte) (s state, e error) { 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) nextState, err := escState.baseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
@ -24,7 +24,7 @@ func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
} }
func (escState escapeIntermediateState) Transition(s state) 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) escState.baseState.Transition(s)
switch s { switch s {

View file

@ -5,7 +5,7 @@ type escapeState struct {
} }
func (escState escapeState) Handle(b byte) (s state, e error) { 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) nextState, err := escState.baseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
@ -28,7 +28,7 @@ func (escState escapeState) Handle(b byte) (s state, e error) {
} }
func (escState escapeState) Transition(s state) 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) escState.baseState.Transition(s)
switch s { switch s {

View file

@ -5,7 +5,7 @@ type oscStringState struct {
} }
func (oscState oscStringState) Handle(b byte) (s state, e error) { 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) nextState, err := oscState.baseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err

View file

@ -2,14 +2,10 @@ package ansiterm
import ( import (
"errors" "errors"
"io/ioutil" "log"
"os" "os"
"github.com/sirupsen/logrus"
) )
var logger *logrus.Logger
type AnsiParser struct { type AnsiParser struct {
currState state currState state
eventHandler AnsiEventHandler eventHandler AnsiEventHandler
@ -23,50 +19,69 @@ type AnsiParser struct {
ground state ground state
oscString state oscString state
stateMap []state stateMap []state
logf func(string, ...interface{})
} }
func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser { type Option func(*AnsiParser)
logFile := ioutil.Discard
if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" { func WithLogf(f func(string, ...interface{})) Option {
logFile, _ = os.Create("ansiParser.log") return func(ap *AnsiParser) {
ap.logf = f
} }
}
logger = &logrus.Logger{ func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser {
Out: logFile, ap := &AnsiParser{
Formatter: new(logrus.TextFormatter),
Level: logrus.InfoLevel,
}
parser := &AnsiParser{
eventHandler: evtHandler, eventHandler: evtHandler,
context: &ansiContext{}, context: &ansiContext{},
} }
for _, o := range opts {
parser.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: parser}} o(ap)
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,
} }
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) if ap.logf == nil {
return parser 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 { func getState(name string, states []state) state {
@ -97,7 +112,7 @@ func (ap *AnsiParser) handle(b byte) error {
} }
if newState == nil { if newState == nil {
logger.Warning("newState is nil") ap.logf("WARNING: newState is nil")
return errors.New("New state of 'nil' is invalid.") 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 { 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 // Exit old state
if err := ap.currState.Exit(); err != nil { 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 return err
} }
// Perform transition action // Perform transition action
if err := ap.currState.Transition(newState); err != nil { 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 return err
} }
// Enter new state // Enter new state
if err := newState.Enter(); err != nil { 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 return err
} }

View file

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

View file

@ -1,19 +1,15 @@
package ansiterm package ansiterm
import (
"fmt"
)
func (ap *AnsiParser) collectParam() error { func (ap *AnsiParser) collectParam() error {
currChar := ap.context.currentChar currChar := ap.context.currentChar
logger.Infof("collectParam %#x", currChar) ap.logf("collectParam %#x", currChar)
ap.context.paramBuffer = append(ap.context.paramBuffer, currChar) ap.context.paramBuffer = append(ap.context.paramBuffer, currChar)
return nil return nil
} }
func (ap *AnsiParser) collectInter() error { func (ap *AnsiParser) collectInter() error {
currChar := ap.context.currentChar currChar := ap.context.currentChar
logger.Infof("collectInter %#x", currChar) ap.logf("collectInter %#x", currChar)
ap.context.paramBuffer = append(ap.context.interBuffer, currChar) ap.context.paramBuffer = append(ap.context.interBuffer, currChar)
return nil return nil
} }
@ -21,8 +17,8 @@ func (ap *AnsiParser) collectInter() error {
func (ap *AnsiParser) escDispatch() error { func (ap *AnsiParser) escDispatch() error {
cmd, _ := parseCmd(*ap.context) cmd, _ := parseCmd(*ap.context)
intermeds := ap.context.interBuffer intermeds := ap.context.interBuffer
logger.Infof("escDispatch currentChar: %#x", ap.context.currentChar) ap.logf("escDispatch currentChar: %#x", ap.context.currentChar)
logger.Infof("escDispatch: %v(%v)", cmd, intermeds) ap.logf("escDispatch: %v(%v)", cmd, intermeds)
switch cmd { switch cmd {
case "D": // IND case "D": // IND
@ -43,8 +39,9 @@ func (ap *AnsiParser) escDispatch() error {
func (ap *AnsiParser) csiDispatch() error { func (ap *AnsiParser) csiDispatch() error {
cmd, _ := parseCmd(*ap.context) cmd, _ := parseCmd(*ap.context)
params, _ := parseParams(ap.context.paramBuffer) 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 { switch cmd {
case "@": case "@":
@ -102,7 +99,7 @@ func (ap *AnsiParser) csiDispatch() error {
top, bottom := ints[0], ints[1] top, bottom := ints[0], ints[1]
return ap.eventHandler.DECSTBM(top, bottom) return ap.eventHandler.DECSTBM(top, bottom)
default: 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 return nil
} }

View file

@ -175,7 +175,7 @@ func GetStdFile(nFile int) (*os.File, uintptr) {
fd, err := syscall.GetStdHandle(nFile) fd, err := syscall.GetStdHandle(nFile)
if err != nil { 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) return file, uintptr(fd)

View file

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

View file

@ -34,7 +34,7 @@ func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL
if err != nil { if err != nil {
return err 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 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. // 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 { 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) h.logf("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: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
// Copy from and clip to the scroll region (full buffer width) // Copy from and clip to the scroll region (full buffer width)
scrollRect := SMALL_RECT{ scrollRect := SMALL_RECT{

View file

@ -4,16 +4,13 @@ package winterm
import ( import (
"bytes" "bytes"
"io/ioutil" "log"
"os" "os"
"strconv" "strconv"
"github.com/Azure/go-ansiterm" "github.com/Azure/go-ansiterm"
"github.com/sirupsen/logrus"
) )
var logger *logrus.Logger
type windowsAnsiEventHandler struct { type windowsAnsiEventHandler struct {
fd uintptr fd uintptr
file *os.File file *os.File
@ -28,32 +25,52 @@ type windowsAnsiEventHandler struct {
marginByte byte marginByte byte
curInfo *CONSOLE_SCREEN_BUFFER_INFO curInfo *CONSOLE_SCREEN_BUFFER_INFO
curPos COORD curPos COORD
logf func(string, ...interface{})
} }
func CreateWinEventHandler(fd uintptr, file *os.File) ansiterm.AnsiEventHandler { type Option func(*windowsAnsiEventHandler)
logFile := ioutil.Discard
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { func WithLogf(f func(string, ...interface{})) Option {
logFile, _ = os.Create("winEventHandler.log") return func(w *windowsAnsiEventHandler) {
} w.logf = f
logger = &logrus.Logger{
Out: logFile,
Formatter: new(logrus.TextFormatter),
Level: logrus.DebugLevel,
} }
}
func CreateWinEventHandler(fd uintptr, file *os.File, opts ...Option) ansiterm.AnsiEventHandler {
infoReset, err := GetConsoleScreenBufferInfo(fd) infoReset, err := GetConsoleScreenBufferInfo(fd)
if err != nil { if err != nil {
return nil return nil
} }
return &windowsAnsiEventHandler{ h := &windowsAnsiEventHandler{
fd: fd, fd: fd,
file: file, file: file,
infoReset: infoReset, infoReset: infoReset,
attributes: infoReset.Attributes, 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 { type scrollRegion struct {
@ -96,7 +113,7 @@ func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return false, err return false, err
} }
logger.Info("Simulating LF inside scroll region") h.logf("Simulating LF inside scroll region")
if err := h.scrollUp(1); err != nil { if err := h.scrollUp(1); err != nil {
return false, err return false, err
} }
@ -119,7 +136,7 @@ func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
} else { } else {
// The cursor is at the bottom of the screen but outside the scroll // The cursor is at the bottom of the screen but outside the scroll
// region. Skip the LF. // region. Skip the LF.
logger.Info("Simulating LF outside scroll region") h.logf("Simulating LF outside scroll region")
if includeCR { if includeCR {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return false, err return false, err
@ -151,7 +168,7 @@ func (h *windowsAnsiEventHandler) executeLF() error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err 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 { if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
return err return err
} }
@ -186,7 +203,7 @@ func (h *windowsAnsiEventHandler) Print(b byte) error {
func (h *windowsAnsiEventHandler) Execute(b byte) error { func (h *windowsAnsiEventHandler) Execute(b byte) error {
switch b { switch b {
case ansiterm.ANSI_TAB: 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. // Move to the next tab stop, but preserve auto-wrap if already set.
if !h.wrapNext { if !h.wrapNext {
pos, info, err := h.getCurrentInfo() pos, info, err := h.getCurrentInfo()
@ -269,7 +286,7 @@ func (h *windowsAnsiEventHandler) CUU(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUU: [%v]", []string{strconv.Itoa(param)}) h.logf("CUU: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorVertical(-param) return h.moveCursorVertical(-param)
} }
@ -278,7 +295,7 @@ func (h *windowsAnsiEventHandler) CUD(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUD: [%v]", []string{strconv.Itoa(param)}) h.logf("CUD: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorVertical(param) return h.moveCursorVertical(param)
} }
@ -287,7 +304,7 @@ func (h *windowsAnsiEventHandler) CUF(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUF: [%v]", []string{strconv.Itoa(param)}) h.logf("CUF: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorHorizontal(param) return h.moveCursorHorizontal(param)
} }
@ -296,7 +313,7 @@ func (h *windowsAnsiEventHandler) CUB(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUB: [%v]", []string{strconv.Itoa(param)}) h.logf("CUB: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorHorizontal(-param) return h.moveCursorHorizontal(-param)
} }
@ -305,7 +322,7 @@ func (h *windowsAnsiEventHandler) CNL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CNL: [%v]", []string{strconv.Itoa(param)}) h.logf("CNL: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorLine(param) return h.moveCursorLine(param)
} }
@ -314,7 +331,7 @@ func (h *windowsAnsiEventHandler) CPL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CPL: [%v]", []string{strconv.Itoa(param)}) h.logf("CPL: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorLine(-param) return h.moveCursorLine(-param)
} }
@ -323,7 +340,7 @@ func (h *windowsAnsiEventHandler) CHA(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CHA: [%v]", []string{strconv.Itoa(param)}) h.logf("CHA: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorColumn(param) return h.moveCursorColumn(param)
} }
@ -332,7 +349,7 @@ func (h *windowsAnsiEventHandler) VPA(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("VPA: [[%d]]", param) h.logf("VPA: [[%d]]", param)
h.clearWrap() h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
@ -348,7 +365,7 @@ func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUP: [[%d %d]]", row, col) h.logf("CUP: [[%d %d]]", row, col)
h.clearWrap() h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
@ -364,7 +381,7 @@ func (h *windowsAnsiEventHandler) HVP(row int, col int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("HVP: [[%d %d]]", row, col) h.logf("HVP: [[%d %d]]", row, col)
h.clearWrap() h.clearWrap()
return h.CUP(row, col) return h.CUP(row, col)
} }
@ -373,7 +390,7 @@ func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECTCEM: [%v]", []string{strconv.FormatBool(visible)}) h.logf("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
h.clearWrap() h.clearWrap()
return nil return nil
} }
@ -382,7 +399,7 @@ func (h *windowsAnsiEventHandler) DECOM(enable bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECOM: [%v]", []string{strconv.FormatBool(enable)}) h.logf("DECOM: [%v]", []string{strconv.FormatBool(enable)})
h.clearWrap() h.clearWrap()
h.originMode = enable h.originMode = enable
return h.CUP(1, 1) return h.CUP(1, 1)
@ -392,7 +409,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECCOLM: [%v]", []string{strconv.FormatBool(use132)}) h.logf("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
h.clearWrap() h.clearWrap()
if err := h.ED(2); err != nil { if err := h.ED(2); err != nil {
return err return err
@ -407,7 +424,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
} }
if info.Size.X < targetWidth { if info.Size.X < targetWidth {
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { 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 return err
} }
} }
@ -415,12 +432,12 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
window.Left = 0 window.Left = 0
window.Right = targetWidth - 1 window.Right = targetWidth - 1
if err := SetConsoleWindowInfo(h.fd, true, window); err != nil { if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
logger.Info("set window failed:", err) h.logf("set window failed: %v", err)
return err return err
} }
if info.Size.X > targetWidth { if info.Size.X > targetWidth {
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { 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 return err
} }
} }
@ -431,7 +448,7 @@ func (h *windowsAnsiEventHandler) ED(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("ED: [%v]", []string{strconv.Itoa(param)}) h.logf("ED: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
// [J -- Erases from the cursor to the end of the screen, including the cursor position. // [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 { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("EL: [%v]", strconv.Itoa(param)) h.logf("EL: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
// [K -- Erases from the cursor to the end of the line, including the cursor position. // [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 { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("IL: [%v]", strconv.Itoa(param)) h.logf("IL: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
return h.insertLines(param) return h.insertLines(param)
} }
@ -540,7 +557,7 @@ func (h *windowsAnsiEventHandler) DL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DL: [%v]", strconv.Itoa(param)) h.logf("DL: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
return h.deleteLines(param) return h.deleteLines(param)
} }
@ -549,7 +566,7 @@ func (h *windowsAnsiEventHandler) ICH(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("ICH: [%v]", strconv.Itoa(param)) h.logf("ICH: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
return h.insertCharacters(param) return h.insertCharacters(param)
} }
@ -558,7 +575,7 @@ func (h *windowsAnsiEventHandler) DCH(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DCH: [%v]", strconv.Itoa(param)) h.logf("DCH: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
return h.deleteCharacters(param) return h.deleteCharacters(param)
} }
@ -572,7 +589,7 @@ func (h *windowsAnsiEventHandler) SGR(params []int) error {
strings = append(strings, strconv.Itoa(v)) strings = append(strings, strconv.Itoa(v))
} }
logger.Infof("SGR: [%v]", strings) h.logf("SGR: [%v]", strings)
if len(params) <= 0 { if len(params) <= 0 {
h.attributes = h.infoReset.Attributes h.attributes = h.infoReset.Attributes
@ -606,7 +623,7 @@ func (h *windowsAnsiEventHandler) SU(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("SU: [%v]", []string{strconv.Itoa(param)}) h.logf("SU: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.scrollUp(param) return h.scrollUp(param)
} }
@ -615,13 +632,13 @@ func (h *windowsAnsiEventHandler) SD(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("SD: [%v]", []string{strconv.Itoa(param)}) h.logf("SD: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.scrollDown(param) return h.scrollDown(param)
} }
func (h *windowsAnsiEventHandler) DA(params []string) error { 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, // DA cannot be implemented because it must send data on the VT100 input stream,
// which is not available to go-ansiterm. // which is not available to go-ansiterm.
return nil return nil
@ -631,7 +648,7 @@ func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECSTBM: [%d, %d]", top, bottom) h.logf("DECSTBM: [%d, %d]", top, bottom)
// Windows is 0 indexed, Linux is 1 indexed // Windows is 0 indexed, Linux is 1 indexed
h.sr.top = int16(top - 1) h.sr.top = int16(top - 1)
@ -646,7 +663,7 @@ func (h *windowsAnsiEventHandler) RI() error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Info("RI: []") h.logf("RI: []")
h.clearWrap() h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
@ -663,21 +680,21 @@ func (h *windowsAnsiEventHandler) RI() error {
} }
func (h *windowsAnsiEventHandler) IND() error { func (h *windowsAnsiEventHandler) IND() error {
logger.Info("IND: []") h.logf("IND: []")
return h.executeLF() return h.executeLF()
} }
func (h *windowsAnsiEventHandler) Flush() error { func (h *windowsAnsiEventHandler) Flush() error {
h.curInfo = nil h.curInfo = nil
if h.buffer.Len() > 0 { 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 { if _, err := h.buffer.WriteTo(h.file); err != nil {
return err return err
} }
} }
if h.wrapNext && !h.drewMarginByte { 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) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { 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: This file is largely based on rjw57/oauth2device's code, with the follow differences:
@ -10,16 +10,17 @@ package azure
*/ */
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
"github.com/Azure/go-autorest/autorest"
) )
const ( const (
logPrefix = "autorest/azure/devicetoken:" logPrefix = "autorest/adal/devicetoken:"
) )
var ( var (
@ -38,10 +39,17 @@ var (
// ErrDeviceSlowDown represents the service telling us we're polling too often during device flow // 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) 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" errCodeSendingFails = "Error occurred while sending request for Device Authorization Code"
errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint" errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint"
errTokenSendingFails = "Error occurred while sending request with device code for a token" 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)" 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 // 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 // InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode
// that can be used with CheckForUserCompletion or WaitForUserCompletion. // that can be used with CheckForUserCompletion or WaitForUserCompletion.
func InitiateDeviceAuth(client *autorest.Client, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) { func InitiateDeviceAuth(sender Sender, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) {
req, _ := autorest.Prepare( v := url.Values{
&http.Request{}, "client_id": []string{clientID},
autorest.AsPost(), "resource": []string{resource},
autorest.AsFormURLEncoded(), }
autorest.WithBaseURL(oauthConfig.DeviceCodeEndpoint.String()),
autorest.WithFormData(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 { 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 var code DeviceCode
err = autorest.Respond( err = json.Unmarshal(rb, &code)
resp,
autorest.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&code),
autorest.ByClosing())
if err != nil { 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 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 // 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 // to see if the device flow has: been completed, timed out, or otherwise failed
func CheckForUserCompletion(client *autorest.Client, code *DeviceCode) (*Token, error) { func CheckForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
req, _ := autorest.Prepare( v := url.Values{
&http.Request{}, "client_id": []string{code.ClientID},
autorest.AsPost(), "code": []string{*code.DeviceCode},
autorest.AsFormURLEncoded(), "grant_type": []string{OAuthGrantTypeDeviceCode},
autorest.WithBaseURL(code.OAuthConfig.TokenEndpoint.String()), "resource": []string{code.Resource},
autorest.WithFormData(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 { 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 var token deviceToken
err = autorest.Respond( err = json.Unmarshal(rb, &token)
resp,
autorest.WithErrorUnlessStatusCode(http.StatusOK, http.StatusBadRequest),
autorest.ByUnmarshallingJSON(&token),
autorest.ByClosing())
if err != nil { 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 { 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. // WaitForUserCompletion calls CheckForUserCompletion repeatedly until a token is granted or an error state occurs.
// This prevents the user from looping and checking against 'ErrDeviceAuthorizationPending'. // 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 intervalDuration := time.Duration(*code.Interval) * time.Second
waitDuration := intervalDuration waitDuration := intervalDuration
for { for {
token, err := CheckForUserCompletion(client, code) token, err := CheckForUserCompletion(sender, code)
if err == nil { if err == nil {
return token, 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 ( import (
"encoding/json" "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 ( import (
"crypto/rand" "crypto/rand"
@ -6,19 +6,21 @@ import (
"crypto/sha1" "crypto/sha1"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/date"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
) )
const ( const (
defaultRefresh = 5 * time.Minute defaultRefresh = 5 * time.Minute
tokenBaseDate = "1970-01-01T00:00:00Z"
// OAuthGrantTypeDeviceCode is the "grant_type" identifier used in device flow // OAuthGrantTypeDeviceCode is the "grant_type" identifier used in device flow
OAuthGrantTypeDeviceCode = "device_code" OAuthGrantTypeDeviceCode = "device_code"
@ -28,12 +30,21 @@ const (
// OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows // OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows
OAuthGrantTypeRefreshToken = "refresh_token" 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() { // Refresher is an interface for token refresh functionality
expirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate) type Refresher interface {
Refresh() error
RefreshExchange(resource string) error
EnsureFresh() error
} }
// TokenRefreshCallback is the type representing callbacks that will be called after // TokenRefreshCallback is the type representing callbacks that will be called after
@ -59,7 +70,10 @@ func (t Token) Expires() time.Time {
if err != nil { if err != nil {
s = -3600 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. // 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)) return !t.Expires().After(time.Now().Add(d))
} }
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose //OAuthToken return the current access token
// value is "Bearer " followed by the AccessToken of the Token. func (t *Token) OAuthToken() string {
func (t *Token) WithAuthorization() autorest.PrepareDecorator { return t.AccessToken
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)
})
}
} }
// ServicePrincipalNoSecret represents a secret type that contains no secret // ServicePrincipalNoSecret represents a secret type that contains no secret
@ -118,6 +127,15 @@ type ServicePrincipalCertificateSecret struct {
PrivateKey *rsa.PrivateKey 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. // SignJwt returns the JWT signed with the certificate's private key.
func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) { func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) {
hasher := sha1.New() hasher := sha1.New()
@ -173,7 +191,7 @@ type ServicePrincipalToken struct {
resource string resource string
autoRefresh bool autoRefresh bool
refreshWithin time.Duration refreshWithin time.Duration
sender autorest.Sender sender Sender
refreshCallbacks []TokenRefreshCallback 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 // 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 { func (spt *ServicePrincipalToken) EnsureFresh() error {
if spt.WillExpireIn(spt.refreshWithin) { if spt.autoRefresh && spt.WillExpireIn(spt.refreshWithin) {
return spt.Refresh() return spt.Refresh()
} }
return nil return nil
@ -253,8 +319,7 @@ func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error {
for _, callback := range spt.refreshCallbacks { for _, callback := range spt.refreshCallbacks {
err := callback(spt.Token) err := callback(spt.Token)
if err != nil { if err != nil {
return autorest.NewErrorWithError(err, return fmt.Errorf("adal: TokenRefreshCallback handler failed. Error = '%v'", err)
"azure.ServicePrincipalToken", "InvokeRefreshCallbacks", nil, "A TokenRefreshCallback handler returned an error")
} }
} }
} }
@ -287,39 +352,48 @@ func (spt *ServicePrincipalToken) refreshInternal(resource string) error {
} }
} }
req, _ := autorest.Prepare(&http.Request{}, s := v.Encode()
autorest.AsPost(), body := ioutil.NopCloser(strings.NewReader(s))
autorest.AsFormURLEncoded(), req, err := http.NewRequest(http.MethodPost, spt.oauthConfig.TokenEndpoint.String(), body)
autorest.WithBaseURL(spt.oauthConfig.TokenEndpoint.String()),
autorest.WithFormData(v))
resp, err := autorest.SendWithSender(spt.sender, req)
if err != nil { if err != nil {
return autorest.NewErrorWithError(err, return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err)
"azure.ServicePrincipalToken", "Refresh", resp, "Failure sending request for Service Principal %s",
spt.clientID)
} }
var newToken Token req.ContentLength = int64(len(s))
err = autorest.Respond(resp, req.Header.Set(contentType, mimeTypeFormPost)
autorest.WithErrorUnlessStatusCode(http.StatusOK), if _, ok := spt.secret.(*ServicePrincipalMSISecret); ok {
autorest.ByUnmarshallingJSON(&newToken), req.Header.Set(metadataHeader, "true")
autorest.ByClosing()) }
resp, err := spt.sender.Do(req)
if err != nil { if err != nil {
return autorest.NewErrorWithError(err, return fmt.Errorf("adal: Failed to execute the refresh request. Error = '%v'", err)
"azure.ServicePrincipalToken", "Refresh", resp, "Failure handling response to Service Principal %s request",
spt.clientID)
} }
spt.Token = newToken defer resp.Body.Close()
rb, err := ioutil.ReadAll(resp.Body)
err = spt.InvokeRefreshCallbacks(newToken) if resp.StatusCode != http.StatusOK {
if err != nil { if err != nil {
// its already wrapped inside InvokeRefreshCallbacks return fmt.Errorf("adal: Refresh request failed. Status Code = '%d'. Failed reading response body", resp.StatusCode)
return err }
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. // SetAutoRefresh enables or disables automatic refreshing of stale tokens.
@ -334,30 +408,6 @@ func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) {
return 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. // undecorated http.Client is used by default.
func (spt *ServicePrincipalToken) SetSender(s autorest.Sender) { func (spt *ServicePrincipalToken) SetSender(s Sender) { spt.sender = s }
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)
})
}
}

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 ( const (
methodDelete = "DELETE"
methodPatch = "PATCH"
methodPost = "POST"
methodPut = "PUT"
methodGet = "GET"
operationInProgress string = "InProgress" operationInProgress string = "InProgress"
operationCanceled string = "Canceled" operationCanceled string = "Canceled"
operationFailed string = "Failed" 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 // Lastly, requests against an existing resource, use the last request URI
if ps.uri == "" { if ps.uri == "" {
m := strings.ToUpper(req.Method) 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() 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 ( import (
"fmt" "fmt"
"net/url"
"strings" "strings"
) )
const (
activeDirectoryAPIVersion = "1.0"
)
var environments = map[string]Environment{ var environments = map[string]Environment{
"AZURECHINACLOUD": ChinaCloud, "AZURECHINACLOUD": ChinaCloud,
"AZUREGERMANCLOUD": GermanCloud, "AZUREGERMANCLOUD": GermanCloud,
@ -133,35 +128,3 @@ func EnvironmentFromName(name string) (Environment, error) {
} }
return env, nil 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{ statusCodesForRetry = []int{
http.StatusRequestTimeout, // 408 http.StatusRequestTimeout, // 408
http.StatusTooManyRequests, // 429
http.StatusInternalServerError, // 500 http.StatusInternalServerError, // 500
http.StatusBadGateway, // 502 http.StatusBadGateway, // 502
http.StatusServiceUnavailable, // 503 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 // Service Error is the response body of failed API in bytes
ServiceError []byte 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 // 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, Method: method,
StatusCode: statusCode, StatusCode: statusCode,
Message: fmt.Sprintf(message, args...), 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 package autorest
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"math" "math"
"net/http" "net/http"
"strconv"
"time" "time"
) )
@ -175,8 +174,13 @@ func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...
func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator { func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
return func(s Sender) Sender { return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
rr := NewRetriableRequest(r)
for attempt := 0; attempt < attempts; attempt++ { 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 { if err == nil {
return resp, err 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 { func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator {
return func(s Sender) Sender { return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
b := []byte{} rr := NewRetriableRequest(r)
if r.Body != nil {
b, err = ioutil.ReadAll(r.Body)
if err != nil {
return resp, err
}
}
// Increment to add the first call (attempts denotes number of retries) // Increment to add the first call (attempts denotes number of retries)
attempts++ attempts++
for attempt := 0; attempt < attempts; attempt++ { for attempt := 0; attempt < attempts; attempt++ {
r.Body = ioutil.NopCloser(bytes.NewBuffer(b)) err = rr.Prepare()
resp, err = s.Do(r) if err != nil {
return resp, err
}
resp, err = s.Do(rr.Request())
if err != nil || !ResponseHasStatusCode(resp, codes...) { if err != nil || !ResponseHasStatusCode(resp, codes...) {
return resp, err return resp, err
} }
DelayForBackoff(backoff, attempt, r.Cancel) delayed := DelayWithRetryAfter(resp, r.Cancel)
if !delayed {
DelayForBackoff(backoff, attempt, r.Cancel)
}
} }
return resp, err 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 // 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 // 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 // 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 { func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator {
return func(s Sender) Sender { return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
rr := NewRetriableRequest(r)
end := time.Now().Add(d) end := time.Now().Add(d)
for attempt := 0; time.Now().Before(end); attempt++ { 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 { if err == nil {
return resp, err return resp, err
} }

View file

@ -1,29 +1,35 @@
package autorest package autorest
import ( import (
"bytes"
"fmt" "fmt"
"strings" "strings"
"sync" "sync"
) )
const ( const (
major = 7 major = 8
minor = 3 minor = 0
patch = 1 patch = 0
tag = "" tag = ""
) )
var versionLock sync.Once var once sync.Once
var version string var version string
// Version returns the semantic version (see http://semver.org). // Version returns the semantic version (see http://semver.org).
func Version() string { func Version() string {
versionLock.Do(func() { once.Do(func() {
version = fmt.Sprintf("v%d.%d.%d", major, minor, patch) semver := fmt.Sprintf("%d.%d.%d", major, minor, patch)
verBuilder := bytes.NewBufferString(semver)
if trimmed := strings.TrimPrefix(tag, "-"); trimmed != "" { if tag != "" && tag != "-" {
version = fmt.Sprintf("%s-%s", version, trimmed) updated := strings.TrimPrefix(tag, "-")
_, err := verBuilder.WriteString("-" + updated)
if err == nil {
verBuilder = bytes.NewBufferString(semver)
}
} }
version = verBuilder.String()
}) })
return version return version
} }

View file

@ -35,19 +35,31 @@ func (a *AccountInfo) UnmarshalJSON(data []byte) (err error) {
return err return err
} }
b, err := strconv.ParseFloat(fmt.Sprintf("%v", fields["balance"]), 64) value := fmt.Sprintf("%v", fields["balance"])
if len(value) == 0 || value == "<nil>" {
value = "0"
}
b, err := strconv.ParseFloat(value, 64)
if err != nil { if err != nil {
return err return err
} }
a.Balance = b a.Balance = b
pc, err := strconv.ParseFloat(fmt.Sprintf("%v", fields["pending_charges"]), 64) value = fmt.Sprintf("%v", fields["pending_charges"])
if len(value) == 0 || value == "<nil>" {
value = "0"
}
pc, err := strconv.ParseFloat(value, 64)
if err != nil { if err != nil {
return err return err
} }
a.PendingCharges = pc a.PendingCharges = pc
lpa, err := strconv.ParseFloat(fmt.Sprintf("%v", fields["last_payment_amount"]), 64) value = fmt.Sprintf("%v", fields["last_payment_amount"])
if len(value) == 0 || value == "<nil>" {
value = "0"
}
lpa, err := strconv.ParseFloat(value, 64)
if err != nil { if err != nil {
return err return err
} }

View file

@ -1,6 +1,7 @@
package lib package lib
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"errors" "errors"
@ -168,8 +169,25 @@ func (c *Client) do(req *http.Request, data interface{}) error {
// Throttle http requests to avoid hitting Vultr's API rate-limit // Throttle http requests to avoid hitting Vultr's API rate-limit
c.bucket.Wait(1) c.bucket.Wait(1)
// Request body gets drained on each read so we
// need to save it's content for retrying requests
var err error
var requestBody []byte
if req.Body != nil {
requestBody, err = ioutil.ReadAll(req.Body)
if err != nil {
return fmt.Errorf("Error reading request body: %v", err)
}
req.Body.Close()
}
var apiError error var apiError error
for tryCount := 1; tryCount <= c.MaxAttempts; tryCount++ { for tryCount := 1; tryCount <= c.MaxAttempts; tryCount++ {
// Restore request body to the original state
if requestBody != nil {
req.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody))
}
resp, err := c.client.Do(req) resp, err := c.client.Do(req)
if err != nil { if err != nil {
return err return err

View file

@ -72,7 +72,7 @@ func (c *Client) GetDNSRecords(domain string) (records []DNSRecord, err error) {
func (c *Client) CreateDNSDomain(domain, serverIP string) error { func (c *Client) CreateDNSDomain(domain, serverIP string) error {
values := url.Values{ values := url.Values{
"domain": {domain}, "domain": {domain},
"serverIP": {serverIP}, "serverip": {serverIP},
} }
if err := c.post(`dns/create_domain`, values, nil); err != nil { if err := c.post(`dns/create_domain`, values, nil); err != nil {

248
vendor/github.com/JamesClonk/vultr/lib/firewall.go generated vendored Normal file
View file

@ -0,0 +1,248 @@
package lib
import (
"encoding/json"
"fmt"
"net"
"net/url"
"sort"
"strconv"
"strings"
)
// FirewallGroup represents a firewall group on Vultr
type FirewallGroup struct {
ID string `json:"FIREWALLGROUPID"`
Description string `json:"description"`
Created string `json:"date_created"`
Modified string `json:"date_modified"`
InstanceCount int `json:"instance_count"`
RuleCount int `json:"rule_count"`
MaxRuleCount int `json:"max_rule_count"`
}
// FirewallRule represents a firewall rule on Vultr
type FirewallRule struct {
RuleNumber int `json:"rulenumber"`
Action string `json:"action"`
Protocol string `json:"protocol"`
Port string `json:"port"`
Network *net.IPNet
}
type firewallGroups []FirewallGroup
func (f firewallGroups) Len() int { return len(f) }
func (f firewallGroups) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (f firewallGroups) Less(i, j int) bool {
// sort order: description
return strings.ToLower(f[i].Description) < strings.ToLower(f[j].Description)
}
type firewallRules []FirewallRule
func (r firewallRules) Len() int { return len(r) }
func (r firewallRules) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r firewallRules) Less(i, j int) bool {
// sort order: rule number
return r[i].RuleNumber < r[j].RuleNumber
}
// UnmarshalJSON implements json.Unmarshaller on FirewallRule.
// This is needed because the Vultr API is inconsistent in it's JSON responses.
// Some fields can change type, from JSON number to JSON string and vice-versa.
func (r *FirewallRule) UnmarshalJSON(data []byte) (err error) {
if r == nil {
*r = FirewallRule{}
}
var fields map[string]interface{}
if err := json.Unmarshal(data, &fields); err != nil {
return err
}
value := fmt.Sprintf("%v", fields["rulenumber"])
if len(value) == 0 || value == "<nil>" {
value = "0"
}
number, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
r.RuleNumber = int(number)
value = fmt.Sprintf("%v", fields["subnet_size"])
if len(value) == 0 || value == "<nil>" {
value = "0"
}
subnetSize, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
r.Action = fmt.Sprintf("%v", fields["action"])
r.Protocol = fmt.Sprintf("%v", fields["protocol"])
r.Port = fmt.Sprintf("%v", fields["port"])
subnet := fmt.Sprintf("%v", fields["subnet"])
if subnetSize > 0 && len(subnet) > 0 {
_, r.Network, err = net.ParseCIDR(fmt.Sprintf("%s/%d", subnet, subnetSize))
if err != nil {
return fmt.Errorf("Failed to parse subnet from Vultr API")
}
} else {
_, r.Network, _ = net.ParseCIDR("0.0.0.0/0")
}
return
}
// GetFirewallGroups returns a list of all available firewall groups on Vultr
func (c *Client) GetFirewallGroups() ([]FirewallGroup, error) {
var groupMap map[string]FirewallGroup
if err := c.get(`firewall/group_list`, &groupMap); err != nil {
return nil, err
}
var groupList []FirewallGroup
for _, g := range groupMap {
groupList = append(groupList, g)
}
sort.Sort(firewallGroups(groupList))
return groupList, nil
}
// GetFirewallGroup returns the firewall group with given ID
func (c *Client) GetFirewallGroup(id string) (FirewallGroup, error) {
groups, err := c.GetFirewallGroups()
if err != nil {
return FirewallGroup{}, err
}
for _, g := range groups {
if g.ID == id {
return g, nil
}
}
return FirewallGroup{}, fmt.Errorf("Firewall group with ID %v not found", id)
}
// CreateFirewallGroup creates a new firewall group in Vultr account
func (c *Client) CreateFirewallGroup(description string) (string, error) {
values := url.Values{}
// Optional description
if len(description) > 0 {
values.Add("description", description)
}
var result FirewallGroup
err := c.post(`firewall/group_create`, values, &result)
if err != nil {
return "", err
}
return result.ID, nil
}
// DeleteFirewallGroup deletes an existing firewall group
func (c *Client) DeleteFirewallGroup(groupID string) error {
values := url.Values{
"FIREWALLGROUPID": {groupID},
}
if err := c.post(`firewall/group_delete`, values, nil); err != nil {
return err
}
return nil
}
// SetFirewallGroupDescription sets the description of an existing firewall group
func (c *Client) SetFirewallGroupDescription(groupID, description string) error {
values := url.Values{
"FIREWALLGROUPID": {groupID},
"description": {description},
}
if err := c.post(`firewall/group_set_description`, values, nil); err != nil {
return err
}
return nil
}
// GetFirewallRules returns a list of rules for the given firewall group
func (c *Client) GetFirewallRules(groupID string) ([]FirewallRule, error) {
var ruleMap map[string]FirewallRule
ipTypes := []string{"v4", "v6"}
for _, ipType := range ipTypes {
args := fmt.Sprintf("direction=in&FIREWALLGROUPID=%s&ip_type=%s",
groupID, ipType)
if err := c.get(`firewall/rule_list?`+args, &ruleMap); err != nil {
return nil, err
}
}
var ruleList []FirewallRule
for _, r := range ruleMap {
ruleList = append(ruleList, r)
}
sort.Sort(firewallRules(ruleList))
return ruleList, nil
}
// CreateFirewallRule creates a new firewall rule in Vultr account.
// groupID is the ID of the firewall group to create the rule in
// protocol must be one of: "icmp", "tcp", "udp", "gre"
// port can be a port number or colon separated port range (TCP/UDP only)
func (c *Client) CreateFirewallRule(groupID, protocol, port string,
network *net.IPNet) (int, error) {
ip := network.IP.String()
maskBits, _ := network.Mask.Size()
if ip == "<nil>" {
return 0, fmt.Errorf("Invalid network")
}
var ipType string
if network.IP.To4() != nil {
ipType = "v4"
} else {
ipType = "v6"
}
values := url.Values{
"FIREWALLGROUPID": {groupID},
// possible values: "in"
"direction": {"in"},
// possible values: "icmp", "tcp", "udp", "gre"
"protocol": {protocol},
// possible values: "v4", "v6"
"ip_type": {ipType},
// IP address representing a subnet
"subnet": {ip},
// IP prefix size in bits
"subnet_size": {fmt.Sprintf("%v", maskBits)},
}
if len(port) > 0 {
values.Add("port", port)
}
var result FirewallRule
err := c.post(`firewall/rule_create`, values, &result)
if err != nil {
return 0, err
}
return result.RuleNumber, nil
}
// DeleteFirewallRule deletes an existing firewall rule
func (c *Client) DeleteFirewallRule(ruleNumber int, groupID string) error {
values := url.Values{
"FIREWALLGROUPID": {groupID},
"rulenumber": {fmt.Sprintf("%v", ruleNumber)},
}
if err := c.post(`firewall/rule_delete`, values, nil); err != nil {
return err
}
return nil
}

View file

@ -1,6 +1,7 @@
package lib package lib
import ( import (
"fmt"
"net/url" "net/url"
"sort" "sort"
) )
@ -78,6 +79,32 @@ func (c *Client) ListIPv4(id string) (list []IPv4, err error) {
return list, nil return list, nil
} }
// CreateIPv4 creates an IPv4 address and attaches it to a virtual machine
func (c *Client) CreateIPv4(id string, reboot bool) error {
values := url.Values{
"SUBID": {id},
"reboot": {fmt.Sprintf("%t", reboot)},
}
if err := c.post(`server/create_ipv4`, values, nil); err != nil {
return err
}
return nil
}
// DeleteIPv4 deletes an IPv4 address and detaches it from a virtual machine
func (c *Client) DeleteIPv4(id, ip string) error {
values := url.Values{
"SUBID": {id},
"ip": {ip},
}
if err := c.post(`server/destroy_ipv4`, values, nil); err != nil {
return err
}
return nil
}
// ListIPv6 lists the IPv4 information of a virtual machine // ListIPv6 lists the IPv4 information of a virtual machine
func (c *Client) ListIPv6(id string) (list []IPv6, err error) { func (c *Client) ListIPv6(id string) (list []IPv6, err error) {
var ipMap map[string][]IPv6 var ipMap map[string][]IPv6

View file

@ -40,6 +40,7 @@ type Server struct {
Tag string `json:"tag"` Tag string `json:"tag"`
OSID string `json:"OSID"` OSID string `json:"OSID"`
AppID string `json:"APPID"` AppID string `json:"APPID"`
FirewallGroupID string `json:"FIREWALLGROUPID"`
} }
// ServerOptions are optional parameters to be used during server creation // ServerOptions are optional parameters to be used during server creation
@ -50,6 +51,7 @@ type ServerOptions struct {
UserData string UserData string
Snapshot string Snapshot string
SSHKey string SSHKey string
ReservedIP string
IPV6 bool IPV6 bool
PrivateNetworking bool PrivateNetworking bool
AutoBackups bool AutoBackups bool
@ -57,6 +59,7 @@ type ServerOptions struct {
Hostname string Hostname string
Tag string Tag string
AppID string AppID string
FirewallGroupID string
} }
type servers []Server type servers []Server
@ -166,11 +169,17 @@ func (s *Server) UnmarshalJSON(data []byte) (err error) {
s.OSID = value s.OSID = value
value = fmt.Sprintf("%v", fields["APPID"]) value = fmt.Sprintf("%v", fields["APPID"])
if value == "<nil>" { if value == "<nil>" || value == "0" {
value = "" value = ""
} }
s.AppID = value s.AppID = value
value = fmt.Sprintf("%v", fields["FIREWALLGROUPID"])
if value == "<nil>" || value == "0" {
value = ""
}
s.FirewallGroupID = value
s.ID = fmt.Sprintf("%v", fields["SUBID"]) s.ID = fmt.Sprintf("%v", fields["SUBID"])
s.Name = fmt.Sprintf("%v", fields["label"]) s.Name = fmt.Sprintf("%v", fields["label"])
s.OS = fmt.Sprintf("%v", fields["os"]) s.OS = fmt.Sprintf("%v", fields["os"])
@ -280,6 +289,10 @@ func (c *Client) CreateServer(name string, regionID, planID, osID int, options *
values.Add("SSHKEYID", options.SSHKey) values.Add("SSHKEYID", options.SSHKey)
} }
if options.ReservedIP != "" {
values.Add("reserved_ip_v4", options.ReservedIP)
}
values.Add("enable_ipv6", "no") values.Add("enable_ipv6", "no")
if options.IPV6 { if options.IPV6 {
values.Set("enable_ipv6", "yes") values.Set("enable_ipv6", "yes")
@ -311,6 +324,10 @@ func (c *Client) CreateServer(name string, regionID, planID, osID int, options *
if options.AppID != "" { if options.AppID != "" {
values.Add("APPID", options.AppID) values.Add("APPID", options.AppID)
} }
if options.FirewallGroupID != "" {
values.Add("FIREWALLGROUPID", options.FirewallGroupID)
}
} }
var server Server var server Server
@ -337,6 +354,19 @@ func (c *Client) RenameServer(id, name string) error {
return nil return nil
} }
// TagServer replaces the tag on an existing virtual machine
func (c *Client) TagServer(id, tag string) error {
values := url.Values{
"SUBID": {id},
"tag": {tag},
}
if err := c.post(`server/tag_set`, values, nil); err != nil {
return err
}
return nil
}
// StartServer starts an existing virtual machine // StartServer starts an existing virtual machine
func (c *Client) StartServer(id string) error { func (c *Client) StartServer(id string) error {
values := url.Values{ values := url.Values{
@ -457,6 +487,24 @@ func (c *Client) DeleteServer(id string) error {
return nil return nil
} }
// SetFirewallGroup adds a virtual machine to a firewall group
func (c *Client) SetFirewallGroup(id, firewallgroup string) error {
values := url.Values{
"SUBID": {id},
"FIREWALLGROUPID": {firewallgroup},
}
if err := c.post(`server/firewall_group_set`, values, nil); err != nil {
return err
}
return nil
}
// UnsetFirewallGroup removes a virtual machine from a firewall group
func (c *Client) UnsetFirewallGroup(id string) error {
return c.SetFirewallGroup(id, "0")
}
// BandwidthOfServer retrieves the bandwidth used by a virtual machine // BandwidthOfServer retrieves the bandwidth used by a virtual machine
func (c *Client) BandwidthOfServer(id string) (bandwidth []map[string]string, err error) { func (c *Client) BandwidthOfServer(id string) (bandwidth []map[string]string, err error) {
var bandwidthMap map[string][][]string var bandwidthMap map[string][][]string

View file

@ -15,18 +15,18 @@ type DomainsService struct {
} }
type DomainInfo struct { type DomainInfo struct {
DomainTotal int `json:"domain_total,omitempty"` DomainTotal int `json:"domain_total,omitempty"`
AllTotal int `json:"all_total,omitempty"` AllTotal int `json:"all_total,omitempty"`
MineTotal int `json:"mine_total,omitempty"` MineTotal int `json:"mine_total,omitempty"`
ShareTotal int `json:"share_total,omitempty"` ShareTotal string `json:"share_total,omitempty"`
VipTotal int `json:"vip_total,omitempty"` VipTotal int `json:"vip_total,omitempty"`
IsMarkTotal int `json:"ismark_total,omitempty"` IsMarkTotal int `json:"ismark_total,omitempty"`
PauseTotal int `json:"pause_total,omitempty"` PauseTotal int `json:"pause_total,omitempty"`
ErrorTotal int `json:"error_total,omitempty"` ErrorTotal int `json:"error_total,omitempty"`
LockTotal int `json:"lock_total,omitempty"` LockTotal int `json:"lock_total,omitempty"`
SpamTotal int `json:"spam_total,omitempty"` SpamTotal int `json:"spam_total,omitempty"`
VipExpire int `json:"vip_expire,omitempty"` VipExpire int `json:"vip_expire,omitempty"`
ShareOutTotal int `json:"share_out_total,omitempty"` ShareOutTotal int `json:"share_out_total,omitempty"`
} }
type Domain struct { type Domain struct {

File diff suppressed because it is too large Load diff

View file

@ -10,9 +10,10 @@ func (exo *Client) CreateDomain(name string) (*DNSDomain, error) {
var hdr = make(http.Header) var hdr = make(http.Header)
var domain DNSDomainCreateRequest var domain DNSDomainCreateRequest
hdr.Add("X-DNS-TOKEN", exo.apiKey + ":" + exo.apiSecret) hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret)
domain.Domain.Name = name domain.Domain.Name = name
m, err := json.Marshal(domain); if err != nil { m, err := json.Marshal(domain)
if err != nil {
return nil, err return nil, err
} }
@ -32,10 +33,10 @@ func (exo *Client) CreateDomain(name string) (*DNSDomain, error) {
func (exo *Client) GetDomain(name string) (*DNSDomain, error) { func (exo *Client) GetDomain(name string) (*DNSDomain, error) {
var hdr = make(http.Header) var hdr = make(http.Header)
hdr.Add("X-DNS-TOKEN", exo.apiKey + ":" + exo.apiSecret) hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret)
hdr.Add("Accept", "application/json") hdr.Add("Accept", "application/json")
resp, err := exo.DetailedRequest("/v1/domains/" + name, "", "GET", hdr) resp, err := exo.DetailedRequest("/v1/domains/"+name, "", "GET", hdr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -48,12 +49,12 @@ func (exo *Client) GetDomain(name string) (*DNSDomain, error) {
return d, nil return d, nil
} }
func (exo *Client) DeleteDomain(name string) (error) { func (exo *Client) DeleteDomain(name string) error {
var hdr = make(http.Header) var hdr = make(http.Header)
hdr.Add("X-DNS-TOKEN", exo.apiKey + ":" + exo.apiSecret) hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret)
hdr.Add("Accept", "application/json") hdr.Add("Accept", "application/json")
_, err := exo.DetailedRequest("/v1/domains/" + name, "", "DELETE", hdr) _, err := exo.DetailedRequest("/v1/domains/"+name, "", "DELETE", hdr)
if err != nil { if err != nil {
return err return err
} }
@ -63,10 +64,10 @@ func (exo *Client) DeleteDomain(name string) (error) {
func (exo *Client) GetRecords(name string) ([]*DNSRecordResponse, error) { func (exo *Client) GetRecords(name string) ([]*DNSRecordResponse, error) {
var hdr = make(http.Header) var hdr = make(http.Header)
hdr.Add("X-DNS-TOKEN", exo.apiKey + ":" + exo.apiSecret) hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret)
hdr.Add("Accept", "application/json") hdr.Add("Accept", "application/json")
resp, err := exo.DetailedRequest("/v1/domains/" + name + "/records", "", "GET", hdr) resp, err := exo.DetailedRequest("/v1/domains/"+name+"/records", "", "GET", hdr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -79,20 +80,21 @@ func (exo *Client) GetRecords(name string) ([]*DNSRecordResponse, error) {
return r, nil return r, nil
} }
func(exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecordResponse, error) { func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecordResponse, error) {
var hdr = make(http.Header) var hdr = make(http.Header)
hdr.Add("X-DNS-TOKEN", exo.apiKey + ":" + exo.apiSecret) hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret)
hdr.Add("Accept", "application/json") hdr.Add("Accept", "application/json")
hdr.Add("Content-Type", "application/json") hdr.Add("Content-Type", "application/json")
var rr DNSRecordResponse var rr DNSRecordResponse
rr.Record = rec rr.Record = rec
body, err := json.Marshal(rr); if err != nil { body, err := json.Marshal(rr)
if err != nil {
return nil, err return nil, err
} }
resp, err := exo.DetailedRequest("/v1/domains/" + name + "/records", string(body), "POST", hdr) resp, err := exo.DetailedRequest("/v1/domains/"+name+"/records", string(body), "POST", hdr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -105,21 +107,22 @@ func(exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecordResponse,
return r, nil return r, nil
} }
func(exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecordResponse, error) { func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecordResponse, error) {
var hdr = make(http.Header) var hdr = make(http.Header)
id := strconv.FormatInt(rec.Id, 10) id := strconv.FormatInt(rec.Id, 10)
hdr.Add("X-DNS-TOKEN", exo.apiKey + ":" + exo.apiSecret) hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret)
hdr.Add("Accept", "application/json") hdr.Add("Accept", "application/json")
hdr.Add("Content-Type", "application/json") hdr.Add("Content-Type", "application/json")
var rr DNSRecordResponse var rr DNSRecordResponse
rr.Record = rec rr.Record = rec
body, err := json.Marshal(rr); if err != nil { body, err := json.Marshal(rr)
if err != nil {
return nil, err return nil, err
} }
resp, err := exo.DetailedRequest("/v1/domains/" + name + "/records/" + id, resp, err := exo.DetailedRequest("/v1/domains/"+name+"/records/"+id,
string(body), "PUT", hdr) string(body), "PUT", hdr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -133,14 +136,14 @@ func(exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecordResponse,
return r, nil return r, nil
} }
func(exo *Client) DeleteRecord(name string, rec DNSRecord) (error) { func (exo *Client) DeleteRecord(name string, rec DNSRecord) error {
var hdr = make(http.Header) var hdr = make(http.Header)
id := strconv.FormatInt(rec.Id, 10) id := strconv.FormatInt(rec.Id, 10)
hdr.Add("X-DNS-TOKEN", exo.apiKey + ":" + exo.apiSecret) hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret)
hdr.Add("Accept", "application/json") hdr.Add("Accept", "application/json")
hdr.Add("Content-Type", "application/json") hdr.Add("Content-Type", "application/json")
_, err := exo.DetailedRequest("/v1/domains/" + name + "/records/" + id, _, err := exo.DetailedRequest("/v1/domains/"+name+"/records/"+id,
"", "DELETE", hdr) "", "DELETE", hdr)
if err != nil { if err != nil {
return err return err

View file

@ -11,17 +11,17 @@ func (exo *Client) CreateEgressRule(rule SecurityGroupRule) (*AuthorizeSecurityG
params := url.Values{} params := url.Values{}
params.Set("securitygroupid", rule.SecurityGroupId) params.Set("securitygroupid", rule.SecurityGroupId)
if rule.Cidr != "" { if rule.Cidr != "" {
params.Set("cidrlist", rule.Cidr) params.Set("cidrlist", rule.Cidr)
} else if len(rule.UserSecurityGroupList) > 0 { } else if len(rule.UserSecurityGroupList) > 0 {
usg,err := json.Marshal(rule.UserSecurityGroupList) usg, err := json.Marshal(rule.UserSecurityGroupList)
if err != nil { if err != nil {
return nil, err return nil, err
} }
params.Set("usersecuritygrouplist", string(usg)) params.Set("usersecuritygrouplist", string(usg))
} else { } else {
return nil, fmt.Errorf("No Egress rule CIDR or Security Group List provided") return nil, fmt.Errorf("No Egress rule CIDR or Security Group List provided")
} }
params.Set("protocol", rule.Protocol) params.Set("protocol", rule.Protocol)
@ -53,17 +53,17 @@ func (exo *Client) CreateIngressRule(rule SecurityGroupRule) (*AuthorizeSecurity
params := url.Values{} params := url.Values{}
params.Set("securitygroupid", rule.SecurityGroupId) params.Set("securitygroupid", rule.SecurityGroupId)
if rule.Cidr != "" { if rule.Cidr != "" {
params.Set("cidrlist", rule.Cidr) params.Set("cidrlist", rule.Cidr)
} else if len(rule.UserSecurityGroupList) >0 { } else if len(rule.UserSecurityGroupList) > 0 {
for i := 0; i < len(rule.UserSecurityGroupList); i++ { for i := 0; i < len(rule.UserSecurityGroupList); i++ {
params.Set(fmt.Sprintf("usersecuritygrouplist[%d].account", i), rule.UserSecurityGroupList[i].Account) params.Set(fmt.Sprintf("usersecuritygrouplist[%d].account", i), rule.UserSecurityGroupList[i].Account)
params.Set(fmt.Sprintf("usersecuritygrouplist[%d].group", i), rule.UserSecurityGroupList[i].Group) params.Set(fmt.Sprintf("usersecuritygrouplist[%d].group", i), rule.UserSecurityGroupList[i].Group)
} }
} else { } else {
return nil, fmt.Errorf("No Ingress rule CIDR or Security Group List provided") return nil, fmt.Errorf("No Ingress rule CIDR or Security Group List provided")
} }
params.Set("protocol", rule.Protocol) params.Set("protocol", rule.Protocol)
@ -77,7 +77,7 @@ func (exo *Client) CreateIngressRule(rule SecurityGroupRule) (*AuthorizeSecurity
return nil, fmt.Errorf("Invalid Egress rule Protocol: %s", rule.Protocol) return nil, fmt.Errorf("Invalid Egress rule Protocol: %s", rule.Protocol)
} }
fmt.Printf("## params: %+v\n", params) fmt.Printf("## params: %+v\n", params)
resp, err := exo.Request("authorizeSecurityGroupIngress", params) resp, err := exo.Request("authorizeSecurityGroupIngress", params)
@ -130,11 +130,12 @@ func (exo *Client) CreateSecurityGroupWithRules(name string, ingress []SecurityG
return &r.Wrapped, nil return &r.Wrapped, nil
} }
func (exo *Client) DeleteSecurityGroup(name string) (error) { func (exo *Client) DeleteSecurityGroup(name string) error {
params := url.Values{} params := url.Values{}
params.Set("name", name) params.Set("name", name)
resp, err := exo.Request("deleteSecurityGroup", params); if err != nil { resp, err := exo.Request("deleteSecurityGroup", params)
if err != nil {
return err return err
} }

View file

@ -9,8 +9,8 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"sort" "sort"
"strings"
) )
func csQuotePlus(s string) string { func csQuotePlus(s string) string {
@ -89,11 +89,11 @@ func (exo *Client) Request(command string, params url.Values) (json.RawMessage,
params.Set("command", command) params.Set("command", command)
params.Set("response", "json") params.Set("response", "json")
for k, _ := range(params) { for k, _ := range params {
keys = append(keys, k) keys = append(keys, k)
} }
sort.Strings(keys) sort.Strings(keys)
for _, k := range(keys) { for _, k := range keys {
arg := k + "=" + csEncode(params[k][0]) arg := k + "=" + csEncode(params[k][0])
unencoded = append(unencoded, arg) unencoded = append(unencoded, arg)
} }
@ -113,17 +113,18 @@ func (exo *Client) Request(command string, params url.Values) (json.RawMessage,
return exo.ParseResponse(resp) return exo.ParseResponse(resp)
} }
func (exo *Client) DetailedRequest(uri string, params string, method string, header http.Header) (json.RawMessage, error) { func (exo *Client) DetailedRequest(uri string, params string, method string, header http.Header) (json.RawMessage, error) {
url := exo.endpoint + uri url := exo.endpoint + uri
req, err := http.NewRequest(method, url, strings.NewReader(params)); if err != nil { req, err := http.NewRequest(method, url, strings.NewReader(params))
if err != nil {
return nil, err return nil, err
} }
req.Header = header req.Header = header
response, err := exo.client.Do(req); if err != nil { response, err := exo.client.Do(req)
if err != nil {
return nil, err return nil, err
} }

View file

@ -31,12 +31,14 @@ func (exo *Client) GetSecurityGroups() (map[string]SecurityGroup, error) {
func (exo *Client) GetSecurityGroupId(name string) (string, error) { func (exo *Client) GetSecurityGroupId(name string) (string, error) {
params := url.Values{} params := url.Values{}
resp, err := exo.Request("listSecurityGroups", params); if err != nil { resp, err := exo.Request("listSecurityGroups", params)
if err != nil {
return "", err return "", err
} }
var r ListSecurityGroupsResponse var r ListSecurityGroupsResponse
err = json.Unmarshal(resp, &r); if err != nil { err = json.Unmarshal(resp, &r)
if err != nil {
return "", err return "", err
} }
@ -142,7 +144,7 @@ func (exo *Client) GetImages() (map[string]map[int]string, error) {
images = make(map[string]map[int]string) images = make(map[string]map[int]string)
params := url.Values{} params := url.Values{}
params.Set("templatefilter", "executable") params.Set("templatefilter", "featured")
resp, err := exo.Request("listTemplates", params) resp, err := exo.Request("listTemplates", params)
@ -191,7 +193,7 @@ func (exo *Client) GetTopology() (*Topology, error) {
return nil, err return nil, err
} }
groups := make(map[string]string) groups := make(map[string]string)
for k,v := range securityGroups { for k, v := range securityGroups {
groups[k] = v.Id groups[k] = v.Id
} }

View file

@ -33,18 +33,18 @@ type Topology struct {
} }
type SecurityGroupRule struct { type SecurityGroupRule struct {
Cidr string Cidr string
IcmpType int IcmpType int
IcmpCode int IcmpCode int
Port int Port int
Protocol string Protocol string
SecurityGroupId string SecurityGroupId string
UserSecurityGroupList []UserSecurityGroup `json:"usersecuritygrouplist,omitempty"` UserSecurityGroupList []UserSecurityGroup `json:"usersecuritygrouplist,omitempty"`
} }
type UserSecurityGroup struct { type UserSecurityGroup struct {
Group string `json:"group,omitempty"` Group string `json:"group,omitempty"`
Account string `json:"account,omitempty"` Account string `json:"account,omitempty"`
} }
type MachineProfile struct { type MachineProfile struct {
@ -390,39 +390,39 @@ type VirtualMachine struct {
Networkkbsread int64 `json:"networkkbsread,omitempty"` Networkkbsread int64 `json:"networkkbsread,omitempty"`
Networkkbswrite int64 `json:"networkkbswrite,omitempty"` Networkkbswrite int64 `json:"networkkbswrite,omitempty"`
Nic []struct { Nic []struct {
Broadcasturi string `json:"broadcasturi,omitempty"` Broadcasturi string `json:"broadcasturi,omitempty"`
Gateway string `json:"gateway,omitempty"` Gateway string `json:"gateway,omitempty"`
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Ip6address string `json:"ip6address,omitempty"` Ip6address string `json:"ip6address,omitempty"`
Ip6cidr string `json:"ip6cidr,omitempty"` Ip6cidr string `json:"ip6cidr,omitempty"`
Ip6gateway string `json:"ip6gateway,omitempty"` Ip6gateway string `json:"ip6gateway,omitempty"`
Ipaddress string `json:"ipaddress,omitempty"` Ipaddress string `json:"ipaddress,omitempty"`
Isdefault bool `json:"isdefault,omitempty"` Isdefault bool `json:"isdefault,omitempty"`
Isolationuri string `json:"isolationuri,omitempty"` Isolationuri string `json:"isolationuri,omitempty"`
Macaddress string `json:"macaddress,omitempty"` Macaddress string `json:"macaddress,omitempty"`
Netmask string `json:"netmask,omitempty"` Netmask string `json:"netmask,omitempty"`
Networkid string `json:"networkid,omitempty"` Networkid string `json:"networkid,omitempty"`
Networkname string `json:"networkname,omitempty"` Networkname string `json:"networkname,omitempty"`
Secondaryip []struct { Secondaryip []struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
IpAddress string `json:"ipaddress,omitempty"` IpAddress string `json:"ipaddress,omitempty"`
} `json:"secondaryip,omitempty"` } `json:"secondaryip,omitempty"`
Traffictype string `json:"traffictype,omitempty"` Traffictype string `json:"traffictype,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
} `json:"nic,omitempty"` } `json:"nic,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
Passwordenabled bool `json:"passwordenabled,omitempty"` Passwordenabled bool `json:"passwordenabled,omitempty"`
Project string `json:"project,omitempty"` Project string `json:"project,omitempty"`
Projectid string `json:"projectid,omitempty"` Projectid string `json:"projectid,omitempty"`
Publicip string `json:"publicip,omitempty"` Publicip string `json:"publicip,omitempty"`
Publicipid string `json:"publicipid,omitempty"` Publicipid string `json:"publicipid,omitempty"`
Rootdeviceid int64 `json:"rootdeviceid,omitempty"` Rootdeviceid int64 `json:"rootdeviceid,omitempty"`
Rootdevicetype string `json:"rootdevicetype,omitempty"` Rootdevicetype string `json:"rootdevicetype,omitempty"`
SecurityGroups []struct { SecurityGroups []struct {
Account string `json:"account,omitempty"` Account string `json:"account,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Name string `json:"name,omitemtpy"` Name string `json:"name,omitemtpy"`
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
} `json:"securitygroup,omitempty"` } `json:"securitygroup,omitempty"`
Serviceofferingid string `json:"serviceofferingid,omitempty"` Serviceofferingid string `json:"serviceofferingid,omitempty"`
@ -465,11 +465,11 @@ type RemoveIpFromNicResponse struct {
} }
type AddIpToNicResponse struct { type AddIpToNicResponse struct {
Id string `json:"id"` Id string `json:"id"`
IpAddress string `json:"ipaddress"` IpAddress string `json:"ipaddress"`
NetworkId string `json:"networkid"` NetworkId string `json:"networkid"`
NicId string `json:"nicid"` NicId string `json:"nicid"`
VmId string `json:"virtualmachineid"` VmId string `json:"virtualmachineid"`
} }
type CreateAffinityGroupResponse struct { type CreateAffinityGroupResponse struct {

View file

@ -145,7 +145,7 @@ func (exo *Client) GetVirtualMachine(id string) (*VirtualMachine, error) {
func (exo *Client) ListVirtualMachines() ([]*VirtualMachine, error) { func (exo *Client) ListVirtualMachines() ([]*VirtualMachine, error) {
resp, err := exo.Request("listVirtualMachines", url.Values{}) resp, err := exo.Request("listVirtualMachines", url.Values{})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -176,7 +176,7 @@ recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within the same "printed page" as the copyright notice for easier identification within
third-party archives. third-party archives.
Copyright [yyyy] [name of copyright owner] Copyright 2014 Unknwon
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

391
vendor/github.com/go-ini/ini/file.go generated vendored Normal file
View file

@ -0,0 +1,391 @@
// Copyright 2017 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"sync"
)
// File represents a combination of a or more INI file(s) in memory.
type File struct {
options LoadOptions
dataSources []dataSource
// Should make things safe, but sometimes doesn't matter.
BlockMode bool
lock sync.RWMutex
// To keep data in order.
sectionList []string
// Actual data is stored here.
sections map[string]*Section
NameMapper
ValueMapper
}
// newFile initializes File object with given data sources.
func newFile(dataSources []dataSource, opts LoadOptions) *File {
return &File{
BlockMode: true,
dataSources: dataSources,
sections: make(map[string]*Section),
sectionList: make([]string, 0, 10),
options: opts,
}
}
// Empty returns an empty file object.
func Empty() *File {
// Ignore error here, we sure our data is good.
f, _ := Load([]byte(""))
return f
}
// NewSection creates a new section.
func (f *File) NewSection(name string) (*Section, error) {
if len(name) == 0 {
return nil, errors.New("error creating new section: empty section name")
} else if f.options.Insensitive && name != DEFAULT_SECTION {
name = strings.ToLower(name)
}
if f.BlockMode {
f.lock.Lock()
defer f.lock.Unlock()
}
if inSlice(name, f.sectionList) {
return f.sections[name], nil
}
f.sectionList = append(f.sectionList, name)
f.sections[name] = newSection(f, name)
return f.sections[name], nil
}
// NewRawSection creates a new section with an unparseable body.
func (f *File) NewRawSection(name, body string) (*Section, error) {
section, err := f.NewSection(name)
if err != nil {
return nil, err
}
section.isRawSection = true
section.rawBody = body
return section, nil
}
// NewSections creates a list of sections.
func (f *File) NewSections(names ...string) (err error) {
for _, name := range names {
if _, err = f.NewSection(name); err != nil {
return err
}
}
return nil
}
// GetSection returns section by given name.
func (f *File) GetSection(name string) (*Section, error) {
if len(name) == 0 {
name = DEFAULT_SECTION
} else if f.options.Insensitive {
name = strings.ToLower(name)
}
if f.BlockMode {
f.lock.RLock()
defer f.lock.RUnlock()
}
sec := f.sections[name]
if sec == nil {
return nil, fmt.Errorf("section '%s' does not exist", name)
}
return sec, nil
}
// Section assumes named section exists and returns a zero-value when not.
func (f *File) Section(name string) *Section {
sec, err := f.GetSection(name)
if err != nil {
// Note: It's OK here because the only possible error is empty section name,
// but if it's empty, this piece of code won't be executed.
sec, _ = f.NewSection(name)
return sec
}
return sec
}
// Section returns list of Section.
func (f *File) Sections() []*Section {
sections := make([]*Section, len(f.sectionList))
for i := range f.sectionList {
sections[i] = f.Section(f.sectionList[i])
}
return sections
}
// ChildSections returns a list of child sections of given section name.
func (f *File) ChildSections(name string) []*Section {
return f.Section(name).ChildSections()
}
// SectionStrings returns list of section names.
func (f *File) SectionStrings() []string {
list := make([]string, len(f.sectionList))
copy(list, f.sectionList)
return list
}
// DeleteSection deletes a section.
func (f *File) DeleteSection(name string) {
if f.BlockMode {
f.lock.Lock()
defer f.lock.Unlock()
}
if len(name) == 0 {
name = DEFAULT_SECTION
}
for i, s := range f.sectionList {
if s == name {
f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
delete(f.sections, name)
return
}
}
}
func (f *File) reload(s dataSource) error {
r, err := s.ReadCloser()
if err != nil {
return err
}
defer r.Close()
return f.parse(r)
}
// Reload reloads and parses all data sources.
func (f *File) Reload() (err error) {
for _, s := range f.dataSources {
if err = f.reload(s); err != nil {
// In loose mode, we create an empty default section for nonexistent files.
if os.IsNotExist(err) && f.options.Loose {
f.parse(bytes.NewBuffer(nil))
continue
}
return err
}
}
return nil
}
// Append appends one or more data sources and reloads automatically.
func (f *File) Append(source interface{}, others ...interface{}) error {
ds, err := parseDataSource(source)
if err != nil {
return err
}
f.dataSources = append(f.dataSources, ds)
for _, s := range others {
ds, err = parseDataSource(s)
if err != nil {
return err
}
f.dataSources = append(f.dataSources, ds)
}
return f.Reload()
}
func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
equalSign := "="
if PrettyFormat {
equalSign = " = "
}
// Use buffer to make sure target is safe until finish encoding.
buf := bytes.NewBuffer(nil)
for i, sname := range f.sectionList {
sec := f.Section(sname)
if len(sec.Comment) > 0 {
if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
sec.Comment = "; " + sec.Comment
} else {
sec.Comment = sec.Comment[:1] + " " + strings.TrimSpace(sec.Comment[1:])
}
if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil {
return nil, err
}
}
if i > 0 || DefaultHeader {
if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
return nil, err
}
} else {
// Write nothing if default section is empty
if len(sec.keyList) == 0 {
continue
}
}
if sec.isRawSection {
if _, err := buf.WriteString(sec.rawBody); err != nil {
return nil, err
}
if PrettySection {
// Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err
}
}
continue
}
// Count and generate alignment length and buffer spaces using the
// longest key. Keys may be modifed if they contain certain characters so
// we need to take that into account in our calculation.
alignLength := 0
if PrettyFormat {
for _, kname := range sec.keyList {
keyLength := len(kname)
// First case will surround key by ` and second by """
if strings.ContainsAny(kname, "\"=:") {
keyLength += 2
} else if strings.Contains(kname, "`") {
keyLength += 6
}
if keyLength > alignLength {
alignLength = keyLength
}
}
}
alignSpaces := bytes.Repeat([]byte(" "), alignLength)
KEY_LIST:
for _, kname := range sec.keyList {
key := sec.Key(kname)
if len(key.Comment) > 0 {
if len(indent) > 0 && sname != DEFAULT_SECTION {
buf.WriteString(indent)
}
if key.Comment[0] != '#' && key.Comment[0] != ';' {
key.Comment = "; " + key.Comment
} else {
key.Comment = key.Comment[:1] + " " + strings.TrimSpace(key.Comment[1:])
}
if _, err := buf.WriteString(key.Comment + LineBreak); err != nil {
return nil, err
}
}
if len(indent) > 0 && sname != DEFAULT_SECTION {
buf.WriteString(indent)
}
switch {
case key.isAutoIncrement:
kname = "-"
case strings.ContainsAny(kname, "\"=:"):
kname = "`" + kname + "`"
case strings.Contains(kname, "`"):
kname = `"""` + kname + `"""`
}
for _, val := range key.ValueWithShadows() {
if _, err := buf.WriteString(kname); err != nil {
return nil, err
}
if key.isBooleanType {
if kname != sec.keyList[len(sec.keyList)-1] {
buf.WriteString(LineBreak)
}
continue KEY_LIST
}
// Write out alignment spaces before "=" sign
if PrettyFormat {
buf.Write(alignSpaces[:alignLength-len(kname)])
}
// In case key value contains "\n", "`", "\"", "#" or ";"
if strings.ContainsAny(val, "\n`") {
val = `"""` + val + `"""`
} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
val = "`" + val + "`"
}
if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
return nil, err
}
}
}
if PrettySection {
// Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err
}
}
}
return buf, nil
}
// WriteToIndent writes content into io.Writer with given indention.
// If PrettyFormat has been set to be true,
// it will align "=" sign with spaces under each section.
func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) {
buf, err := f.writeToBuffer(indent)
if err != nil {
return 0, err
}
return buf.WriteTo(w)
}
// WriteTo writes file content into io.Writer.
func (f *File) WriteTo(w io.Writer) (int64, error) {
return f.WriteToIndent(w, "")
}
// SaveToIndent writes content to file system with given value indention.
func (f *File) SaveToIndent(filename, indent string) error {
// Note: Because we are truncating with os.Create,
// so it's safer to save to a temporary file location and rename afte done.
buf, err := f.writeToBuffer(indent)
if err != nil {
return err
}
return ioutil.WriteFile(filename, buf.Bytes(), 0666)
}
// SaveTo writes content to file system.
func (f *File) SaveTo(filename string) error {
return f.SaveToIndent(filename, "")
}

382
vendor/github.com/go-ini/ini/ini.go generated vendored
View file

@ -17,17 +17,12 @@ package ini
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"regexp" "regexp"
"runtime" "runtime"
"strconv"
"strings"
"sync"
"time"
) )
const ( const (
@ -37,7 +32,7 @@ const (
// Maximum allowed depth when recursively substituing variable names. // Maximum allowed depth when recursively substituing variable names.
_DEPTH_VALUES = 99 _DEPTH_VALUES = 99
_VERSION = "1.27.0" _VERSION = "1.30.2"
) )
// Version returns current package version literal. // Version returns current package version literal.
@ -60,6 +55,9 @@ var (
// Explicitly write DEFAULT section header // Explicitly write DEFAULT section header
DefaultHeader = false DefaultHeader = false
// Indicate whether to put a line between sections
PrettySection = true
) )
func init() { func init() {
@ -91,18 +89,6 @@ func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
return os.Open(s.name) return os.Open(s.name)
} }
type bytesReadCloser struct {
reader io.Reader
}
func (rc *bytesReadCloser) Read(p []byte) (n int, err error) {
return rc.reader.Read(p)
}
func (rc *bytesReadCloser) Close() error {
return nil
}
// sourceData represents an object that contains content in memory. // sourceData represents an object that contains content in memory.
type sourceData struct { type sourceData struct {
data []byte data []byte
@ -121,38 +107,6 @@ func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
return s.reader, nil return s.reader, nil
} }
// File represents a combination of a or more INI file(s) in memory.
type File struct {
// Should make things safe, but sometimes doesn't matter.
BlockMode bool
// Make sure data is safe in multiple goroutines.
lock sync.RWMutex
// Allow combination of multiple data sources.
dataSources []dataSource
// Actual data is stored here.
sections map[string]*Section
// To keep data in order.
sectionList []string
options LoadOptions
NameMapper
ValueMapper
}
// newFile initializes File object with given data sources.
func newFile(dataSources []dataSource, opts LoadOptions) *File {
return &File{
BlockMode: true,
dataSources: dataSources,
sections: make(map[string]*Section),
sectionList: make([]string, 0, 10),
options: opts,
}
}
func parseDataSource(source interface{}) (dataSource, error) { func parseDataSource(source interface{}) (dataSource, error) {
switch s := source.(type) { switch s := source.(type) {
case string: case string:
@ -180,6 +134,8 @@ type LoadOptions struct {
AllowBooleanKeys bool AllowBooleanKeys bool
// AllowShadows indicates whether to keep track of keys with same name under same section. // AllowShadows indicates whether to keep track of keys with same name under same section.
AllowShadows bool AllowShadows bool
// UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
UnescapeValueDoubleQuotes bool
// Some INI formats allow group blocks that store a block of raw content that doesn't otherwise // Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
// conform to key/value pairs. Specify the names of those blocks here. // conform to key/value pairs. Specify the names of those blocks here.
UnparseableSections []string UnparseableSections []string
@ -228,329 +184,3 @@ func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
func ShadowLoad(source interface{}, others ...interface{}) (*File, error) { func ShadowLoad(source interface{}, others ...interface{}) (*File, error) {
return LoadSources(LoadOptions{AllowShadows: true}, source, others...) return LoadSources(LoadOptions{AllowShadows: true}, source, others...)
} }
// Empty returns an empty file object.
func Empty() *File {
// Ignore error here, we sure our data is good.
f, _ := Load([]byte(""))
return f
}
// NewSection creates a new section.
func (f *File) NewSection(name string) (*Section, error) {
if len(name) == 0 {
return nil, errors.New("error creating new section: empty section name")
} else if f.options.Insensitive && name != DEFAULT_SECTION {
name = strings.ToLower(name)
}
if f.BlockMode {
f.lock.Lock()
defer f.lock.Unlock()
}
if inSlice(name, f.sectionList) {
return f.sections[name], nil
}
f.sectionList = append(f.sectionList, name)
f.sections[name] = newSection(f, name)
return f.sections[name], nil
}
// NewRawSection creates a new section with an unparseable body.
func (f *File) NewRawSection(name, body string) (*Section, error) {
section, err := f.NewSection(name)
if err != nil {
return nil, err
}
section.isRawSection = true
section.rawBody = body
return section, nil
}
// NewSections creates a list of sections.
func (f *File) NewSections(names ...string) (err error) {
for _, name := range names {
if _, err = f.NewSection(name); err != nil {
return err
}
}
return nil
}
// GetSection returns section by given name.
func (f *File) GetSection(name string) (*Section, error) {
if len(name) == 0 {
name = DEFAULT_SECTION
} else if f.options.Insensitive {
name = strings.ToLower(name)
}
if f.BlockMode {
f.lock.RLock()
defer f.lock.RUnlock()
}
sec := f.sections[name]
if sec == nil {
return nil, fmt.Errorf("section '%s' does not exist", name)
}
return sec, nil
}
// Section assumes named section exists and returns a zero-value when not.
func (f *File) Section(name string) *Section {
sec, err := f.GetSection(name)
if err != nil {
// Note: It's OK here because the only possible error is empty section name,
// but if it's empty, this piece of code won't be executed.
sec, _ = f.NewSection(name)
return sec
}
return sec
}
// Section returns list of Section.
func (f *File) Sections() []*Section {
sections := make([]*Section, len(f.sectionList))
for i := range f.sectionList {
sections[i] = f.Section(f.sectionList[i])
}
return sections
}
// ChildSections returns a list of child sections of given section name.
func (f *File) ChildSections(name string) []*Section {
return f.Section(name).ChildSections()
}
// SectionStrings returns list of section names.
func (f *File) SectionStrings() []string {
list := make([]string, len(f.sectionList))
copy(list, f.sectionList)
return list
}
// DeleteSection deletes a section.
func (f *File) DeleteSection(name string) {
if f.BlockMode {
f.lock.Lock()
defer f.lock.Unlock()
}
if len(name) == 0 {
name = DEFAULT_SECTION
}
for i, s := range f.sectionList {
if s == name {
f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
delete(f.sections, name)
return
}
}
}
func (f *File) reload(s dataSource) error {
r, err := s.ReadCloser()
if err != nil {
return err
}
defer r.Close()
return f.parse(r)
}
// Reload reloads and parses all data sources.
func (f *File) Reload() (err error) {
for _, s := range f.dataSources {
if err = f.reload(s); err != nil {
// In loose mode, we create an empty default section for nonexistent files.
if os.IsNotExist(err) && f.options.Loose {
f.parse(bytes.NewBuffer(nil))
continue
}
return err
}
}
return nil
}
// Append appends one or more data sources and reloads automatically.
func (f *File) Append(source interface{}, others ...interface{}) error {
ds, err := parseDataSource(source)
if err != nil {
return err
}
f.dataSources = append(f.dataSources, ds)
for _, s := range others {
ds, err = parseDataSource(s)
if err != nil {
return err
}
f.dataSources = append(f.dataSources, ds)
}
return f.Reload()
}
// WriteToIndent writes content into io.Writer with given indention.
// If PrettyFormat has been set to be true,
// it will align "=" sign with spaces under each section.
func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
equalSign := "="
if PrettyFormat {
equalSign = " = "
}
// Use buffer to make sure target is safe until finish encoding.
buf := bytes.NewBuffer(nil)
for i, sname := range f.sectionList {
sec := f.Section(sname)
if len(sec.Comment) > 0 {
if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
sec.Comment = "; " + sec.Comment
}
if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil {
return 0, err
}
}
if i > 0 || DefaultHeader {
if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
return 0, err
}
} else {
// Write nothing if default section is empty
if len(sec.keyList) == 0 {
continue
}
}
if sec.isRawSection {
if _, err = buf.WriteString(sec.rawBody); err != nil {
return 0, err
}
continue
}
// Count and generate alignment length and buffer spaces using the
// longest key. Keys may be modifed if they contain certain characters so
// we need to take that into account in our calculation.
alignLength := 0
if PrettyFormat {
for _, kname := range sec.keyList {
keyLength := len(kname)
// First case will surround key by ` and second by """
if strings.ContainsAny(kname, "\"=:") {
keyLength += 2
} else if strings.Contains(kname, "`") {
keyLength += 6
}
if keyLength > alignLength {
alignLength = keyLength
}
}
}
alignSpaces := bytes.Repeat([]byte(" "), alignLength)
KEY_LIST:
for _, kname := range sec.keyList {
key := sec.Key(kname)
if len(key.Comment) > 0 {
if len(indent) > 0 && sname != DEFAULT_SECTION {
buf.WriteString(indent)
}
if key.Comment[0] != '#' && key.Comment[0] != ';' {
key.Comment = "; " + key.Comment
}
if _, err = buf.WriteString(key.Comment + LineBreak); err != nil {
return 0, err
}
}
if len(indent) > 0 && sname != DEFAULT_SECTION {
buf.WriteString(indent)
}
switch {
case key.isAutoIncrement:
kname = "-"
case strings.ContainsAny(kname, "\"=:"):
kname = "`" + kname + "`"
case strings.Contains(kname, "`"):
kname = `"""` + kname + `"""`
}
for _, val := range key.ValueWithShadows() {
if _, err = buf.WriteString(kname); err != nil {
return 0, err
}
if key.isBooleanType {
if kname != sec.keyList[len(sec.keyList)-1] {
buf.WriteString(LineBreak)
}
continue KEY_LIST
}
// Write out alignment spaces before "=" sign
if PrettyFormat {
buf.Write(alignSpaces[:alignLength-len(kname)])
}
// In case key value contains "\n", "`", "\"", "#" or ";"
if strings.ContainsAny(val, "\n`") {
val = `"""` + val + `"""`
} else if strings.ContainsAny(val, "#;") {
val = "`" + val + "`"
}
if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil {
return 0, err
}
}
}
// Put a line between sections
if _, err = buf.WriteString(LineBreak); err != nil {
return 0, err
}
}
return buf.WriteTo(w)
}
// WriteTo writes file content into io.Writer.
func (f *File) WriteTo(w io.Writer) (int64, error) {
return f.WriteToIndent(w, "")
}
// SaveToIndent writes content to file system with given value indention.
func (f *File) SaveToIndent(filename, indent string) error {
// Note: Because we are truncating with os.Create,
// so it's safer to save to a temporary file location and rename afte done.
tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp"
defer os.Remove(tmpPath)
fw, err := os.Create(tmpPath)
if err != nil {
return err
}
if _, err = f.WriteToIndent(fw, indent); err != nil {
fw.Close()
return err
}
fw.Close()
// Remove old file and rename the new one.
os.Remove(filename)
return os.Rename(tmpPath, filename)
}
// SaveTo writes content to file system.
func (f *File) SaveTo(filename string) error {
return f.SaveToIndent(filename, "")
}

40
vendor/github.com/go-ini/ini/key.go generated vendored
View file

@ -15,6 +15,7 @@
package ini package ini
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"strconv" "strconv"
@ -25,6 +26,7 @@ import (
// Key represents a key under a section. // Key represents a key under a section.
type Key struct { type Key struct {
s *Section s *Section
Comment string
name string name string
value string value string
isAutoIncrement bool isAutoIncrement bool
@ -32,8 +34,6 @@ type Key struct {
isShadow bool isShadow bool
shadows []*Key shadows []*Key
Comment string
} }
// newKey simply return a key object with given values. // newKey simply return a key object with given values.
@ -444,11 +444,39 @@ func (k *Key) Strings(delim string) []string {
return []string{} return []string{}
} }
vals := strings.Split(str, delim) runes := []rune(str)
for i := range vals { vals := make([]string, 0, 2)
// vals[i] = k.transformValue(strings.TrimSpace(vals[i])) var buf bytes.Buffer
vals[i] = strings.TrimSpace(vals[i]) escape := false
idx := 0
for {
if escape {
escape = false
if runes[idx] != '\\' && !strings.HasPrefix(string(runes[idx:]), delim) {
buf.WriteRune('\\')
}
buf.WriteRune(runes[idx])
} else {
if runes[idx] == '\\' {
escape = true
} else if strings.HasPrefix(string(runes[idx:]), delim) {
idx += len(delim) - 1
vals = append(vals, strings.TrimSpace(buf.String()))
buf.Reset()
} else {
buf.WriteRune(runes[idx])
}
}
idx += 1
if idx == len(runes) {
break
}
} }
if buf.Len() > 0 {
vals = append(vals, strings.TrimSpace(buf.String()))
}
return vals return vals
} }

View file

@ -189,11 +189,11 @@ func (p *parser) readContinuationLines(val string) (string, error) {
// are quotes \" or \'. // are quotes \" or \'.
// It returns false if any other parts also contain same kind of quotes. // It returns false if any other parts also contain same kind of quotes.
func hasSurroundedQuote(in string, quote byte) bool { func hasSurroundedQuote(in string, quote byte) bool {
return len(in) > 2 && in[0] == quote && in[len(in)-1] == quote && return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote &&
strings.IndexByte(in[1:], quote) == len(in)-2 strings.IndexByte(in[1:], quote) == len(in)-2
} }
func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bool) (string, error) { func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes bool) (string, error) {
line := strings.TrimLeftFunc(string(in), unicode.IsSpace) line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
if len(line) == 0 { if len(line) == 0 {
return "", nil return "", nil
@ -204,6 +204,8 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo
valQuote = `"""` valQuote = `"""`
} else if line[0] == '`' { } else if line[0] == '`' {
valQuote = "`" valQuote = "`"
} else if unescapeValueDoubleQuotes && line[0] == '"' {
valQuote = `"`
} }
if len(valQuote) > 0 { if len(valQuote) > 0 {
@ -214,6 +216,9 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo
return p.readMultilines(line, line[startIdx:], valQuote) return p.readMultilines(line, line[startIdx:], valQuote)
} }
if unescapeValueDoubleQuotes && valQuote == `"` {
return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil
}
return line[startIdx : pos+startIdx], nil return line[startIdx : pos+startIdx], nil
} }
@ -234,7 +239,7 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment bo
} }
} }
// Trim single quotes // Trim single and double quotes
if hasSurroundedQuote(line, '\'') || if hasSurroundedQuote(line, '\'') ||
hasSurroundedQuote(line, '"') { hasSurroundedQuote(line, '"') {
line = line[1 : len(line)-1] line = line[1 : len(line)-1]
@ -321,7 +326,10 @@ func (f *File) parse(reader io.Reader) (err error) {
if err != nil { if err != nil {
// Treat as boolean key when desired, and whole line is key name. // Treat as boolean key when desired, and whole line is key name.
if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys { if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys {
kname, err := p.readValue(line, f.options.IgnoreContinuation, f.options.IgnoreInlineComment) kname, err := p.readValue(line,
f.options.IgnoreContinuation,
f.options.IgnoreInlineComment,
f.options.UnescapeValueDoubleQuotes)
if err != nil { if err != nil {
return err return err
} }
@ -344,7 +352,10 @@ func (f *File) parse(reader io.Reader) (err error) {
p.count++ p.count++
} }
value, err := p.readValue(line[offset:], f.options.IgnoreContinuation, f.options.IgnoreInlineComment) value, err := p.readValue(line[offset:],
f.options.IgnoreContinuation,
f.options.IgnoreInlineComment,
f.options.UnescapeValueDoubleQuotes)
if err != nil { if err != nil {
return err return err
} }

View file

@ -54,6 +54,14 @@ func (s *Section) Body() string {
return strings.TrimSpace(s.rawBody) return strings.TrimSpace(s.rawBody)
} }
// SetBody updates body content only if section is raw.
func (s *Section) SetBody(body string) {
if !s.isRawSection {
return
}
s.rawBody = body
}
// NewKey creates a new key to given section. // NewKey creates a new key to given section.
func (s *Section) NewKey(name, val string) (*Key, error) { func (s *Section) NewKey(name, val string) (*Key, error) {
if len(name) == 0 { if len(name) == 0 {
@ -136,6 +144,7 @@ func (s *Section) HasKey(name string) bool {
} }
// Haskey is a backwards-compatible name for HasKey. // Haskey is a backwards-compatible name for HasKey.
// TODO: delete me in v2
func (s *Section) Haskey(name string) bool { func (s *Section) Haskey(name string) bool {
return s.HasKey(name) return s.HasKey(name)
} }

View file

@ -78,7 +78,7 @@ func parseDelim(actual string) string {
var reflectTime = reflect.TypeOf(time.Now()).Kind() var reflectTime = reflect.TypeOf(time.Now()).Kind()
// setSliceWithProperType sets proper values to slice based on its type. // setSliceWithProperType sets proper values to slice based on its type.
func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error { func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
var strs []string var strs []string
if allowShadow { if allowShadow {
strs = key.StringsWithShadows(delim) strs = key.StringsWithShadows(delim)
@ -92,26 +92,30 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh
} }
var vals interface{} var vals interface{}
var err error
sliceOf := field.Type().Elem().Kind() sliceOf := field.Type().Elem().Kind()
switch sliceOf { switch sliceOf {
case reflect.String: case reflect.String:
vals = strs vals = strs
case reflect.Int: case reflect.Int:
vals, _ = key.parseInts(strs, true, false) vals, err = key.parseInts(strs, true, false)
case reflect.Int64: case reflect.Int64:
vals, _ = key.parseInt64s(strs, true, false) vals, err = key.parseInt64s(strs, true, false)
case reflect.Uint: case reflect.Uint:
vals, _ = key.parseUints(strs, true, false) vals, err = key.parseUints(strs, true, false)
case reflect.Uint64: case reflect.Uint64:
vals, _ = key.parseUint64s(strs, true, false) vals, err = key.parseUint64s(strs, true, false)
case reflect.Float64: case reflect.Float64:
vals, _ = key.parseFloat64s(strs, true, false) vals, err = key.parseFloat64s(strs, true, false)
case reflectTime: case reflectTime:
vals, _ = key.parseTimesFormat(time.RFC3339, strs, true, false) vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)
default: default:
return fmt.Errorf("unsupported type '[]%s'", sliceOf) return fmt.Errorf("unsupported type '[]%s'", sliceOf)
} }
if err != nil && isStrict {
return err
}
slice := reflect.MakeSlice(field.Type(), numVals, numVals) slice := reflect.MakeSlice(field.Type(), numVals, numVals)
for i := 0; i < numVals; i++ { for i := 0; i < numVals; i++ {
@ -136,10 +140,17 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh
return nil return nil
} }
func wrapStrictError(err error, isStrict bool) error {
if isStrict {
return err
}
return nil
}
// setWithProperType sets proper value to field based on its type, // setWithProperType sets proper value to field based on its type,
// but it does not return error for failing parsing, // but it does not return error for failing parsing,
// because we want to use default value that is already assigned to strcut. // because we want to use default value that is already assigned to strcut.
func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error { func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
switch t.Kind() { switch t.Kind() {
case reflect.String: case reflect.String:
if len(key.String()) == 0 { if len(key.String()) == 0 {
@ -149,7 +160,7 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
case reflect.Bool: case reflect.Bool:
boolVal, err := key.Bool() boolVal, err := key.Bool()
if err != nil { if err != nil {
return nil return wrapStrictError(err, isStrict)
} }
field.SetBool(boolVal) field.SetBool(boolVal)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@ -161,8 +172,8 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
} }
intVal, err := key.Int64() intVal, err := key.Int64()
if err != nil || intVal == 0 { if err != nil {
return nil return wrapStrictError(err, isStrict)
} }
field.SetInt(intVal) field.SetInt(intVal)
// byte is an alias for uint8, so supporting uint8 breaks support for byte // byte is an alias for uint8, so supporting uint8 breaks support for byte
@ -176,24 +187,24 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
uintVal, err := key.Uint64() uintVal, err := key.Uint64()
if err != nil { if err != nil {
return nil return wrapStrictError(err, isStrict)
} }
field.SetUint(uintVal) field.SetUint(uintVal)
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
floatVal, err := key.Float64() floatVal, err := key.Float64()
if err != nil { if err != nil {
return nil return wrapStrictError(err, isStrict)
} }
field.SetFloat(floatVal) field.SetFloat(floatVal)
case reflectTime: case reflectTime:
timeVal, err := key.Time() timeVal, err := key.Time()
if err != nil { if err != nil {
return nil return wrapStrictError(err, isStrict)
} }
field.Set(reflect.ValueOf(timeVal)) field.Set(reflect.ValueOf(timeVal))
case reflect.Slice: case reflect.Slice:
return setSliceWithProperType(key, field, delim, allowShadow) return setSliceWithProperType(key, field, delim, allowShadow, isStrict)
default: default:
return fmt.Errorf("unsupported type '%s'", t) return fmt.Errorf("unsupported type '%s'", t)
} }
@ -212,7 +223,7 @@ func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bo
return rawName, omitEmpty, allowShadow return rawName, omitEmpty, allowShadow
} }
func (s *Section) mapTo(val reflect.Value) error { func (s *Section) mapTo(val reflect.Value, isStrict bool) error {
if val.Kind() == reflect.Ptr { if val.Kind() == reflect.Ptr {
val = val.Elem() val = val.Elem()
} }
@ -241,7 +252,7 @@ func (s *Section) mapTo(val reflect.Value) error {
if isAnonymous || isStruct { if isAnonymous || isStruct {
if sec, err := s.f.GetSection(fieldName); err == nil { if sec, err := s.f.GetSection(fieldName); err == nil {
if err = sec.mapTo(field); err != nil { if err = sec.mapTo(field, isStrict); err != nil {
return fmt.Errorf("error mapping field(%s): %v", fieldName, err) return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
} }
continue continue
@ -250,7 +261,7 @@ func (s *Section) mapTo(val reflect.Value) error {
if key, err := s.GetKey(fieldName); err == nil { if key, err := s.GetKey(fieldName); err == nil {
delim := parseDelim(tpField.Tag.Get("delim")) delim := parseDelim(tpField.Tag.Get("delim"))
if err = setWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil { if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
return fmt.Errorf("error mapping field(%s): %v", fieldName, err) return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
} }
} }
@ -269,7 +280,22 @@ func (s *Section) MapTo(v interface{}) error {
return errors.New("cannot map to non-pointer struct") return errors.New("cannot map to non-pointer struct")
} }
return s.mapTo(val) return s.mapTo(val, false)
}
// MapTo maps section to given struct in strict mode,
// which returns all possible error including value parsing error.
func (s *Section) StrictMapTo(v interface{}) error {
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
} else {
return errors.New("cannot map to non-pointer struct")
}
return s.mapTo(val, true)
} }
// MapTo maps file to given struct. // MapTo maps file to given struct.
@ -277,6 +303,12 @@ func (f *File) MapTo(v interface{}) error {
return f.Section("").MapTo(v) return f.Section("").MapTo(v)
} }
// MapTo maps file to given struct in strict mode,
// which returns all possible error including value parsing error.
func (f *File) StrictMapTo(v interface{}) error {
return f.Section("").StrictMapTo(v)
}
// MapTo maps data sources to given struct with name mapper. // MapTo maps data sources to given struct with name mapper.
func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
cfg, err := Load(source, others...) cfg, err := Load(source, others...)
@ -287,11 +319,28 @@ func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, other
return cfg.MapTo(v) return cfg.MapTo(v)
} }
// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode,
// which returns all possible error including value parsing error.
func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
cfg, err := Load(source, others...)
if err != nil {
return err
}
cfg.NameMapper = mapper
return cfg.StrictMapTo(v)
}
// MapTo maps data sources to given struct. // MapTo maps data sources to given struct.
func MapTo(v, source interface{}, others ...interface{}) error { func MapTo(v, source interface{}, others ...interface{}) error {
return MapToWithMapper(v, nil, source, others...) return MapToWithMapper(v, nil, source, others...)
} }
// StrictMapTo maps data sources to given struct in strict mode,
// which returns all possible error including value parsing error.
func StrictMapTo(v, source interface{}, others ...interface{}) error {
return StrictMapToWithMapper(v, nil, source, others...)
}
// reflectSliceWithProperType does the opposite thing as setSliceWithProperType. // reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error { func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error {
slice := field.Slice(0, field.Len()) slice := field.Slice(0, field.Len())
@ -359,10 +408,11 @@ func isEmptyValue(v reflect.Value) bool {
return v.Uint() == 0 return v.Uint() == 0
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return v.Float() == 0 return v.Float() == 0
case reflectTime:
return v.Interface().(time.Time).IsZero()
case reflect.Interface, reflect.Ptr: case reflect.Interface, reflect.Ptr:
return v.IsNil() return v.IsNil()
case reflectTime:
t, ok := v.Interface().(time.Time)
return ok && t.IsZero()
} }
return false return false
} }
@ -400,6 +450,12 @@ func (s *Section) reflectFrom(val reflect.Value) error {
// Note: fieldName can never be empty here, ignore error. // Note: fieldName can never be empty here, ignore error.
sec, _ = s.f.NewSection(fieldName) sec, _ = s.f.NewSection(fieldName)
} }
// Add comment from comment tag
if len(sec.Comment) == 0 {
sec.Comment = tpField.Tag.Get("comment")
}
if err = sec.reflectFrom(field); err != nil { if err = sec.reflectFrom(field); err != nil {
return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
} }
@ -411,6 +467,12 @@ func (s *Section) reflectFrom(val reflect.Value) error {
if err != nil { if err != nil {
key, _ = s.NewKey(fieldName, "") key, _ = s.NewKey(fieldName, "")
} }
// Add comment from comment tag
if len(key.Comment) == 0 {
key.Comment = tpField.Tag.Get("comment")
}
if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil { if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
} }

View file

@ -188,9 +188,10 @@ func (c *Client) getResponse(response *http.Response, resType interface{}) error
if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
apiError := &APIError{Code: response.StatusCode} apiError := &APIError{Code: response.StatusCode}
if err = json.Unmarshal(body, apiError); err != nil { if err = json.Unmarshal(body, apiError); err != nil {
return err apiError.Message = string(body)
} }
apiError.QueryID = response.Header.Get("X-Ovh-QueryID") apiError.QueryID = response.Header.Get("X-Ovh-QueryID")
return apiError return apiError
} }

View file

@ -1,39 +0,0 @@
package main
import (
"egoscale"
"fmt"
"flag"
"os"
)
var apikey = flag.String("xk", "", "Exoscale API Key")
var apisecret = flag.String("xs", "", "Exoscale API Secret")
var endpoint = flag.String("xe", "https://api.exoscale.ch/compute", "Exoscale API Endpoint")
func main() {
flag.Parse()
client := egoscale.NewClient(*endpoint, *apikey, *apisecret)
vms, err := client.ListVirtualMachines()
if err != nil {
fmt.Printf("got error: %s\n", err)
os.Exit(1)
}
for _, vm := range(vms) {
fmt.Println("vm:", vm.Displayname)
for _, nic := range(vm.Nic) {
fmt.Println("ip:", nic.Ipaddress)
}
for _, sg := range(vm.SecurityGroups) {
fmt.Println("securitygroup:", sg.Name)
}
}
os.Exit(0)
}

View file

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015 Sebastian Erhart Copyright (c) 2015-2017 Sebastian Erhart
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -330,6 +330,10 @@ DNSNames:
challenges, failures := c.getChallenges(domains) challenges, failures := c.getChallenges(domains)
// If any challenge fails - return. Do not generate partial SAN certificates. // If any challenge fails - return. Do not generate partial SAN certificates.
if len(failures) > 0 { if len(failures) > 0 {
for _, auth := range challenges {
c.disableAuthz(auth)
}
return CertificateResource{}, failures return CertificateResource{}, failures
} }
@ -373,6 +377,10 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
challenges, failures := c.getChallenges(domains) challenges, failures := c.getChallenges(domains)
// If any challenge fails - return. Do not generate partial SAN certificates. // If any challenge fails - return. Do not generate partial SAN certificates.
if len(failures) > 0 { if len(failures) > 0 {
for _, auth := range challenges {
c.disableAuthz(auth)
}
return CertificateResource{}, failures return CertificateResource{}, failures
} }
@ -493,10 +501,12 @@ func (c *Client) solveChallenges(challenges []authorizationResource) map[string]
// TODO: do not immediately fail if one domain fails to validate. // TODO: do not immediately fail if one domain fails to validate.
err := solver.Solve(authz.Body.Challenges[i], authz.Domain) err := solver.Solve(authz.Body.Challenges[i], authz.Domain)
if err != nil { if err != nil {
c.disableAuthz(authz)
failures[authz.Domain] = err failures[authz.Domain] = err
} }
} }
} else { } else {
c.disableAuthz(authz)
failures[authz.Domain] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Domain) failures[authz.Domain] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Domain)
} }
} }
@ -586,6 +596,13 @@ func logAuthz(authz []authorizationResource) {
} }
} }
// cleanAuthz loops through the passed in slice and disables any auths which are not "valid"
func (c *Client) disableAuthz(auth authorizationResource) error {
var disabledAuth authorization
_, err := postJSON(c.jws, auth.AuthURL, deactivateAuthMessage{Resource: "authz", Status: "deactivated"}, &disabledAuth)
return err
}
func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) { func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) {
if len(authz) == 0 { if len(authz) == 0 {
return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!") return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!")

View file

@ -194,7 +194,7 @@ func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (
if err == dns.ErrTruncated { if err == dns.ErrTruncated {
tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout} tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout}
// If the TCP request suceeds, the err will reset to nil // If the TCP request succeeds, the err will reset to nil
in, _, err = tcp.Exchange(m, ns) in, _, err = tcp.Exchange(m, ns)
} }

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net"
"net/http" "net/http"
"runtime" "runtime"
"strings" "strings"
@ -15,7 +16,17 @@ import (
var UserAgent string var UserAgent string
// HTTPClient is an HTTP client with a reasonable timeout value. // HTTPClient is an HTTP client with a reasonable timeout value.
var HTTPClient = http.Client{Timeout: 10 * time.Second} var HTTPClient = http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 15 * time.Second,
ResponseHeaderTimeout: 15 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
const ( const (
// defaultGoUserAgent is the Go HTTP package user agent string. Too // defaultGoUserAgent is the Go HTTP package user agent string. Too

View file

@ -93,6 +93,11 @@ type revokeCertMessage struct {
Certificate string `json:"certificate"` Certificate string `json:"certificate"`
} }
type deactivateAuthMessage struct {
Resource string `json:"resource,omitempty"`
Status string `jsom:"status"`
}
// CertificateResource represents a CA issued certificate. // CertificateResource represents a CA issued certificate.
// PrivateKey, Certificate and IssuerCertificate are all // PrivateKey, Certificate and IssuerCertificate are all
// already PEM encoded and can be directly written to disk. // already PEM encoded and can be directly written to disk.

View file

@ -32,7 +32,7 @@ func main() {
app.Name = "lego" app.Name = "lego"
app.Usage = "Let's Encrypt client written in Go" app.Usage = "Let's Encrypt client written in Go"
version := "0.3.1" version := "0.4.1"
if strings.HasPrefix(gittag, "v") { if strings.HasPrefix(gittag, "v") {
version = gittag version = gittag
} }
@ -109,7 +109,7 @@ func main() {
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
cli.StringSliceFlag{ cli.StringSliceFlag{
Name: "domains, d", Name: "domains, d",
Usage: "Add domains to the process", Usage: "Add a domain to the process. Can be specified multiple times.",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "csr, c", Name: "csr, c",
@ -209,18 +209,19 @@ Here is an example bash command using the CloudFlare DNS provider:
fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET") fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET")
fmt.Fprintln(w, "\texoscale:\tEXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT") fmt.Fprintln(w, "\texoscale:\tEXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT")
fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY") fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY")
fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT") fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT, GCE_SERVICE_ACCOUNT_FILE")
fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY") fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY")
fmt.Fprintln(w, "\tmanual:\tnone") fmt.Fprintln(w, "\tmanual:\tnone")
fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_API_USER, NAMECHEAP_API_KEY") fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_API_USER, NAMECHEAP_API_KEY")
fmt.Fprintln(w, "\trackspace:\tRACKSPACE_USER, RACKSPACE_API_KEY") fmt.Fprintln(w, "\trackspace:\tRACKSPACE_USER, RACKSPACE_API_KEY")
fmt.Fprintln(w, "\trfc2136:\tRFC2136_TSIG_KEY, RFC2136_TSIG_SECRET,\n\t\tRFC2136_TSIG_ALGORITHM, RFC2136_NAMESERVER") fmt.Fprintln(w, "\trfc2136:\tRFC2136_TSIG_KEY, RFC2136_TSIG_SECRET,\n\t\tRFC2136_TSIG_ALGORITHM, RFC2136_NAMESERVER")
fmt.Fprintln(w, "\troute53:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION") fmt.Fprintln(w, "\troute53:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_HOSTED_ZONE_ID")
fmt.Fprintln(w, "\tdyn:\tDYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD") fmt.Fprintln(w, "\tdyn:\tDYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD")
fmt.Fprintln(w, "\tvultr:\tVULTR_API_KEY") fmt.Fprintln(w, "\tvultr:\tVULTR_API_KEY")
fmt.Fprintln(w, "\tovh:\tOVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY") fmt.Fprintln(w, "\tovh:\tOVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY")
fmt.Fprintln(w, "\tpdns:\tPDNS_API_KEY, PDNS_API_URL") fmt.Fprintln(w, "\tpdns:\tPDNS_API_KEY, PDNS_API_URL")
fmt.Fprintln(w, "\tdnspod:\tDNSPOD_API_KEY") fmt.Fprintln(w, "\tdnspod:\tDNSPOD_API_KEY")
fmt.Fprintln(w, "\totc:\tOTC_USER_NAME, OTC_PASSWORD, OTC_PROJECT_NAME, OTC_DOMAIN_NAME, OTC_IDENTITY_ENDPOINT")
w.Flush() w.Flush()
fmt.Println(` fmt.Println(`

View file

@ -114,7 +114,7 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
} }
if c.GlobalIsSet("dns") { if c.GlobalIsSet("dns") {
provider, err := dns.NewDNSChallengeProviderByName(c.GlobalString("dns")) provider, err := dns.NewDNSChallengeProviderByName(c.GlobalString("dns"))
if err != nil { if err != nil {
logger().Fatal(err) logger().Fatal(err)
} }

View file

@ -12,6 +12,8 @@ import (
"strings" "strings"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to" "github.com/Azure/go-autorest/autorest/to"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
@ -69,7 +71,9 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
} }
rsc := dns.NewRecordSetsClient(c.subscriptionId) rsc := dns.NewRecordSetsClient(c.subscriptionId)
rsc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
rec := dns.RecordSet{ rec := dns.RecordSet{
Name: &relative, Name: &relative,
@ -103,7 +107,8 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
rsc := dns.NewRecordSetsClient(c.subscriptionId) rsc := dns.NewRecordSetsClient(c.subscriptionId)
rsc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
_, err = rsc.Delete(c.resourceGroup, zone, relative, dns.TXT, "") _, err = rsc.Delete(c.resourceGroup, zone, relative, dns.TXT, "")
if err != nil { if err != nil {
return err return err
@ -120,8 +125,11 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
} }
// Now we want to to Azure and get the zone. // Now we want to to Azure and get the zone.
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
dc := dns.NewZonesClient(c.subscriptionId) dc := dns.NewZonesClient(c.subscriptionId)
dc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) dc.Authorizer = autorest.NewBearerAuthorizer(spt)
zone, err := dc.Get(c.resourceGroup, acme.UnFqdn(authZone)) zone, err := dc.Get(c.resourceGroup, acme.UnFqdn(authZone))
if err != nil { if err != nil {
@ -134,10 +142,10 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
// NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the // NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the
// passed credentials map. // passed credentials map.
func (c *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*azure.ServicePrincipalToken, error) { func (c *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) {
oauthConfig, err := azure.PublicCloud.OAuthConfigForTenant(c.tenantId) oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, c.tenantId)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return azure.NewServicePrincipalToken(*oauthConfig, c.clientId, c.clientSecret, scope) return adal.NewServicePrincipalToken(*oauthConfig, c.clientId, c.clientSecret, scope)
} }

View file

@ -19,6 +19,7 @@ import (
"github.com/xenolf/lego/providers/dns/linode" "github.com/xenolf/lego/providers/dns/linode"
"github.com/xenolf/lego/providers/dns/namecheap" "github.com/xenolf/lego/providers/dns/namecheap"
"github.com/xenolf/lego/providers/dns/ns1" "github.com/xenolf/lego/providers/dns/ns1"
"github.com/xenolf/lego/providers/dns/otc"
"github.com/xenolf/lego/providers/dns/ovh" "github.com/xenolf/lego/providers/dns/ovh"
"github.com/xenolf/lego/providers/dns/pdns" "github.com/xenolf/lego/providers/dns/pdns"
"github.com/xenolf/lego/providers/dns/rackspace" "github.com/xenolf/lego/providers/dns/rackspace"
@ -73,6 +74,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
provider, err = pdns.NewDNSProvider() provider, err = pdns.NewDNSProvider()
case "ns1": case "ns1":
provider, err = ns1.NewDNSProvider() provider, err = ns1.NewDNSProvider()
case "otc":
provider, err = otc.NewDNSProvider()
default: default:
err = fmt.Errorf("Unrecognised DNS provider: %s", name) err = fmt.Errorf("Unrecognised DNS provider: %s", name)
} }

View file

@ -7,7 +7,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/pyr/egoscale/src/egoscale" "github.com/exoscale/egoscale"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
) )

View file

@ -4,12 +4,14 @@ package googlecloud
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
"google.golang.org/api/dns/v1" "google.golang.org/api/dns/v1"
@ -22,9 +24,14 @@ type DNSProvider struct {
} }
// NewDNSProvider returns a DNSProvider instance configured for Google Cloud // NewDNSProvider returns a DNSProvider instance configured for Google Cloud
// DNS. Credentials must be passed in the environment variable: GCE_PROJECT. // DNS. Project name must be passed in the environment variable: GCE_PROJECT.
// A Service Account file can be passed in the environment variable:
// GCE_SERVICE_ACCOUNT_FILE
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
project := os.Getenv("GCE_PROJECT") project := os.Getenv("GCE_PROJECT")
if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok {
return NewDNSProviderServiceAccount(project, saFile)
}
return NewDNSProviderCredentials(project) return NewDNSProviderCredentials(project)
} }
@ -49,6 +56,36 @@ func NewDNSProviderCredentials(project string) (*DNSProvider, error) {
}, nil }, nil
} }
// NewDNSProviderServiceAccount uses the supplied service account JSON file to
// return a DNSProvider instance configured for Google Cloud DNS.
func NewDNSProviderServiceAccount(project string, saFile string) (*DNSProvider, error) {
if project == "" {
return nil, fmt.Errorf("Google Cloud project name missing")
}
if saFile == "" {
return nil, fmt.Errorf("Google Cloud Service Account file missing")
}
dat, err := ioutil.ReadFile(saFile)
if err != nil {
return nil, fmt.Errorf("Unable to read Service Account file: %v", err)
}
conf, err := google.JWTConfigFromJSON(dat, dns.NdevClouddnsReadwriteScope)
if err != nil {
return nil, fmt.Errorf("Unable to acquire config: %v", err)
}
client := conf.Client(oauth2.NoContext)
svc, err := dns.New(client)
if err != nil {
return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err)
}
return &DNSProvider{
project: project,
client: svc,
}, nil
}
// Present creates a TXT record to fulfil the dns-01 challenge. // Present creates a TXT record to fulfil the dns-01 challenge.
func (c *DNSProvider) Present(domain, token, keyAuth string) error { func (c *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)

152
vendor/github.com/xenolf/lego/providers/dns/otc/mock.go generated vendored Normal file
View file

@ -0,0 +1,152 @@
package otc
import (
"fmt"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
var fakeOTCUserName = "test"
var fakeOTCPassword = "test"
var fakeOTCDomainName = "test"
var fakeOTCProjectName = "test"
var fakeOTCToken = "62244bc21da68d03ebac94e6636ff01f"
type DNSMock struct {
t *testing.T
Server *httptest.Server
Mux *http.ServeMux
}
func NewDNSMock(t *testing.T) *DNSMock {
return &DNSMock{
t: t,
}
}
// Setup creates the mock server
func (m *DNSMock) Setup() {
m.Mux = http.NewServeMux()
m.Server = httptest.NewServer(m.Mux)
}
// ShutdownServer creates the mock server
func (m *DNSMock) ShutdownServer() {
m.Server.Close()
}
func (m *DNSMock) HandleAuthSuccessfully() {
m.Mux.HandleFunc("/v3/auth/token", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Subject-Token", fakeOTCToken)
fmt.Fprintf(w, `{
"token": {
"catalog": [
{
"type": "dns",
"id": "56cd81db1f8445d98652479afe07c5ba",
"name": "",
"endpoints": [
{
"url": "%s",
"region": "eu-de",
"region_id": "eu-de",
"interface": "public",
"id": "0047a06690484d86afe04877074efddf"
}
]
}
]
}}`, m.Server.URL)
})
}
func (m *DNSMock) HandleListZonesSuccessfully() {
m.Mux.HandleFunc("/v2/zones", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{
"zones":[{
"id":"123123"
}]}
`)
assert.Equal(m.t, r.Method, "GET")
assert.Equal(m.t, r.URL.Path, "/v2/zones")
assert.Equal(m.t, r.URL.RawQuery, "name=example.com.")
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
})
}
func (m *DNSMock) HandleListZonesEmpty() {
m.Mux.HandleFunc("/v2/zones", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{
"zones":[
]}
`)
assert.Equal(m.t, r.Method, "GET")
assert.Equal(m.t, r.URL.Path, "/v2/zones")
assert.Equal(m.t, r.URL.RawQuery, "name=example.com.")
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
})
}
func (m *DNSMock) HandleDeleteRecordsetsSuccessfully() {
m.Mux.HandleFunc("/v2/zones/123123/recordsets/321321", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{
"zones":[{
"id":"123123"
}]}
`)
assert.Equal(m.t, r.Method, "DELETE")
assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets/321321")
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
})
}
func (m *DNSMock) HandleListRecordsetsEmpty() {
m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{
"recordsets":[
]}
`)
assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets")
assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.")
})
}
func (m *DNSMock) HandleListRecordsetsSuccessfully() {
m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
fmt.Fprintf(w, `{
"recordsets":[{
"id":"321321"
}]}
`)
assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets")
assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.")
} else if r.Method == "POST" {
body, err := ioutil.ReadAll(r.Body)
assert.Nil(m.t, err)
exceptedString := "{\"name\":\"_acme-challenge.example.com.\",\"description\":\"Added TXT record for ACME dns-01 challenge using lego client\",\"type\":\"TXT\",\"ttl\":300,\"records\":[\"\\\"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI\\\"\"]}"
assert.Equal(m.t, string(body), exceptedString)
fmt.Fprintf(w, `{
"recordsets":[{
"id":"321321"
}]}
`)
} else {
m.t.Errorf("Expected method to be 'GET' or 'POST' but got '%s'", r.Method)
}
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
})
}

388
vendor/github.com/xenolf/lego/providers/dns/otc/otc.go generated vendored Normal file
View file

@ -0,0 +1,388 @@
// Package otc implements a DNS provider for solving the DNS-01 challenge
// using Open Telekom Cloud Managed DNS.
package otc
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"time"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
// OTC's Managed DNS API to manage TXT records for a domain.
type DNSProvider struct {
identityEndpoint string
otcBaseURL string
domainName string
projectName string
userName string
password string
token string
}
// NewDNSProvider returns a DNSProvider instance configured for OTC DNS.
// Credentials must be passed in the environment variables: OTC_USER_NAME,
// OTC_DOMAIN_NAME, OTC_PASSWORD OTC_PROJECT_NAME and OTC_IDENTITY_ENDPOINT.
func NewDNSProvider() (*DNSProvider, error) {
domainName := os.Getenv("OTC_DOMAIN_NAME")
userName := os.Getenv("OTC_USER_NAME")
password := os.Getenv("OTC_PASSWORD")
projectName := os.Getenv("OTC_PROJECT_NAME")
identityEndpoint := os.Getenv("OTC_IDENTITY_ENDPOINT")
return NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for OTC DNS.
func NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint string) (*DNSProvider, error) {
if domainName == "" || userName == "" || password == "" || projectName == "" {
return nil, fmt.Errorf("OTC credentials missing")
}
if identityEndpoint == "" {
identityEndpoint = "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens"
}
return &DNSProvider{
identityEndpoint: identityEndpoint,
domainName: domainName,
userName: userName,
password: password,
projectName: projectName,
}, nil
}
func (d *DNSProvider) SendRequest(method, resource string, payload interface{}) (io.Reader, error) {
url := fmt.Sprintf("%s/%s", d.otcBaseURL, resource)
body, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest(method, url, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
if len(d.token) > 0 {
req.Header.Set("X-Auth-Token", d.token)
}
// Workaround for keep alive bug in otc api
tr := http.DefaultTransport.(*http.Transport)
tr.DisableKeepAlives = true
client := &http.Client{
Timeout: time.Duration(10 * time.Second),
Transport: tr,
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("OTC API request %s failed with HTTP status code %d", url, resp.StatusCode)
}
body1, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return bytes.NewReader(body1), nil
}
func (d *DNSProvider) loginRequest() error {
type nameResponse struct {
Name string `json:"name"`
}
type userResponse struct {
Name string `json:"name"`
Password string `json:"password"`
Domain nameResponse `json:"domain"`
}
type passwordResponse struct {
User userResponse `json:"user"`
}
type identityResponse struct {
Methods []string `json:"methods"`
Password passwordResponse `json:"password"`
}
type scopeResponse struct {
Project nameResponse `json:"project"`
}
type authResponse struct {
Identity identityResponse `json:"identity"`
Scope scopeResponse `json:"scope"`
}
type loginResponse struct {
Auth authResponse `json:"auth"`
}
userResp := userResponse{
Name: d.userName,
Password: d.password,
Domain: nameResponse{
Name: d.domainName,
},
}
loginResp := loginResponse{
Auth: authResponse{
Identity: identityResponse{
Methods: []string{"password"},
Password: passwordResponse{
User: userResp,
},
},
Scope: scopeResponse{
Project: nameResponse{
Name: d.projectName,
},
},
},
}
body, err := json.Marshal(loginResp)
if err != nil {
return err
}
req, err := http.NewRequest("POST", d.identityEndpoint, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: time.Duration(10 * time.Second)}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return fmt.Errorf("OTC API request failed with HTTP status code %d", resp.StatusCode)
}
d.token = resp.Header.Get("X-Subject-Token")
if d.token == "" {
return fmt.Errorf("unable to get auth token")
}
type endpointResponse struct {
Token struct {
Catalog []struct {
Type string `json:"type"`
Endpoints []struct {
URL string `json:"url"`
} `json:"endpoints"`
} `json:"catalog"`
} `json:"token"`
}
var endpointResp endpointResponse
err = json.NewDecoder(resp.Body).Decode(&endpointResp)
if err != nil {
return err
}
for _, v := range endpointResp.Token.Catalog {
if v.Type == "dns" {
for _, endpoint := range v.Endpoints {
d.otcBaseURL = fmt.Sprintf("%s/v2", endpoint.URL)
continue
}
}
}
if d.otcBaseURL == "" {
return fmt.Errorf("unable to get dns endpoint")
}
return nil
}
// Starts a new OTC API Session. Authenticates using userName, password
// and receives a token to be used in for subsequent requests.
func (d *DNSProvider) login() error {
err := d.loginRequest()
if err != nil {
return err
}
return nil
}
func (d *DNSProvider) getZoneID(zone string) (string, error) {
type zoneItem struct {
ID string `json:"id"`
}
type zonesResponse struct {
Zones []zoneItem `json:"zones"`
}
resource := fmt.Sprintf("zones?name=%s", zone)
resp, err := d.SendRequest("GET", resource, nil)
if err != nil {
return "", err
}
var zonesRes zonesResponse
err = json.NewDecoder(resp).Decode(&zonesRes)
if err != nil {
return "", err
}
if len(zonesRes.Zones) < 1 {
return "", fmt.Errorf("zone %s not found", zone)
}
if len(zonesRes.Zones) > 1 {
return "", fmt.Errorf("to many zones found")
}
if zonesRes.Zones[0].ID == "" {
return "", fmt.Errorf("id not found")
}
return zonesRes.Zones[0].ID, nil
}
func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) {
type recordSet struct {
ID string `json:"id"`
}
type recordSetsResponse struct {
RecordSets []recordSet `json:"recordsets"`
}
resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn)
resp, err := d.SendRequest("GET", resource, nil)
if err != nil {
return "", err
}
var recordSetsRes recordSetsResponse
err = json.NewDecoder(resp).Decode(&recordSetsRes)
if err != nil {
return "", err
}
if len(recordSetsRes.RecordSets) < 1 {
return "", fmt.Errorf("record not found")
}
if len(recordSetsRes.RecordSets) > 1 {
return "", fmt.Errorf("to many records found")
}
if recordSetsRes.RecordSets[0].ID == "" {
return "", fmt.Errorf("id not found")
}
return recordSetsRes.RecordSets[0].ID, nil
}
func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error {
resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID)
_, err := d.SendRequest("DELETE", resource, nil)
if err != nil {
return err
}
return nil
}
// Present creates a TXT record using the specified parameters
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
if ttl < 300 {
ttl = 300 // 300 is otc minimum value for ttl
}
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return err
}
err = d.login()
if err != nil {
return err
}
zoneID, err := d.getZoneID(authZone)
if err != nil {
return fmt.Errorf("unable to get zone: %s", err)
}
resource := fmt.Sprintf("zones/%s/recordsets", zoneID)
type recordset struct {
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Ttl int `json:"ttl"`
Records []string `json:"records"`
}
r1 := &recordset{
Name: fqdn,
Description: "Added TXT record for ACME dns-01 challenge using lego client",
Type: "TXT",
Ttl: 300,
Records: []string{fmt.Sprintf("\"%s\"", value)},
}
_, err = d.SendRequest("POST", resource, r1)
if err != nil {
return err
}
return nil
}
// CleanUp removes the TXT record matching the specified parameters
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return err
}
err = d.login()
if err != nil {
return err
}
zoneID, err := d.getZoneID(authZone)
if err != nil {
return err
}
recordID, err := d.getRecordSetID(zoneID, fqdn)
if err != nil {
return fmt.Errorf("unable go get record %s for zone %s: %s", fqdn, domain, err)
}
return d.deleteRecordSet(zoneID, recordID)
}

View file

@ -269,7 +269,7 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
return r, nil return r, nil
} }
// RackspaceRecords is the list of records sent/recieved from the DNS API // RackspaceRecords is the list of records sent/received from the DNS API
type RackspaceRecords struct { type RackspaceRecords struct {
RackspaceRecord []RackspaceRecord `json:"records"` RackspaceRecord []RackspaceRecord `json:"records"`
} }

View file

@ -20,27 +20,32 @@ type DNSProvider struct {
tsigAlgorithm string tsigAlgorithm string
tsigKey string tsigKey string
tsigSecret string tsigSecret string
timeout time.Duration
} }
// NewDNSProvider returns a DNSProvider instance configured for rfc2136 // NewDNSProvider returns a DNSProvider instance configured for rfc2136
// dynamic update. Credentials must be passed in the environment variables: // dynamic update. Configured with environment variables:
// RFC2136_NAMESERVER, RFC2136_TSIG_ALGORITHM, RFC2136_TSIG_KEY and // RFC2136_NAMESERVER: Network address in the form "host" or "host:port".
// RFC2136_TSIG_SECRET. To disable TSIG authentication, leave the TSIG // RFC2136_TSIG_ALGORITHM: Defaults to hmac-md5.sig-alg.reg.int. (HMAC-MD5).
// variables unset. RFC2136_NAMESERVER must be a network address in the form // See https://github.com/miekg/dns/blob/master/tsig.go for supported values.
// "host" or "host:port". // RFC2136_TSIG_KEY: Name of the secret key as defined in DNS server configuration.
// RFC2136_TSIG_SECRET: Secret key payload.
// RFC2136_TIMEOUT: DNS propagation timeout in time.ParseDuration format. (60s)
// To disable TSIG authentication, leave the RFC2136_TSIG* variables unset.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
nameserver := os.Getenv("RFC2136_NAMESERVER") nameserver := os.Getenv("RFC2136_NAMESERVER")
tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM")
tsigKey := os.Getenv("RFC2136_TSIG_KEY") tsigKey := os.Getenv("RFC2136_TSIG_KEY")
tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") tsigSecret := os.Getenv("RFC2136_TSIG_SECRET")
return NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret) timeout := os.Getenv("RFC2136_TIMEOUT")
return NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout)
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for rfc2136 dynamic update. To disable TSIG // DNSProvider instance configured for rfc2136 dynamic update. To disable TSIG
// authentication, leave the TSIG parameters as empty strings. // authentication, leave the TSIG parameters as empty strings.
// nameserver must be a network address in the form "host" or "host:port". // nameserver must be a network address in the form "host" or "host:port".
func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret string) (*DNSProvider, error) { func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout string) (*DNSProvider, error) {
if nameserver == "" { if nameserver == "" {
return nil, fmt.Errorf("RFC2136 nameserver missing") return nil, fmt.Errorf("RFC2136 nameserver missing")
} }
@ -65,9 +70,27 @@ func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret st
d.tsigSecret = tsigSecret d.tsigSecret = tsigSecret
} }
if timeout == "" {
d.timeout = 60 * time.Second
} else {
t, err := time.ParseDuration(timeout)
if err != nil {
return nil, err
} else if t < 0 {
return nil, fmt.Errorf("Invalid/negative RFC2136_TIMEOUT: %v", timeout)
} else {
d.timeout = t
}
}
return d, nil return d, nil
} }
// Returns the timeout configured with RFC2136_TIMEOUT, or 60s.
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.timeout, 2 * time.Second
}
// Present creates a TXT record using the specified parameters // Present creates a TXT record using the specified parameters
func (r *DNSProvider) Present(domain, token, keyAuth string) error { func (r *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)

View file

@ -5,6 +5,7 @@ package route53
import ( import (
"fmt" "fmt"
"math/rand" "math/rand"
"os"
"strings" "strings"
"time" "time"
@ -23,7 +24,8 @@ const (
// DNSProvider implements the acme.ChallengeProvider interface // DNSProvider implements the acme.ChallengeProvider interface
type DNSProvider struct { type DNSProvider struct {
client *route53.Route53 client *route53.Route53
hostedZoneID string
} }
// customRetryer implements the client.Retryer interface by composing the // customRetryer implements the client.Retryer interface by composing the
@ -58,14 +60,22 @@ func (d customRetryer) RetryRules(r *request.Request) time.Duration {
// 2. Shared credentials file (defaults to ~/.aws/credentials) // 2. Shared credentials file (defaults to ~/.aws/credentials)
// 3. Amazon EC2 IAM role // 3. Amazon EC2 IAM role
// //
// If AWS_HOSTED_ZONE_ID is not set, Lego tries to determine the correct
// public hosted zone via the FQDN.
//
// See also: https://github.com/aws/aws-sdk-go/wiki/configuring-sdk // See also: https://github.com/aws/aws-sdk-go/wiki/configuring-sdk
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
hostedZoneID := os.Getenv("AWS_HOSTED_ZONE_ID")
r := customRetryer{} r := customRetryer{}
r.NumMaxRetries = maxRetries r.NumMaxRetries = maxRetries
config := request.WithRetryer(aws.NewConfig(), r) config := request.WithRetryer(aws.NewConfig(), r)
client := route53.New(session.New(config)) client := route53.New(session.New(config))
return &DNSProvider{client: client}, nil return &DNSProvider{
client: client,
hostedZoneID: hostedZoneID,
}, nil
} }
// Present creates a TXT record using the specified parameters // Present creates a TXT record using the specified parameters
@ -83,7 +93,7 @@ func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
} }
func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
hostedZoneID, err := getHostedZoneID(fqdn, r.client) hostedZoneID, err := r.getHostedZoneID(fqdn)
if err != nil { if err != nil {
return fmt.Errorf("Failed to determine Route 53 hosted zone ID: %v", err) return fmt.Errorf("Failed to determine Route 53 hosted zone ID: %v", err)
} }
@ -124,7 +134,11 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
}) })
} }
func getHostedZoneID(fqdn string, client *route53.Route53) (string, error) { func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
if r.hostedZoneID != "" {
return r.hostedZoneID, nil
}
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil { if err != nil {
return "", err return "", err
@ -134,7 +148,7 @@ func getHostedZoneID(fqdn string, client *route53.Route53) (string, error) {
reqParams := &route53.ListHostedZonesByNameInput{ reqParams := &route53.ListHostedZonesByNameInput{
DNSName: aws.String(acme.UnFqdn(authZone)), DNSName: aws.String(acme.UnFqdn(authZone)),
} }
resp, err := client.ListHostedZonesByName(reqParams) resp, err := r.client.ListHostedZonesByName(reqParams)
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -168,12 +168,20 @@ type Change struct {
// server regardless of whether the field is empty or not. This may be // server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests. // used to include empty fields in Patch requests.
ForceSendFields []string `json:"-"` ForceSendFields []string `json:"-"`
// NullFields is a list of field names (e.g. "Additions") to include in
// API requests with the JSON null value. By default, fields with empty
// values are omitted from API requests. However, any field with an
// empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
NullFields []string `json:"-"`
} }
func (s *Change) MarshalJSON() ([]byte, error) { func (s *Change) MarshalJSON() ([]byte, error) {
type noMethod Change type noMethod Change
raw := noMethod(*s) raw := noMethod(*s)
return gensupport.MarshalJSON(raw, s.ForceSendFields) return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
} }
// ChangesListResponse: The response to a request to enumerate Changes // ChangesListResponse: The response to a request to enumerate Changes
@ -209,12 +217,20 @@ type ChangesListResponse struct {
// server regardless of whether the field is empty or not. This may be // server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests. // used to include empty fields in Patch requests.
ForceSendFields []string `json:"-"` ForceSendFields []string `json:"-"`
// NullFields is a list of field names (e.g. "Changes") to include in
// API requests with the JSON null value. By default, fields with empty
// values are omitted from API requests. However, any field with an
// empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
NullFields []string `json:"-"`
} }
func (s *ChangesListResponse) MarshalJSON() ([]byte, error) { func (s *ChangesListResponse) MarshalJSON() ([]byte, error) {
type noMethod ChangesListResponse type noMethod ChangesListResponse
raw := noMethod(*s) raw := noMethod(*s)
return gensupport.MarshalJSON(raw, s.ForceSendFields) return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
} }
// ManagedZone: A zone is a subtree of the DNS namespace under one // ManagedZone: A zone is a subtree of the DNS namespace under one
@ -243,7 +259,7 @@ type ManagedZone struct {
Kind string `json:"kind,omitempty"` Kind string `json:"kind,omitempty"`
// Name: User assigned name for this resource. Must be unique within the // Name: User assigned name for this resource. Must be unique within the
// project. The name must be 1-32 characters long, must begin with a // project. The name must be 1-63 characters long, must begin with a
// letter, end with a letter or digit, and only contain lowercase // letter, end with a letter or digit, and only contain lowercase
// letters, digits or dashes. // letters, digits or dashes.
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
@ -268,12 +284,20 @@ type ManagedZone struct {
// server regardless of whether the field is empty or not. This may be // server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests. // used to include empty fields in Patch requests.
ForceSendFields []string `json:"-"` ForceSendFields []string `json:"-"`
// NullFields is a list of field names (e.g. "CreationTime") to include
// in API requests with the JSON null value. By default, fields with
// empty values are omitted from API requests. However, any field with
// an empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
NullFields []string `json:"-"`
} }
func (s *ManagedZone) MarshalJSON() ([]byte, error) { func (s *ManagedZone) MarshalJSON() ([]byte, error) {
type noMethod ManagedZone type noMethod ManagedZone
raw := noMethod(*s) raw := noMethod(*s)
return gensupport.MarshalJSON(raw, s.ForceSendFields) return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
} }
type ManagedZonesListResponse struct { type ManagedZonesListResponse struct {
@ -307,12 +331,20 @@ type ManagedZonesListResponse struct {
// server regardless of whether the field is empty or not. This may be // server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests. // used to include empty fields in Patch requests.
ForceSendFields []string `json:"-"` ForceSendFields []string `json:"-"`
// NullFields is a list of field names (e.g. "Kind") to include in API
// requests with the JSON null value. By default, fields with empty
// values are omitted from API requests. However, any field with an
// empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
NullFields []string `json:"-"`
} }
func (s *ManagedZonesListResponse) MarshalJSON() ([]byte, error) { func (s *ManagedZonesListResponse) MarshalJSON() ([]byte, error) {
type noMethod ManagedZonesListResponse type noMethod ManagedZonesListResponse
raw := noMethod(*s) raw := noMethod(*s)
return gensupport.MarshalJSON(raw, s.ForceSendFields) return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
} }
// Project: A project resource. The project is a top level container for // Project: A project resource. The project is a top level container for
@ -344,12 +376,20 @@ type Project struct {
// server regardless of whether the field is empty or not. This may be // server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests. // used to include empty fields in Patch requests.
ForceSendFields []string `json:"-"` ForceSendFields []string `json:"-"`
// NullFields is a list of field names (e.g. "Id") to include in API
// requests with the JSON null value. By default, fields with empty
// values are omitted from API requests. However, any field with an
// empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
NullFields []string `json:"-"`
} }
func (s *Project) MarshalJSON() ([]byte, error) { func (s *Project) MarshalJSON() ([]byte, error) {
type noMethod Project type noMethod Project
raw := noMethod(*s) raw := noMethod(*s)
return gensupport.MarshalJSON(raw, s.ForceSendFields) return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
} }
// Quota: Limits associated with a Project. // Quota: Limits associated with a Project.
@ -388,12 +428,20 @@ type Quota struct {
// server regardless of whether the field is empty or not. This may be // server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests. // used to include empty fields in Patch requests.
ForceSendFields []string `json:"-"` ForceSendFields []string `json:"-"`
// NullFields is a list of field names (e.g. "Kind") to include in API
// requests with the JSON null value. By default, fields with empty
// values are omitted from API requests. However, any field with an
// empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
NullFields []string `json:"-"`
} }
func (s *Quota) MarshalJSON() ([]byte, error) { func (s *Quota) MarshalJSON() ([]byte, error) {
type noMethod Quota type noMethod Quota
raw := noMethod(*s) raw := noMethod(*s)
return gensupport.MarshalJSON(raw, s.ForceSendFields) return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
} }
// ResourceRecordSet: A unit of data that will be returned by the DNS // ResourceRecordSet: A unit of data that will be returned by the DNS
@ -425,12 +473,20 @@ type ResourceRecordSet struct {
// server regardless of whether the field is empty or not. This may be // server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests. // used to include empty fields in Patch requests.
ForceSendFields []string `json:"-"` ForceSendFields []string `json:"-"`
// NullFields is a list of field names (e.g. "Kind") to include in API
// requests with the JSON null value. By default, fields with empty
// values are omitted from API requests. However, any field with an
// empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
NullFields []string `json:"-"`
} }
func (s *ResourceRecordSet) MarshalJSON() ([]byte, error) { func (s *ResourceRecordSet) MarshalJSON() ([]byte, error) {
type noMethod ResourceRecordSet type noMethod ResourceRecordSet
raw := noMethod(*s) raw := noMethod(*s)
return gensupport.MarshalJSON(raw, s.ForceSendFields) return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
} }
type ResourceRecordSetsListResponse struct { type ResourceRecordSetsListResponse struct {
@ -464,12 +520,20 @@ type ResourceRecordSetsListResponse struct {
// server regardless of whether the field is empty or not. This may be // server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests. // used to include empty fields in Patch requests.
ForceSendFields []string `json:"-"` ForceSendFields []string `json:"-"`
// NullFields is a list of field names (e.g. "Kind") to include in API
// requests with the JSON null value. By default, fields with empty
// values are omitted from API requests. However, any field with an
// empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
NullFields []string `json:"-"`
} }
func (s *ResourceRecordSetsListResponse) MarshalJSON() ([]byte, error) { func (s *ResourceRecordSetsListResponse) MarshalJSON() ([]byte, error) {
type noMethod ResourceRecordSetsListResponse type noMethod ResourceRecordSetsListResponse
raw := noMethod(*s) raw := noMethod(*s)
return gensupport.MarshalJSON(raw, s.ForceSendFields) return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
} }
// method id "dns.changes.create": // method id "dns.changes.create":
@ -481,6 +545,7 @@ type ChangesCreateCall struct {
change *Change change *Change
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// Create: Atomically update the ResourceRecordSet collection. // Create: Atomically update the ResourceRecordSet collection.
@ -508,27 +573,37 @@ func (c *ChangesCreateCall) Context(ctx context.Context) *ChangesCreateCall {
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ChangesCreateCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ChangesCreateCall) doRequest(alt string) (*http.Response, error) { func (c *ChangesCreateCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
body, err := googleapi.WithoutDataWrapper.JSONReader(c.change) body, err := googleapi.WithoutDataWrapper.JSONReader(c.change)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctype := "application/json" reqHeaders.Set("Content-Type", "application/json")
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}/changes") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}/changes")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("POST", urls, body) req, _ := http.NewRequest("POST", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"managedZone": c.managedZone, "managedZone": c.managedZone,
}) })
req.Header.Set("Content-Type", ctype) return gensupport.SendRequest(c.ctx_, c.s.client, req)
req.Header.Set("User-Agent", c.s.userAgent())
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.changes.create" call. // Do executes the "dns.changes.create" call.
@ -563,7 +638,8 @@ func (c *ChangesCreateCall) Do(opts ...googleapi.CallOption) (*Change, error) {
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -614,6 +690,7 @@ type ChangesGetCall struct {
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ifNoneMatch_ string ifNoneMatch_ string
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// Get: Fetch the representation of an existing Change. // Get: Fetch the representation of an existing Change.
@ -651,25 +728,36 @@ func (c *ChangesGetCall) Context(ctx context.Context) *ChangesGetCall {
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ChangesGetCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ChangesGetCall) doRequest(alt string) (*http.Response, error) { func (c *ChangesGetCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}/changes/{changeId}") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}/changes/{changeId}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"managedZone": c.managedZone, "managedZone": c.managedZone,
"changeId": c.changeId, "changeId": c.changeId,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.changes.get" call. // Do executes the "dns.changes.get" call.
@ -704,7 +792,8 @@ func (c *ChangesGetCall) Do(opts ...googleapi.CallOption) (*Change, error) {
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -760,6 +849,7 @@ type ChangesListCall struct {
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ifNoneMatch_ string ifNoneMatch_ string
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// List: Enumerate Changes to a ResourceRecordSet collection. // List: Enumerate Changes to a ResourceRecordSet collection.
@ -829,24 +919,35 @@ func (c *ChangesListCall) Context(ctx context.Context) *ChangesListCall {
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ChangesListCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ChangesListCall) doRequest(alt string) (*http.Response, error) { func (c *ChangesListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}/changes") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}/changes")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"managedZone": c.managedZone, "managedZone": c.managedZone,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.changes.list" call. // Do executes the "dns.changes.list" call.
@ -881,7 +982,8 @@ func (c *ChangesListCall) Do(opts ...googleapi.CallOption) (*ChangesListResponse
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -978,6 +1080,7 @@ type ManagedZonesCreateCall struct {
managedzone *ManagedZone managedzone *ManagedZone
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// Create: Create a new ManagedZone. // Create: Create a new ManagedZone.
@ -1004,26 +1107,36 @@ func (c *ManagedZonesCreateCall) Context(ctx context.Context) *ManagedZonesCreat
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ManagedZonesCreateCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ManagedZonesCreateCall) doRequest(alt string) (*http.Response, error) { func (c *ManagedZonesCreateCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
body, err := googleapi.WithoutDataWrapper.JSONReader(c.managedzone) body, err := googleapi.WithoutDataWrapper.JSONReader(c.managedzone)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctype := "application/json" reqHeaders.Set("Content-Type", "application/json")
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("POST", urls, body) req, _ := http.NewRequest("POST", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
}) })
req.Header.Set("Content-Type", ctype) return gensupport.SendRequest(c.ctx_, c.s.client, req)
req.Header.Set("User-Agent", c.s.userAgent())
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.managedZones.create" call. // Do executes the "dns.managedZones.create" call.
@ -1058,7 +1171,8 @@ func (c *ManagedZonesCreateCall) Do(opts ...googleapi.CallOption) (*ManagedZone,
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1100,6 +1214,7 @@ type ManagedZonesDeleteCall struct {
managedZone string managedZone string
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// Delete: Delete a previously created ManagedZone. // Delete: Delete a previously created ManagedZone.
@ -1126,21 +1241,32 @@ func (c *ManagedZonesDeleteCall) Context(ctx context.Context) *ManagedZonesDelet
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ManagedZonesDeleteCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ManagedZonesDeleteCall) doRequest(alt string) (*http.Response, error) { func (c *ManagedZonesDeleteCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("DELETE", urls, body) req, _ := http.NewRequest("DELETE", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"managedZone": c.managedZone, "managedZone": c.managedZone,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.managedZones.delete" call. // Do executes the "dns.managedZones.delete" call.
@ -1195,6 +1321,7 @@ type ManagedZonesGetCall struct {
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ifNoneMatch_ string ifNoneMatch_ string
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// Get: Fetch the representation of an existing ManagedZone. // Get: Fetch the representation of an existing ManagedZone.
@ -1231,24 +1358,35 @@ func (c *ManagedZonesGetCall) Context(ctx context.Context) *ManagedZonesGetCall
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ManagedZonesGetCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ManagedZonesGetCall) doRequest(alt string) (*http.Response, error) { func (c *ManagedZonesGetCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"managedZone": c.managedZone, "managedZone": c.managedZone,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.managedZones.get" call. // Do executes the "dns.managedZones.get" call.
@ -1283,7 +1421,8 @@ func (c *ManagedZonesGetCall) Do(opts ...googleapi.CallOption) (*ManagedZone, er
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1331,6 +1470,7 @@ type ManagedZonesListCall struct {
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ifNoneMatch_ string ifNoneMatch_ string
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// List: Enumerate ManagedZones that have been created but not yet // List: Enumerate ManagedZones that have been created but not yet
@ -1390,23 +1530,34 @@ func (c *ManagedZonesListCall) Context(ctx context.Context) *ManagedZonesListCal
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ManagedZonesListCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ManagedZonesListCall) doRequest(alt string) (*http.Response, error) { func (c *ManagedZonesListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.managedZones.list" call. // Do executes the "dns.managedZones.list" call.
@ -1441,7 +1592,8 @@ func (c *ManagedZonesListCall) Do(opts ...googleapi.CallOption) (*ManagedZonesLi
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1519,6 +1671,7 @@ type ProjectsGetCall struct {
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ifNoneMatch_ string ifNoneMatch_ string
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// Get: Fetch the representation of an existing Project. // Get: Fetch the representation of an existing Project.
@ -1554,23 +1707,34 @@ func (c *ProjectsGetCall) Context(ctx context.Context) *ProjectsGetCall {
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ProjectsGetCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ProjectsGetCall) doRequest(alt string) (*http.Response, error) { func (c *ProjectsGetCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.projects.get" call. // Do executes the "dns.projects.get" call.
@ -1605,7 +1769,8 @@ func (c *ProjectsGetCall) Do(opts ...googleapi.CallOption) (*Project, error) {
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil
@ -1647,6 +1812,7 @@ type ResourceRecordSetsListCall struct {
urlParams_ gensupport.URLParams urlParams_ gensupport.URLParams
ifNoneMatch_ string ifNoneMatch_ string
ctx_ context.Context ctx_ context.Context
header_ http.Header
} }
// List: Enumerate ResourceRecordSets that have been created but not yet // List: Enumerate ResourceRecordSets that have been created but not yet
@ -1715,24 +1881,35 @@ func (c *ResourceRecordSetsListCall) Context(ctx context.Context) *ResourceRecor
return c return c
} }
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *ResourceRecordSetsListCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *ResourceRecordSetsListCall) doRequest(alt string) (*http.Response, error) { func (c *ResourceRecordSetsListCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
if c.ifNoneMatch_ != "" {
reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
}
var body io.Reader = nil var body io.Reader = nil
c.urlParams_.Set("alt", alt) c.urlParams_.Set("alt", alt)
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}/rrsets") urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/managedZones/{managedZone}/rrsets")
urls += "?" + c.urlParams_.Encode() urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("GET", urls, body) req, _ := http.NewRequest("GET", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{ googleapi.Expand(req.URL, map[string]string{
"project": c.project, "project": c.project,
"managedZone": c.managedZone, "managedZone": c.managedZone,
}) })
req.Header.Set("User-Agent", c.s.userAgent()) return gensupport.SendRequest(c.ctx_, c.s.client, req)
if c.ifNoneMatch_ != "" {
req.Header.Set("If-None-Match", c.ifNoneMatch_)
}
if c.ctx_ != nil {
return ctxhttp.Do(c.ctx_, c.s.client, req)
}
return c.s.client.Do(req)
} }
// Do executes the "dns.resourceRecordSets.list" call. // Do executes the "dns.resourceRecordSets.list" call.
@ -1767,7 +1944,8 @@ func (c *ResourceRecordSetsListCall) Do(opts ...googleapi.CallOption) (*Resource
HTTPStatusCode: res.StatusCode, HTTPStatusCode: res.StatusCode,
}, },
} }
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { target := &ret
if err := json.NewDecoder(res.Body).Decode(target); err != nil {
return nil, err return nil, err
} }
return ret, nil return ret, nil

View file

@ -11,8 +11,8 @@ import (
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
// ResumableBuffer buffers data from an io.Reader to support uploading media in retryable chunks. // MediaBuffer buffers data from an io.Reader to support uploading media in retryable chunks.
type ResumableBuffer struct { type MediaBuffer struct {
media io.Reader media io.Reader
chunk []byte // The current chunk which is pending upload. The capacity is the chunk size. chunk []byte // The current chunk which is pending upload. The capacity is the chunk size.
@ -22,42 +22,42 @@ type ResumableBuffer struct {
off int64 off int64
} }
func NewResumableBuffer(media io.Reader, chunkSize int) *ResumableBuffer { func NewMediaBuffer(media io.Reader, chunkSize int) *MediaBuffer {
return &ResumableBuffer{media: media, chunk: make([]byte, 0, chunkSize)} return &MediaBuffer{media: media, chunk: make([]byte, 0, chunkSize)}
} }
// Chunk returns the current buffered chunk, the offset in the underlying media // Chunk returns the current buffered chunk, the offset in the underlying media
// from which the chunk is drawn, and the size of the chunk. // from which the chunk is drawn, and the size of the chunk.
// Successive calls to Chunk return the same chunk between calls to Next. // Successive calls to Chunk return the same chunk between calls to Next.
func (rb *ResumableBuffer) Chunk() (chunk io.Reader, off int64, size int, err error) { func (mb *MediaBuffer) Chunk() (chunk io.Reader, off int64, size int, err error) {
// There may already be data in chunk if Next has not been called since the previous call to Chunk. // There may already be data in chunk if Next has not been called since the previous call to Chunk.
if rb.err == nil && len(rb.chunk) == 0 { if mb.err == nil && len(mb.chunk) == 0 {
rb.err = rb.loadChunk() mb.err = mb.loadChunk()
} }
return bytes.NewReader(rb.chunk), rb.off, len(rb.chunk), rb.err return bytes.NewReader(mb.chunk), mb.off, len(mb.chunk), mb.err
} }
// loadChunk will read from media into chunk, up to the capacity of chunk. // loadChunk will read from media into chunk, up to the capacity of chunk.
func (rb *ResumableBuffer) loadChunk() error { func (mb *MediaBuffer) loadChunk() error {
bufSize := cap(rb.chunk) bufSize := cap(mb.chunk)
rb.chunk = rb.chunk[:bufSize] mb.chunk = mb.chunk[:bufSize]
read := 0 read := 0
var err error var err error
for err == nil && read < bufSize { for err == nil && read < bufSize {
var n int var n int
n, err = rb.media.Read(rb.chunk[read:]) n, err = mb.media.Read(mb.chunk[read:])
read += n read += n
} }
rb.chunk = rb.chunk[:read] mb.chunk = mb.chunk[:read]
return err return err
} }
// Next advances to the next chunk, which will be returned by the next call to Chunk. // Next advances to the next chunk, which will be returned by the next call to Chunk.
// Calls to Next without a corresponding prior call to Chunk will have no effect. // Calls to Next without a corresponding prior call to Chunk will have no effect.
func (rb *ResumableBuffer) Next() { func (mb *MediaBuffer) Next() {
rb.off += int64(len(rb.chunk)) mb.off += int64(len(mb.chunk))
rb.chunk = rb.chunk[0:0] mb.chunk = mb.chunk[0:0]
} }
type readerTyper struct { type readerTyper struct {

22
vendor/google.golang.org/api/gensupport/header.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gensupport
import (
"fmt"
"runtime"
"strings"
)
// GoogleClientHeader returns the value to use for the x-goog-api-client
// header, which is used internally by Google.
func GoogleClientHeader(generatorVersion, clientElement string) string {
elts := []string{"gl-go/" + strings.Replace(runtime.Version(), " ", "_", -1)}
if clientElement != "" {
elts = append(elts, clientElement)
}
elts = append(elts, fmt.Sprintf("gdcl/%s", generatorVersion))
return strings.Join(elts, " ")
}

View file

@ -12,29 +12,43 @@ import (
) )
// MarshalJSON returns a JSON encoding of schema containing only selected fields. // MarshalJSON returns a JSON encoding of schema containing only selected fields.
// A field is selected if: // A field is selected if any of the following is true:
// * it has a non-empty value, or // * it has a non-empty value
// * its field name is present in forceSendFields, and // * its field name is present in forceSendFields and it is not a nil pointer or nil interface
// * it is not a nil pointer or nil interface. // * its field name is present in nullFields.
// The JSON key for each selected field is taken from the field's json: struct tag. // The JSON key for each selected field is taken from the field's json: struct tag.
func MarshalJSON(schema interface{}, forceSendFields []string) ([]byte, error) { func MarshalJSON(schema interface{}, forceSendFields, nullFields []string) ([]byte, error) {
if len(forceSendFields) == 0 { if len(forceSendFields) == 0 && len(nullFields) == 0 {
return json.Marshal(schema) return json.Marshal(schema)
} }
mustInclude := make(map[string]struct{}) mustInclude := make(map[string]bool)
for _, f := range forceSendFields { for _, f := range forceSendFields {
mustInclude[f] = struct{}{} mustInclude[f] = true
}
useNull := make(map[string]bool)
useNullMaps := make(map[string]map[string]bool)
for _, nf := range nullFields {
parts := strings.SplitN(nf, ".", 2)
field := parts[0]
if len(parts) == 1 {
useNull[field] = true
} else {
if useNullMaps[field] == nil {
useNullMaps[field] = map[string]bool{}
}
useNullMaps[field][parts[1]] = true
}
} }
dataMap, err := schemaToMap(schema, mustInclude) dataMap, err := schemaToMap(schema, mustInclude, useNull, useNullMaps)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return json.Marshal(dataMap) return json.Marshal(dataMap)
} }
func schemaToMap(schema interface{}, mustInclude map[string]struct{}) (map[string]interface{}, error) { func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) {
m := make(map[string]interface{}) m := make(map[string]interface{})
s := reflect.ValueOf(schema) s := reflect.ValueOf(schema)
st := s.Type() st := s.Type()
@ -54,10 +68,36 @@ func schemaToMap(schema interface{}, mustInclude map[string]struct{}) (map[strin
v := s.Field(i) v := s.Field(i)
f := st.Field(i) f := st.Field(i)
if useNull[f.Name] {
if !isEmptyValue(v) {
return nil, fmt.Errorf("field %q in NullFields has non-empty value", f.Name)
}
m[tag.apiName] = nil
continue
}
if !includeField(v, f, mustInclude) { if !includeField(v, f, mustInclude) {
continue continue
} }
// If map fields are explicitly set to null, use a map[string]interface{}.
if f.Type.Kind() == reflect.Map && useNullMaps[f.Name] != nil {
ms, ok := v.Interface().(map[string]string)
if !ok {
return nil, fmt.Errorf("field %q has keys in NullFields but is not a map[string]string", f.Name)
}
mi := map[string]interface{}{}
for k, v := range ms {
mi[k] = v
}
for k := range useNullMaps[f.Name] {
mi[k] = nil
}
m[tag.apiName] = mi
continue
}
// nil maps are treated as empty maps. // nil maps are treated as empty maps.
if f.Type.Kind() == reflect.Map && v.IsNil() { if f.Type.Kind() == reflect.Map && v.IsNil() {
m[tag.apiName] = map[string]string{} m[tag.apiName] = map[string]string{}
@ -127,7 +167,7 @@ func parseJSONTag(val string) (jsonTag, error) {
} }
// Reports whether the struct field "f" with value "v" should be included in JSON output. // Reports whether the struct field "f" with value "v" should be included in JSON output.
func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]struct{}) bool { func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]bool) bool {
// The regular JSON encoding of a nil pointer is "null", which means "delete this field". // The regular JSON encoding of a nil pointer is "null", which means "delete this field".
// Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set. // Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set.
// However, many fields are not pointers, so there would be no way to delete these fields. // However, many fields are not pointers, so there would be no way to delete these fields.
@ -144,8 +184,7 @@ func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string
return false return false
} }
_, ok := mustInclude[f.Name] return mustInclude[f.Name] || !isEmptyValue(v)
return ok || !isEmptyValue(v)
} }
// isEmptyValue reports whether v is the empty value for its type. This // isEmptyValue reports whether v is the empty value for its type. This

57
vendor/google.golang.org/api/gensupport/jsonfloat.go generated vendored Normal file
View file

@ -0,0 +1,57 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gensupport
import (
"encoding/json"
"errors"
"fmt"
"math"
)
// JSONFloat64 is a float64 that supports proper unmarshaling of special float
// values in JSON, according to
// https://developers.google.com/protocol-buffers/docs/proto3#json. Although
// that is a proto-to-JSON spec, it applies to all Google APIs.
//
// The jsonpb package
// (https://github.com/golang/protobuf/blob/master/jsonpb/jsonpb.go) has
// similar functionality, but only for direct translation from proto messages
// to JSON.
type JSONFloat64 float64
func (f *JSONFloat64) UnmarshalJSON(data []byte) error {
var ff float64
if err := json.Unmarshal(data, &ff); err == nil {
*f = JSONFloat64(ff)
return nil
}
var s string
if err := json.Unmarshal(data, &s); err == nil {
switch s {
case "NaN":
ff = math.NaN()
case "Infinity":
ff = math.Inf(1)
case "-Infinity":
ff = math.Inf(-1)
default:
return fmt.Errorf("google.golang.org/api/internal: bad float string %q", s)
}
*f = JSONFloat64(ff)
return nil
}
return errors.New("google.golang.org/api/internal: data not float or string")
}

View file

@ -174,27 +174,126 @@ func typeHeader(contentType string) textproto.MIMEHeader {
// PrepareUpload determines whether the data in the supplied reader should be // PrepareUpload determines whether the data in the supplied reader should be
// uploaded in a single request, or in sequential chunks. // uploaded in a single request, or in sequential chunks.
// chunkSize is the size of the chunk that media should be split into. // chunkSize is the size of the chunk that media should be split into.
// If chunkSize is non-zero and the contents of media do not fit in a single //
// chunk (or there is an error reading media), then media will be returned as a // If chunkSize is zero, media is returned as the first value, and the other
// ResumableBuffer. Otherwise, media will be returned as a Reader. // two return values are nil, true.
//
// Otherwise, a MediaBuffer is returned, along with a bool indicating whether the
// contents of media fit in a single chunk.
// //
// After PrepareUpload has been called, media should no longer be used: the // After PrepareUpload has been called, media should no longer be used: the
// media content should be accessed via one of the return values. // media content should be accessed via one of the return values.
func PrepareUpload(media io.Reader, chunkSize int) (io.Reader, func PrepareUpload(media io.Reader, chunkSize int) (r io.Reader, mb *MediaBuffer, singleChunk bool) {
*ResumableBuffer) {
if chunkSize == 0 { // do not chunk if chunkSize == 0 { // do not chunk
return media, nil return media, nil, true
}
mb = NewMediaBuffer(media, chunkSize)
_, _, _, err := mb.Chunk()
// If err is io.EOF, we can upload this in a single request. Otherwise, err is
// either nil or a non-EOF error. If it is the latter, then the next call to
// mb.Chunk will return the same error. Returning a MediaBuffer ensures that this
// error will be handled at some point.
return nil, mb, err == io.EOF
}
// MediaInfo holds information for media uploads. It is intended for use by generated
// code only.
type MediaInfo struct {
// At most one of Media and MediaBuffer will be set.
media io.Reader
buffer *MediaBuffer
singleChunk bool
mType string
size int64 // mediaSize, if known. Used only for calls to progressUpdater_.
progressUpdater googleapi.ProgressUpdater
}
// NewInfoFromMedia should be invoked from the Media method of a call. It returns a
// MediaInfo populated with chunk size and content type, and a reader or MediaBuffer
// if needed.
func NewInfoFromMedia(r io.Reader, options []googleapi.MediaOption) *MediaInfo {
mi := &MediaInfo{}
opts := googleapi.ProcessMediaOptions(options)
if !opts.ForceEmptyContentType {
r, mi.mType = DetermineContentType(r, opts.ContentType)
}
mi.media, mi.buffer, mi.singleChunk = PrepareUpload(r, opts.ChunkSize)
return mi
}
// NewInfoFromResumableMedia should be invoked from the ResumableMedia method of a
// call. It returns a MediaInfo using the given reader, size and media type.
func NewInfoFromResumableMedia(r io.ReaderAt, size int64, mediaType string) *MediaInfo {
rdr := ReaderAtToReader(r, size)
rdr, mType := DetermineContentType(rdr, mediaType)
return &MediaInfo{
size: size,
mType: mType,
buffer: NewMediaBuffer(rdr, googleapi.DefaultUploadChunkSize),
media: nil,
singleChunk: false,
}
}
func (mi *MediaInfo) SetProgressUpdater(pu googleapi.ProgressUpdater) {
if mi != nil {
mi.progressUpdater = pu
}
}
// UploadType determines the type of upload: a single request, or a resumable
// series of requests.
func (mi *MediaInfo) UploadType() string {
if mi.singleChunk {
return "multipart"
}
return "resumable"
}
// UploadRequest sets up an HTTP request for media upload. It adds headers
// as necessary, and returns a replacement for the body.
func (mi *MediaInfo) UploadRequest(reqHeaders http.Header, body io.Reader) (newBody io.Reader, cleanup func()) {
cleanup = func() {}
if mi == nil {
return body, cleanup
}
var media io.Reader
if mi.media != nil {
// This only happens when the caller has turned off chunking. In that
// case, we write all of media in a single non-retryable request.
media = mi.media
} else if mi.singleChunk {
// The data fits in a single chunk, which has now been read into the MediaBuffer.
// We obtain that chunk so we can write it in a single request. The request can
// be retried because the data is stored in the MediaBuffer.
media, _, _, _ = mi.buffer.Chunk()
}
if media != nil {
combined, ctype := CombineBodyMedia(body, "application/json", media, mi.mType)
cleanup = func() { combined.Close() }
reqHeaders.Set("Content-Type", ctype)
body = combined
}
if mi.buffer != nil && mi.mType != "" && !mi.singleChunk {
reqHeaders.Set("X-Upload-Content-Type", mi.mType)
}
return body, cleanup
}
// ResumableUpload returns an appropriately configured ResumableUpload value if the
// upload is resumable, or nil otherwise.
func (mi *MediaInfo) ResumableUpload(locURI string) *ResumableUpload {
if mi == nil || mi.singleChunk {
return nil
}
return &ResumableUpload{
URI: locURI,
Media: mi.buffer,
MediaType: mi.mType,
Callback: func(curr int64) {
if mi.progressUpdater != nil {
mi.progressUpdater(curr, mi.size)
}
},
} }
rb := NewResumableBuffer(media, chunkSize)
rdr, _, _, err := rb.Chunk()
if err == io.EOF { // we can upload this in a single request
return rdr, nil
}
// err might be a non-EOF error. If it is, the next call to rb.Chunk will
// return the same error. Returning a ResumableBuffer ensures that this error
// will be handled at some point.
return nil, rb
} }

View file

@ -5,6 +5,7 @@
package gensupport package gensupport
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -12,14 +13,9 @@ import (
"time" "time"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
) )
const ( const (
// statusResumeIncomplete is the code returned by the Google uploader
// when the transfer is not yet complete.
statusResumeIncomplete = 308
// statusTooManyRequests is returned by the storage API if the // statusTooManyRequests is returned by the storage API if the
// per-project limits have been temporarily exceeded. The request // per-project limits have been temporarily exceeded. The request
// should be retried. // should be retried.
@ -35,7 +31,7 @@ type ResumableUpload struct {
URI string URI string
UserAgent string // User-Agent for header of the request UserAgent string // User-Agent for header of the request
// Media is the object being uploaded. // Media is the object being uploaded.
Media *ResumableBuffer Media *MediaBuffer
// MediaType defines the media type, e.g. "image/jpeg". // MediaType defines the media type, e.g. "image/jpeg".
MediaType string MediaType string
@ -80,8 +76,23 @@ func (rx *ResumableUpload) doUploadRequest(ctx context.Context, data io.Reader,
req.Header.Set("Content-Range", contentRange) req.Header.Set("Content-Range", contentRange)
req.Header.Set("Content-Type", rx.MediaType) req.Header.Set("Content-Type", rx.MediaType)
req.Header.Set("User-Agent", rx.UserAgent) req.Header.Set("User-Agent", rx.UserAgent)
return ctxhttp.Do(ctx, rx.Client, req)
// Google's upload endpoint uses status code 308 for a
// different purpose than the "308 Permanent Redirect"
// since-standardized in RFC 7238. Because of the conflict in
// semantics, Google added this new request header which
// causes it to not use "308" and instead reply with 200 OK
// and sets the upload-specific "X-HTTP-Status-Code-Override:
// 308" response header.
req.Header.Set("X-GUploader-No-308", "yes")
return SendRequest(ctx, rx.Client, req)
}
func statusResumeIncomplete(resp *http.Response) bool {
// This is how the server signals "status resume incomplete"
// when X-GUploader-No-308 is set to "yes":
return resp != nil && resp.Header.Get("X-Http-Status-Code-Override") == "308"
} }
// reportProgress calls a user-supplied callback to report upload progress. // reportProgress calls a user-supplied callback to report upload progress.
@ -112,11 +123,17 @@ func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, e
return res, err return res, err
} }
if res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK { // We sent "X-GUploader-No-308: yes" (see comment elsewhere in
// this file), so we don't expect to get a 308.
if res.StatusCode == 308 {
return nil, errors.New("unexpected 308 response status code")
}
if res.StatusCode == http.StatusOK {
rx.reportProgress(off, off+int64(size)) rx.reportProgress(off, off+int64(size))
} }
if res.StatusCode == statusResumeIncomplete { if statusResumeIncomplete(res) {
rx.Media.Next() rx.Media.Next()
} }
return res, nil return res, nil
@ -135,6 +152,8 @@ func contextDone(ctx context.Context) bool {
// It retries using the provided back off strategy until cancelled or the // It retries using the provided back off strategy until cancelled or the
// strategy indicates to stop retrying. // strategy indicates to stop retrying.
// It is called from the auto-generated API code and is not visible to the user. // It is called from the auto-generated API code and is not visible to the user.
// Before sending an HTTP request, Upload calls any registered hook functions,
// and calls the returned functions after the request returns (see send.go).
// rx is private to the auto-generated API code. // rx is private to the auto-generated API code.
// Exactly one of resp or err will be nil. If resp is non-nil, the caller must call resp.Body.Close. // Exactly one of resp or err will be nil. If resp is non-nil, the caller must call resp.Body.Close.
func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) { func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) {
@ -176,7 +195,7 @@ func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err
// If the chunk was uploaded successfully, but there's still // If the chunk was uploaded successfully, but there's still
// more to go, upload the next chunk without any delay. // more to go, upload the next chunk without any delay.
if status == statusResumeIncomplete { if statusResumeIncomplete(resp) {
pause = 0 pause = 0
backoff.Reset() backoff.Reset()
resp.Body.Close() resp.Body.Close()

View file

@ -1,3 +1,17 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gensupport package gensupport
import ( import (
@ -55,23 +69,17 @@ func DefaultBackoffStrategy() BackoffStrategy {
// shouldRetry returns true if the HTTP response / error indicates that the // shouldRetry returns true if the HTTP response / error indicates that the
// request should be attempted again. // request should be attempted again.
func shouldRetry(status int, err error) bool { func shouldRetry(status int, err error) bool {
// Retry for 5xx response codes. if 500 <= status && status <= 599 {
if 500 <= status && status < 600 {
return true return true
} }
// Retry on statusTooManyRequests{
if status == statusTooManyRequests { if status == statusTooManyRequests {
return true return true
} }
// Retry on unexpected EOFs and temporary network errors.
if err == io.ErrUnexpectedEOF { if err == io.ErrUnexpectedEOF {
return true return true
} }
if err, ok := err.(net.Error); ok { if err, ok := err.(net.Error); ok {
return err.Temporary() return err.Temporary()
} }
return false return false
} }

61
vendor/google.golang.org/api/gensupport/send.go generated vendored Normal file
View file

@ -0,0 +1,61 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gensupport
import (
"errors"
"net/http"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)
// Hook is the type of a function that is called once before each HTTP request
// that is sent by a generated API. It returns a function that is called after
// the request returns.
// Hooks are not called if the context is nil.
type Hook func(ctx context.Context, req *http.Request) func(resp *http.Response)
var hooks []Hook
// RegisterHook registers a Hook to be called before each HTTP request by a
// generated API. Hooks are called in the order they are registered. Each
// hook can return a function; if it is non-nil, it is called after the HTTP
// request returns. These functions are called in the reverse order.
// RegisterHook should not be called concurrently with itself or SendRequest.
func RegisterHook(h Hook) {
hooks = append(hooks, h)
}
// SendRequest sends a single HTTP request using the given client.
// If ctx is non-nil, it calls all hooks, then sends the request with
// ctxhttp.Do, then calls any functions returned by the hooks in reverse order.
func SendRequest(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
// Disallow Accept-Encoding because it interferes with the automatic gzip handling
// done by the default http.Transport. See https://github.com/google/google-api-go-client/issues/219.
if _, ok := req.Header["Accept-Encoding"]; ok {
return nil, errors.New("google api: custom Accept-Encoding headers not allowed")
}
if ctx == nil {
return client.Do(req)
}
// Call hooks in order of registration, store returned funcs.
post := make([]func(resp *http.Response), len(hooks))
for i, h := range hooks {
fn := h(ctx, req)
post[i] = fn
}
// Send request.
resp, err := ctxhttp.Do(ctx, client, req)
// Call returned funcs in reverse order.
for i := len(post) - 1; i >= 0; i-- {
if fn := post[i]; fn != nil {
fn(resp)
}
}
return resp, err
}

View file

@ -50,7 +50,7 @@ const (
// UserAgent is the header string used to identify this package. // UserAgent is the header string used to identify this package.
UserAgent = "google-api-go-client/" + Version UserAgent = "google-api-go-client/" + Version
// The default chunk size to use for resumable uplods if not specified by the user. // The default chunk size to use for resumable uploads if not specified by the user.
DefaultUploadChunkSize = 8 * 1024 * 1024 DefaultUploadChunkSize = 8 * 1024 * 1024
// The minimum chunk size that can be used for resumable uploads. All // The minimum chunk size that can be used for resumable uploads. All
@ -149,12 +149,12 @@ func IsNotModified(err error) bool {
// CheckMediaResponse returns an error (of type *Error) if the response // CheckMediaResponse returns an error (of type *Error) if the response
// status code is not 2xx. Unlike CheckResponse it does not assume the // status code is not 2xx. Unlike CheckResponse it does not assume the
// body is a JSON error document. // body is a JSON error document.
// It is the caller's responsibility to close res.Body.
func CheckMediaResponse(res *http.Response) error { func CheckMediaResponse(res *http.Response) error {
if res.StatusCode >= 200 && res.StatusCode <= 299 { if res.StatusCode >= 200 && res.StatusCode <= 299 {
return nil return nil
} }
slurp, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20)) slurp, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
res.Body.Close()
return &Error{ return &Error{
Code: res.StatusCode, Code: res.StatusCode,
Body: string(slurp), Body: string(slurp),
@ -278,41 +278,15 @@ func ResolveRelative(basestr, relstr string) string {
return us return us
} }
// has4860Fix is whether this Go environment contains the fix for
// http://golang.org/issue/4860
var has4860Fix bool
// init initializes has4860Fix by checking the behavior of the net/http package.
func init() {
r := http.Request{
URL: &url.URL{
Scheme: "http",
Opaque: "//opaque",
},
}
b := &bytes.Buffer{}
r.Write(b)
has4860Fix = bytes.HasPrefix(b.Bytes(), []byte("GET http"))
}
// SetOpaque sets u.Opaque from u.Path such that HTTP requests to it
// don't alter any hex-escaped characters in u.Path.
func SetOpaque(u *url.URL) {
u.Opaque = "//" + u.Host + u.Path
if !has4860Fix {
u.Opaque = u.Scheme + ":" + u.Opaque
}
}
// Expand subsitutes any {encoded} strings in the URL passed in using // Expand subsitutes any {encoded} strings in the URL passed in using
// the map supplied. // the map supplied.
// //
// This calls SetOpaque to avoid encoding of the parameters in the URL path. // This calls SetOpaque to avoid encoding of the parameters in the URL path.
func Expand(u *url.URL, expansions map[string]string) { func Expand(u *url.URL, expansions map[string]string) {
expanded, err := uritemplates.Expand(u.Path, expansions) escaped, unescaped, err := uritemplates.Expand(u.Path, expansions)
if err == nil { if err == nil {
u.Path = expanded u.Path = unescaped
SetOpaque(u) u.RawPath = escaped
} }
} }
@ -421,4 +395,12 @@ type userIP string
func (i userIP) Get() (string, string) { return "userIp", string(i) } func (i userIP) Get() (string, string) { return "userIp", string(i) }
// Trace returns a CallOption that enables diagnostic tracing for a call.
// traceToken is an ID supplied by Google support.
func Trace(traceToken string) CallOption { return traceTok(traceToken) }
type traceTok string
func (t traceTok) Get() (string, string) { return "trace", "token:" + string(t) }
// TODO: Fields too // TODO: Fields too

View file

@ -34,11 +34,37 @@ func pctEncode(src []byte) []byte {
return dst return dst
} }
func escape(s string, allowReserved bool) string { // pairWriter is a convenience struct which allows escaped and unescaped
// versions of the template to be written in parallel.
type pairWriter struct {
escaped, unescaped bytes.Buffer
}
// Write writes the provided string directly without any escaping.
func (w *pairWriter) Write(s string) {
w.escaped.WriteString(s)
w.unescaped.WriteString(s)
}
// Escape writes the provided string, escaping the string for the
// escaped output.
func (w *pairWriter) Escape(s string, allowReserved bool) {
w.unescaped.WriteString(s)
if allowReserved { if allowReserved {
return string(reserved.ReplaceAllFunc([]byte(s), pctEncode)) w.escaped.Write(reserved.ReplaceAllFunc([]byte(s), pctEncode))
} else {
w.escaped.Write(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
} }
return string(unreserved.ReplaceAllFunc([]byte(s), pctEncode)) }
// Escaped returns the escaped string.
func (w *pairWriter) Escaped() string {
return w.escaped.String()
}
// Unescaped returns the unescaped string.
func (w *pairWriter) Unescaped() string {
return w.unescaped.String()
} }
// A uriTemplate is a parsed representation of a URI template. // A uriTemplate is a parsed representation of a URI template.
@ -170,18 +196,20 @@ func parseTerm(term string) (result templateTerm, err error) {
return result, err return result, err
} }
// Expand expands a URI template with a set of values to produce a string. // Expand expands a URI template with a set of values to produce the
func (t *uriTemplate) Expand(values map[string]string) string { // resultant URI. Two forms of the result are returned: one with all the
var buf bytes.Buffer // elements escaped, and one with the elements unescaped.
func (t *uriTemplate) Expand(values map[string]string) (escaped, unescaped string) {
var w pairWriter
for _, p := range t.parts { for _, p := range t.parts {
p.expand(&buf, values) p.expand(&w, values)
} }
return buf.String() return w.Escaped(), w.Unescaped()
} }
func (tp *templatePart) expand(buf *bytes.Buffer, values map[string]string) { func (tp *templatePart) expand(w *pairWriter, values map[string]string) {
if len(tp.raw) > 0 { if len(tp.raw) > 0 {
buf.WriteString(tp.raw) w.Write(tp.raw)
return return
} }
var first = true var first = true
@ -191,30 +219,30 @@ func (tp *templatePart) expand(buf *bytes.Buffer, values map[string]string) {
continue continue
} }
if first { if first {
buf.WriteString(tp.first) w.Write(tp.first)
first = false first = false
} else { } else {
buf.WriteString(tp.sep) w.Write(tp.sep)
} }
tp.expandString(buf, term, value) tp.expandString(w, term, value)
} }
} }
func (tp *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) { func (tp *templatePart) expandName(w *pairWriter, name string, empty bool) {
if tp.named { if tp.named {
buf.WriteString(name) w.Write(name)
if empty { if empty {
buf.WriteString(tp.ifemp) w.Write(tp.ifemp)
} else { } else {
buf.WriteString("=") w.Write("=")
} }
} }
} }
func (tp *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) { func (tp *templatePart) expandString(w *pairWriter, t templateTerm, s string) {
if len(s) > t.truncate && t.truncate > 0 { if len(s) > t.truncate && t.truncate > 0 {
s = s[:t.truncate] s = s[:t.truncate]
} }
tp.expandName(buf, t.name, len(s) == 0) tp.expandName(w, t.name, len(s) == 0)
buf.WriteString(escape(s, tp.allowReserved)) w.Escape(s, tp.allowReserved)
} }

View file

@ -4,10 +4,14 @@
package uritemplates package uritemplates
func Expand(path string, values map[string]string) (string, error) { // Expand parses then expands a URI template with a set of values to produce
// the resultant URI. Two forms of the result are returned: one with all the
// elements escaped, and one with the elements unescaped.
func Expand(path string, values map[string]string) (escaped, unescaped string, err error) {
template, err := parse(path) template, err := parse(path)
if err != nil { if err != nil {
return "", err return "", "", err
} }
return template.Expand(values), nil escaped, unescaped = template.Expand(values)
return escaped, unescaped, nil
} }

View file

@ -6,6 +6,7 @@ package googleapi
import ( import (
"encoding/json" "encoding/json"
"errors"
"strconv" "strconv"
) )
@ -149,6 +150,25 @@ func (s Float64s) MarshalJSON() ([]byte, error) {
}) })
} }
// RawMessage is a raw encoded JSON value.
// It is identical to json.RawMessage, except it does not suffer from
// https://golang.org/issue/14493.
type RawMessage []byte
// MarshalJSON returns m.
func (m RawMessage) MarshalJSON() ([]byte, error) {
return m, nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("googleapi.RawMessage: UnmarshalJSON on nil pointer")
}
*m = append((*m)[:0], data...)
return nil
}
/* /*
* Helper routines for simplifying the creation of optional fields of basic type. * Helper routines for simplifying the creation of optional fields of basic type.
*/ */

Some files were not shown because too many files have changed in this diff Show more