Merge pull request #25679 from quinton-hoole/2016-05-16-update-googleapi-package

Automatic merge from submit-queue

Update vendor package google.golang.org/api/googleapi.
This commit is contained in:
k8s-merge-robot 2016-05-20 03:53:18 -07:00
commit e27f20780d
16 changed files with 7700 additions and 5851 deletions

14
Godeps/Godeps.json generated
View File

@ -1,7 +1,7 @@
{
"ImportPath": "k8s.io/kubernetes",
"GoVersion": "go1.6",
"GodepVersion": "v63",
"GodepVersion": "v67",
"Packages": [
"github.com/ugorji/go/codec/codecgen",
"github.com/onsi/ginkgo/ginkgo",
@ -2003,27 +2003,27 @@
},
{
"ImportPath": "google.golang.org/api/cloudmonitoring/v2beta2",
"Rev": "77e7d383beb96054547729f49c372b3d01e196ff"
"Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037"
},
{
"ImportPath": "google.golang.org/api/compute/v1",
"Rev": "77e7d383beb96054547729f49c372b3d01e196ff"
"Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037"
},
{
"ImportPath": "google.golang.org/api/container/v1",
"Rev": "77e7d383beb96054547729f49c372b3d01e196ff"
"Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037"
},
{
"ImportPath": "google.golang.org/api/gensupport",
"Rev": "77e7d383beb96054547729f49c372b3d01e196ff"
"Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037"
},
{
"ImportPath": "google.golang.org/api/googleapi",
"Rev": "77e7d383beb96054547729f49c372b3d01e196ff"
"Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037"
},
{
"ImportPath": "google.golang.org/api/googleapi/internal/uritemplates",
"Rev": "77e7d383beb96054547729f49c372b3d01e196ff"
"Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037"
},
{
"ImportPath": "google.golang.org/cloud/compute/metadata",

View File

@ -1,14 +1,14 @@
{
"kind": "discovery#restDescription",
"etag": "\"ye6orv2F-1npMW3u9suM3a7C5Bo/avMl03W4ktR_Q7PS4O3ogtyT8Dc\"",
"etag": "\"bRFOOrZKfO9LweMbPqu0kcu6De8/A2G_NAa29vne9MPSojupRQ5bVuo\"",
"discoveryVersion": "v1",
"id": "cloudmonitoring:v2beta2",
"name": "cloudmonitoring",
"canonicalName": "Cloud Monitoring",
"version": "v2beta2",
"revision": "20150713",
"revision": "20160314",
"title": "Cloud Monitoring API",
"description": "API for accessing Google Cloud and API monitoring data.",
"description": "Accesses Google Cloud Monitoring data.",
"ownerDomain": "google.com",
"ownerName": "Google",
"icons": {

View File

@ -754,23 +754,6 @@ func (r *MetricDescriptorsService) Create(project string, metricdescriptor *Metr
return c
}
// QuotaUser sets the optional parameter "quotaUser": Available to use
// for quota purposes for server-side applications. Can be any arbitrary
// string assigned to a user, but should not exceed 40 characters.
// Overrides userIp if both are provided.
func (c *MetricDescriptorsCreateCall) QuotaUser(quotaUser string) *MetricDescriptorsCreateCall {
c.urlParams_.Set("quotaUser", quotaUser)
return c
}
// UserIP sets the optional parameter "userIp": IP address of the site
// where the request originates. Use this if you want to enforce
// per-user limits.
func (c *MetricDescriptorsCreateCall) UserIP(userIP string) *MetricDescriptorsCreateCall {
c.urlParams_.Set("userIp", userIP)
return c
}
// Fields allows partial responses to be retrieved. See
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
// for more information.
@ -816,7 +799,8 @@ func (c *MetricDescriptorsCreateCall) doRequest(alt string) (*http.Response, err
// returned at all) in error.(*googleapi.Error).Header. Use
// googleapi.IsNotModified to check whether the returned error was
// because http.StatusNotModified was returned.
func (c *MetricDescriptorsCreateCall) Do() (*MetricDescriptor, error) {
func (c *MetricDescriptorsCreateCall) Do(opts ...googleapi.CallOption) (*MetricDescriptor, error) {
gensupport.SetOptions(c.urlParams_, opts...)
res, err := c.doRequest("json")
if res != nil && res.StatusCode == http.StatusNotModified {
if res.Body != nil {
@ -892,23 +876,6 @@ func (r *MetricDescriptorsService) Delete(project string, metric string) *Metric
return c
}
// QuotaUser sets the optional parameter "quotaUser": Available to use
// for quota purposes for server-side applications. Can be any arbitrary
// string assigned to a user, but should not exceed 40 characters.
// Overrides userIp if both are provided.
func (c *MetricDescriptorsDeleteCall) QuotaUser(quotaUser string) *MetricDescriptorsDeleteCall {
c.urlParams_.Set("quotaUser", quotaUser)
return c
}
// UserIP sets the optional parameter "userIp": IP address of the site
// where the request originates. Use this if you want to enforce
// per-user limits.
func (c *MetricDescriptorsDeleteCall) UserIP(userIP string) *MetricDescriptorsDeleteCall {
c.urlParams_.Set("userIp", userIP)
return c
}
// Fields allows partial responses to be retrieved. See
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
// for more information.
@ -949,7 +916,8 @@ func (c *MetricDescriptorsDeleteCall) doRequest(alt string) (*http.Response, err
// response was returned at all) in error.(*googleapi.Error).Header. Use
// googleapi.IsNotModified to check whether the returned error was
// because http.StatusNotModified was returned.
func (c *MetricDescriptorsDeleteCall) Do() (*DeleteMetricDescriptorResponse, error) {
func (c *MetricDescriptorsDeleteCall) Do(opts ...googleapi.CallOption) (*DeleteMetricDescriptorResponse, error) {
gensupport.SetOptions(c.urlParams_, opts...)
res, err := c.doRequest("json")
if res != nil && res.StatusCode == http.StatusNotModified {
if res.Body != nil {
@ -1062,23 +1030,6 @@ func (c *MetricDescriptorsListCall) Query(query string) *MetricDescriptorsListCa
return c
}
// QuotaUser sets the optional parameter "quotaUser": Available to use
// for quota purposes for server-side applications. Can be any arbitrary
// string assigned to a user, but should not exceed 40 characters.
// Overrides userIp if both are provided.
func (c *MetricDescriptorsListCall) QuotaUser(quotaUser string) *MetricDescriptorsListCall {
c.urlParams_.Set("quotaUser", quotaUser)
return c
}
// UserIP sets the optional parameter "userIp": IP address of the site
// where the request originates. Use this if you want to enforce
// per-user limits.
func (c *MetricDescriptorsListCall) UserIP(userIP string) *MetricDescriptorsListCall {
c.urlParams_.Set("userIp", userIP)
return c
}
// Fields allows partial responses to be retrieved. See
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
// for more information.
@ -1131,7 +1082,8 @@ func (c *MetricDescriptorsListCall) doRequest(alt string) (*http.Response, error
// response was returned at all) in error.(*googleapi.Error).Header. Use
// googleapi.IsNotModified to check whether the returned error was
// because http.StatusNotModified was returned.
func (c *MetricDescriptorsListCall) Do() (*ListMetricDescriptorsResponse, error) {
func (c *MetricDescriptorsListCall) Do(opts ...googleapi.CallOption) (*ListMetricDescriptorsResponse, error) {
gensupport.SetOptions(c.urlParams_, opts...)
res, err := c.doRequest("json")
if res != nil && res.StatusCode == http.StatusNotModified {
if res.Body != nil {
@ -1208,6 +1160,27 @@ func (c *MetricDescriptorsListCall) Do() (*ListMetricDescriptorsResponse, error)
}
// Pages invokes f for each page of results.
// A non-nil error returned from f will halt the iteration.
// The provided context supersedes any context provided to the Context method.
func (c *MetricDescriptorsListCall) Pages(ctx context.Context, f func(*ListMetricDescriptorsResponse) error) error {
c.ctx_ = ctx
defer c.PageToken(c.urlParams_.Get("pageToken")) // reset paging to original point
for {
x, err := c.Do()
if err != nil {
return err
}
if err := f(x); err != nil {
return err
}
if x.NextPageToken == "" {
return nil
}
c.PageToken(x.NextPageToken)
}
}
// method id "cloudmonitoring.timeseries.list":
type TimeseriesListCall struct {
@ -1289,15 +1262,6 @@ func (c *TimeseriesListCall) PageToken(pageToken string) *TimeseriesListCall {
return c
}
// QuotaUser sets the optional parameter "quotaUser": Available to use
// for quota purposes for server-side applications. Can be any arbitrary
// string assigned to a user, but should not exceed 40 characters.
// Overrides userIp if both are provided.
func (c *TimeseriesListCall) QuotaUser(quotaUser string) *TimeseriesListCall {
c.urlParams_.Set("quotaUser", quotaUser)
return c
}
// Timespan sets the optional parameter "timespan": Length of the time
// interval to query, which is an alternative way to declare the
// interval: (youngest - timespan, youngest]. The timespan and oldest
@ -1316,14 +1280,6 @@ func (c *TimeseriesListCall) Timespan(timespan string) *TimeseriesListCall {
return c
}
// UserIP sets the optional parameter "userIp": IP address of the site
// where the request originates. Use this if you want to enforce
// per-user limits.
func (c *TimeseriesListCall) UserIP(userIP string) *TimeseriesListCall {
c.urlParams_.Set("userIp", userIP)
return c
}
// Window sets the optional parameter "window": The sampling window. At
// most one data point will be returned for each window in the requested
// time interval. This parameter is only valid for non-cumulative metric
@ -1391,7 +1347,8 @@ func (c *TimeseriesListCall) doRequest(alt string) (*http.Response, error) {
// returned at all) in error.(*googleapi.Error).Header. Use
// googleapi.IsNotModified to check whether the returned error was
// because http.StatusNotModified was returned.
func (c *TimeseriesListCall) Do() (*ListTimeseriesResponse, error) {
func (c *TimeseriesListCall) Do(opts ...googleapi.CallOption) (*ListTimeseriesResponse, error) {
gensupport.SetOptions(c.urlParams_, opts...)
res, err := c.doRequest("json")
if res != nil && res.StatusCode == http.StatusNotModified {
if res.Body != nil {
@ -1518,6 +1475,27 @@ func (c *TimeseriesListCall) Do() (*ListTimeseriesResponse, error) {
}
// Pages invokes f for each page of results.
// A non-nil error returned from f will halt the iteration.
// The provided context supersedes any context provided to the Context method.
func (c *TimeseriesListCall) Pages(ctx context.Context, f func(*ListTimeseriesResponse) error) error {
c.ctx_ = ctx
defer c.PageToken(c.urlParams_.Get("pageToken")) // reset paging to original point
for {
x, err := c.Do()
if err != nil {
return err
}
if err := f(x); err != nil {
return err
}
if x.NextPageToken == "" {
return nil
}
c.PageToken(x.NextPageToken)
}
}
// method id "cloudmonitoring.timeseries.write":
type TimeseriesWriteCall struct {
@ -1543,23 +1521,6 @@ func (r *TimeseriesService) Write(project string, writetimeseriesrequest *WriteT
return c
}
// QuotaUser sets the optional parameter "quotaUser": Available to use
// for quota purposes for server-side applications. Can be any arbitrary
// string assigned to a user, but should not exceed 40 characters.
// Overrides userIp if both are provided.
func (c *TimeseriesWriteCall) QuotaUser(quotaUser string) *TimeseriesWriteCall {
c.urlParams_.Set("quotaUser", quotaUser)
return c
}
// UserIP sets the optional parameter "userIp": IP address of the site
// where the request originates. Use this if you want to enforce
// per-user limits.
func (c *TimeseriesWriteCall) UserIP(userIP string) *TimeseriesWriteCall {
c.urlParams_.Set("userIp", userIP)
return c
}
// Fields allows partial responses to be retrieved. See
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
// for more information.
@ -1605,7 +1566,8 @@ func (c *TimeseriesWriteCall) doRequest(alt string) (*http.Response, error) {
// returned at all) in error.(*googleapi.Error).Header. Use
// googleapi.IsNotModified to check whether the returned error was
// because http.StatusNotModified was returned.
func (c *TimeseriesWriteCall) Do() (*WriteTimeseriesResponse, error) {
func (c *TimeseriesWriteCall) Do(opts ...googleapi.CallOption) (*WriteTimeseriesResponse, error) {
gensupport.SetOptions(c.urlParams_, opts...)
res, err := c.doRequest("json")
if res != nil && res.StatusCode == http.StatusNotModified {
if res.Body != nil {
@ -1745,15 +1707,6 @@ func (c *TimeseriesDescriptorsListCall) PageToken(pageToken string) *TimeseriesD
return c
}
// QuotaUser sets the optional parameter "quotaUser": Available to use
// for quota purposes for server-side applications. Can be any arbitrary
// string assigned to a user, but should not exceed 40 characters.
// Overrides userIp if both are provided.
func (c *TimeseriesDescriptorsListCall) QuotaUser(quotaUser string) *TimeseriesDescriptorsListCall {
c.urlParams_.Set("quotaUser", quotaUser)
return c
}
// Timespan sets the optional parameter "timespan": Length of the time
// interval to query, which is an alternative way to declare the
// interval: (youngest - timespan, youngest]. The timespan and oldest
@ -1772,14 +1725,6 @@ func (c *TimeseriesDescriptorsListCall) Timespan(timespan string) *TimeseriesDes
return c
}
// UserIP sets the optional parameter "userIp": IP address of the site
// where the request originates. Use this if you want to enforce
// per-user limits.
func (c *TimeseriesDescriptorsListCall) UserIP(userIP string) *TimeseriesDescriptorsListCall {
c.urlParams_.Set("userIp", userIP)
return c
}
// Window sets the optional parameter "window": The sampling window. At
// most one data point will be returned for each window in the requested
// time interval. This parameter is only valid for non-cumulative metric
@ -1848,7 +1793,8 @@ func (c *TimeseriesDescriptorsListCall) doRequest(alt string) (*http.Response, e
// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
// whether the returned error was because http.StatusNotModified was
// returned.
func (c *TimeseriesDescriptorsListCall) Do() (*ListTimeseriesDescriptorsResponse, error) {
func (c *TimeseriesDescriptorsListCall) Do(opts ...googleapi.CallOption) (*ListTimeseriesDescriptorsResponse, error) {
gensupport.SetOptions(c.urlParams_, opts...)
res, err := c.doRequest("json")
if res != nil && res.StatusCode == http.StatusNotModified {
if res.Body != nil {
@ -1974,3 +1920,24 @@ func (c *TimeseriesDescriptorsListCall) Do() (*ListTimeseriesDescriptorsResponse
// }
}
// Pages invokes f for each page of results.
// A non-nil error returned from f will halt the iteration.
// The provided context supersedes any context provided to the Context method.
func (c *TimeseriesDescriptorsListCall) Pages(ctx context.Context, f func(*ListTimeseriesDescriptorsResponse) error) error {
c.ctx_ = ctx
defer c.PageToken(c.urlParams_.Get("pageToken")) // reset paging to original point
for {
x, err := c.Do()
if err != nil {
return err
}
if err := f(x); err != nil {
return err
}
if x.NextPageToken == "" {
return nil
}
c.PageToken(x.NextPageToken)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
{
"kind": "discovery#restDescription",
"etag": "\"ye6orv2F-1npMW3u9suM3a7C5Bo/FD4oCwspiZqN6eCjsBmxDzsj5B8\"",
"etag": "\"jQLIOHBVnDZie4rQHGH1WJF-INE/cpP4K9eaLrLwMGtsdl5oXjxb8rw\"",
"discoveryVersion": "v1",
"id": "container:v1",
"name": "container",
"version": "v1",
"revision": "20150603",
"revision": "20160421",
"title": "Google Container Engine API",
"description": "The Google Container Engine API is used for building and managing container based applications, powered by the open source Kubernetes technology.",
"description": "Builds and manages clusters that run container-based applications, powered by open source Kubernetes technology.",
"ownerDomain": "google.com",
"ownerName": "Google",
"icons": {
@ -17,7 +17,7 @@
"documentationLink": "https://cloud.google.com/container-engine/",
"protocol": "rest",
"baseUrl": "https://container.googleapis.com/",
"basePath": "/",
"basePath": "",
"rootUrl": "https://container.googleapis.com/",
"servicePath": "",
"batchPath": "batch",
@ -121,6 +121,13 @@
"items": {
"$ref": "Cluster"
}
},
"missingZones": {
"type": "array",
"description": "If any zones are listed here, the list of clusters returned may be missing those zones.",
"items": {
"type": "string"
}
}
}
},
@ -139,33 +146,55 @@
},
"initialNodeCount": {
"type": "integer",
"description": "The number of nodes to create in this cluster. You must ensure that your Compute Engine [resource quota](/compute/docs/resource-quotas) is sufficient for this number of instances. You must also have available firewall and routes quota.",
"description": "The number of nodes to create in this cluster. You must ensure that your Compute Engine resource quota is sufficient for this number of instances. You must also have available firewall and routes quota. For requests, this field should only be used in lieu of a \"node_pool\" object, since this configuration (along with the \"node_config\") will be used to create a \"NodePool\" object with an auto-generated name. Do not use this and a node_pool at the same time.",
"format": "int32"
},
"nodeConfig": {
"$ref": "NodeConfig",
"description": "Parameters used in creating the cluster's nodes. See the descriptions of the child properties of `nodeConfig`. If unspecified, the defaults for all child properties are used."
"description": "Parameters used in creating the cluster's nodes. See `nodeConfig` for the description of its properties. For requests, this field should only be used in lieu of a \"node_pool\" object, since this configuration (along with the \"initial_node_count\") will be used to create a \"NodePool\" object with an auto-generated name. Do not use this and a node_pool at the same time. For responses, this field will be populated with the node configuration of the first node pool. If unspecified, the defaults are used."
},
"masterAuth": {
"$ref": "MasterAuth",
"description": "The authentication information for accessing the master."
"description": "The authentication information for accessing the master endpoint."
},
"loggingService": {
"type": "string",
"description": "The logging service that the cluster should write logs to. Currently available options: * \"logging.googleapis.com\" - the Google Cloud Logging service * \"none\" - no logs will be exported from the cluster * \"\" - default value; the default is \"logging.googleapis.com\""
"description": "The logging service the cluster should use to write logs. Currently available options: * `logging.googleapis.com` - the Google Cloud Logging service. * `none` - no logs will be exported from the cluster. * if left as an empty string,`logging.googleapis.com` will be used."
},
"monitoringService": {
"type": "string",
"description": "The monitoring service that the cluster should write metrics to. Currently available options: * \"monitoring.googleapis.com\" - the Google Cloud Monitoring service * \"none\" - no metrics will be exported from the cluster * \"\" - default value; the default is \"monitoring.googleapis.com\""
"description": "The monitoring service the cluster should use to write metrics. Currently available options: * `monitoring.googleapis.com` - the Google Cloud Monitoring service. * `none` - no metrics will be exported from the cluster. * if left as an empty string, `monitoring.googleapis.com` will be used."
},
"network": {
"type": "string",
"description": "The name of the Google Compute Engine [network](/compute/docs/networking#networks_1) to which the cluster is connected. If left unspecified, the \"default\" network will be used."
"description": "The name of the Google Compute Engine [network](/compute/docs/networks-and-firewalls#networks) to which the cluster is connected. If left unspecified, the `default` network will be used."
},
"clusterIpv4Cidr": {
"type": "string",
"description": "The IP address range of the container pods in this cluster, in [CIDR](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) notation (e.g. `10.96.0.0/14`). Leave blank to have one automatically chosen or specify a `/14` block in `10.0.0.0/8`."
},
"addonsConfig": {
"$ref": "AddonsConfig",
"description": "Configurations for the various addons available to run in the cluster."
},
"subnetwork": {
"type": "string",
"description": "The name of the Google Compute Engine [subnetwork](/compute/docs/subnetworks) to which the cluster is connected."
},
"nodePools": {
"type": "array",
"description": "The node pools associated with this cluster. When creating a new cluster, only a single node pool should be specified. This field should not be set if \"node_config\" or \"initial_node_count\" are specified.",
"items": {
"$ref": "NodePool"
}
},
"locations": {
"type": "array",
"description": "The list of Google Compute Engine [locations](/compute/docs/zones#available) in which the cluster's nodes should be located.",
"items": {
"type": "string"
}
},
"selfLink": {
"type": "string",
"description": "[Output only] Server-defined URL for the resource."
@ -176,11 +205,11 @@
},
"endpoint": {
"type": "string",
"description": "[Output only] The IP address of this cluster's Kubernetes master endpoint. The endpoint can be accessed from the internet at `https://username:password@endpoint/`. See the `masterAuth` property of this resource for username and password information."
"description": "[Output only] The IP address of this cluster's master endpoint. The endpoint can be accessed from the internet at `https://username:password@endpoint/`. See the `masterAuth` property of this resource for username and password information."
},
"initialClusterVersion": {
"type": "string",
"description": "[Output only] The software version of Kubernetes master and kubelets used in the cluster when it was first created. The version can be upgraded over time."
"description": "[Output only] The software version of the master endpoint and kubelets used in the cluster when it was first created. The version can be upgraded over time."
},
"currentMasterVersion": {
"type": "string",
@ -188,7 +217,7 @@
},
"currentNodeVersion": {
"type": "string",
"description": "[Output only] The current version of the node software components. If they are currently at different versions because they're in the process of being upgraded, this reflects the minimum version of any of them."
"description": "[Output only] The current version of the node software components. If they are currently at multiple versions because they're in the process of being upgraded, this reflects the minimum version of all nodes."
},
"createTime": {
"type": "string",
@ -212,12 +241,12 @@
},
"nodeIpv4CidrSize": {
"type": "integer",
"description": "[Output only] The size of the address space on each node for hosting containers. This is provisioned from within the container_ipv4_cidr range.",
"description": "[Output only] The size of the address space on each node for hosting containers. This is provisioned from within the `container_ipv4_cidr` range.",
"format": "int32"
},
"servicesIpv4Cidr": {
"type": "string",
"description": "[Output only] The IP address range of the Kubernetes services in this cluster, in [CIDR](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) notation (e.g. `1.2.3.4/29`). Service addresses are typically put in the last /16 from the container CIDR."
"description": "[Output only] The IP address range of the Kubernetes services in this cluster, in [CIDR](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) notation (e.g. `1.2.3.4/29`). Service addresses are typically put in the last `/16` from the container CIDR."
},
"instanceGroupUrls": {
"type": "array",
@ -225,13 +254,18 @@
"items": {
"type": "string"
}
},
"currentNodeCount": {
"type": "integer",
"description": "[Output only] The number of nodes currently in the cluster.",
"format": "int32"
}
}
},
"NodeConfig": {
"id": "NodeConfig",
"type": "object",
"description": "Per-node parameters.",
"description": "Parameters that describe the nodes in a cluster.",
"properties": {
"machineType": {
"type": "string",
@ -244,10 +278,17 @@
},
"oauthScopes": {
"type": "array",
"description": "The set of Google API scopes to be made available on all of the node VMs under the \"default\" service account. The following scopes are recommended, but not required, and by default are not included: * `https://www.googleapis.com/auth/compute` is required for mounting persistent storage on your nodes. * `https://www.googleapis.com/auth/devstorage.read_only` is required for communicating with *gcr.io*. If unspecified, no scopes are added.",
"description": "The set of Google API scopes to be made available on all of the node VMs under the \"default\" service account. The following scopes are recommended, but not required, and by default are not included: * `https://www.googleapis.com/auth/compute` is required for mounting persistent storage on your nodes. * `https://www.googleapis.com/auth/devstorage.read_only` is required for communicating with **gcr.io** (the [Google Container Registry](/container-registry/)). If unspecified, no scopes are added, unless Cloud Logging or Cloud Monitoring are enabled, in which case their required scopes will be added.",
"items": {
"type": "string"
}
},
"metadata": {
"type": "object",
"description": "The metadata key/value pairs assigned to instances in the cluster. Keys must conform to the regexp [a-zA-Z0-9-_]+ and be less than 128 bytes in length. These are reflected as part of a URL in the metadata server. Additionally, to avoid ambiguity, keys must not conflict with any other metadata keys for the project or be one of the four reserved keys: \"instance-template\", \"kube-env\", \"startup-script\", and \"user-data\" Values are free-form strings, and only have meaning as interpreted by the image running in the instance. The only restriction placed on them is that each value's size must be less than or equal to 32 KB. The total size of all keys and values must be less than 512 KB.",
"additionalProperties": {
"type": "string"
}
}
}
},
@ -258,23 +299,112 @@
"properties": {
"username": {
"type": "string",
"description": "The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint."
"description": "The username to use for HTTP basic authentication to the master endpoint."
},
"password": {
"type": "string",
"description": "The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint. Because the master endpoint is open to the internet, you should create a strong password."
"description": "The password to use for HTTP basic authentication to the master endpoint. Because the master endpoint is open to the Internet, you should create a strong password."
},
"clusterCaCertificate": {
"type": "string",
"description": "[Output only] Base64 encoded public certificate that is the root of trust for the cluster."
"description": "[Output only] Base64-encoded public certificate that is the root of trust for the cluster."
},
"clientCertificate": {
"type": "string",
"description": "[Output only] Base64 encoded public certificate used by clients to authenticate to the cluster endpoint."
"description": "[Output only] Base64-encoded public certificate used by clients to authenticate to the cluster endpoint."
},
"clientKey": {
"type": "string",
"description": "[Output only] Base64 encoded private key used by clients to authenticate to the cluster endpoint."
"description": "[Output only] Base64-encoded private key used by clients to authenticate to the cluster endpoint."
}
}
},
"AddonsConfig": {
"id": "AddonsConfig",
"type": "object",
"description": "Configuration for the addons that can be automatically spun up in the cluster, enabling additional functionality.",
"properties": {
"httpLoadBalancing": {
"$ref": "HttpLoadBalancing",
"description": "Configuration for the HTTP (L7) load balancing controller addon, which makes it easy to set up HTTP load balancers for services in a cluster."
},
"horizontalPodAutoscaling": {
"$ref": "HorizontalPodAutoscaling",
"description": "Configuration for the horizontal pod autoscaling feature, which increases or decreases the number of replica pods a replication controller has based on the resource usage of the existing pods."
}
}
},
"HttpLoadBalancing": {
"id": "HttpLoadBalancing",
"type": "object",
"description": "Configuration options for the HTTP (L7) load balancing controller addon, which makes it easy to set up HTTP load balancers for services in a cluster.",
"properties": {
"disabled": {
"type": "boolean",
"description": "Whether the HTTP Load Balancing controller is enabled in the cluster. When enabled, it runs a small pod in the cluster that manages the load balancers."
}
}
},
"HorizontalPodAutoscaling": {
"id": "HorizontalPodAutoscaling",
"type": "object",
"description": "Configuration options for the horizontal pod autoscaling feature, which increases or decreases the number of replica pods a replication controller has based on the resource usage of the existing pods.",
"properties": {
"disabled": {
"type": "boolean",
"description": "Whether the Horizontal Pod Autoscaling feature is enabled in the cluster. When enabled, it ensures that a Heapster pod is running in the cluster, which is also used by the Cloud Monitoring service."
}
}
},
"NodePool": {
"id": "NodePool",
"type": "object",
"description": "NodePool contains the name and configuration for a cluster's node pool. Node pools are a set of nodes (i.e. VM's), with a common configuration and specification, under the control of the cluster master. They may have a set of Kubernetes labels applied to them, which may be used to reference them during pod scheduling. They may also be resized up or down, to accommodate the workload.",
"properties": {
"name": {
"type": "string",
"description": "The name of the node pool."
},
"config": {
"$ref": "NodeConfig",
"description": "The node configuration of the pool."
},
"initialNodeCount": {
"type": "integer",
"description": "The initial node count for the pool. You must ensure that your Compute Engine resource quota is sufficient for this number of instances. You must also have available firewall and routes quota.",
"format": "int32"
},
"selfLink": {
"type": "string",
"description": "Server-defined URL for the resource."
},
"version": {
"type": "string",
"description": "The version of the Kubernetes of this node."
},
"instanceGroupUrls": {
"type": "array",
"description": "[Output only] The resource URLs of [instance groups](/compute/docs/instance-groups/) associated with this node pool.",
"items": {
"type": "string"
}
},
"status": {
"type": "string",
"description": "The status of the nodes in this pool instance.",
"enum": [
"STATUS_UNSPECIFIED",
"PROVISIONING",
"RUNNING",
"RUNNING_WITH_ERROR",
"RECONCILING",
"STOPPING",
"ERROR"
]
},
"statusMessage": {
"type": "string",
"description": "[Output only] Additional information about the current status of this node pool instance, if available."
}
}
},
@ -292,7 +422,7 @@
"Operation": {
"id": "Operation",
"type": "object",
"description": "Defines the operation resource. All fields are output only.",
"description": "This operation resource represents operations that may have happened or are happening on the cluster. All fields are output only.",
"properties": {
"name": {
"type": "string",
@ -311,7 +441,10 @@
"DELETE_CLUSTER",
"UPGRADE_MASTER",
"UPGRADE_NODES",
"REPAIR_CLUSTER"
"REPAIR_CLUSTER",
"UPDATE_CLUSTER",
"CREATE_NODE_POOL",
"DELETE_NODE_POOL"
]
},
"status": {
@ -324,6 +457,10 @@
"DONE"
]
},
"detail": {
"type": "string",
"description": "Detailed operation progress, if available."
},
"statusMessage": {
"type": "string",
"description": "If an error has occurred, a textual description of the error."
@ -341,7 +478,7 @@
"UpdateClusterRequest": {
"id": "UpdateClusterRequest",
"type": "object",
"description": "UpdateClusterRequest updates a cluster.",
"description": "UpdateClusterRequest updates the settings of a cluster.",
"properties": {
"update": {
"$ref": "ClusterUpdate",
@ -352,11 +489,27 @@
"ClusterUpdate": {
"id": "ClusterUpdate",
"type": "object",
"description": "ClusterUpdate describes an update to the cluster.",
"description": "ClusterUpdate describes an update to the cluster. Exactly one update can be applied to a cluster with each request, so at most one field can be provided.",
"properties": {
"desiredNodeVersion": {
"type": "string",
"description": "The Kubernetes version to change the nodes to (typically an upgrade). Use \"-\" to upgrade to the latest version supported by the server."
"description": "The Kubernetes version to change the nodes to (typically an upgrade). Use `-` to upgrade to the latest version supported by the server."
},
"desiredMonitoringService": {
"type": "string",
"description": "The monitoring service the cluster should use to write metrics. Currently available options: * \"monitoring.googleapis.com\" - the Google Cloud Monitoring service * \"none\" - no metrics will be exported from the cluster"
},
"desiredAddonsConfig": {
"$ref": "AddonsConfig",
"description": "Configurations for the various addons available to run in the cluster."
},
"desiredNodePoolId": {
"type": "string",
"description": "The node pool to be upgraded. This field is mandatory if the \"desired_node_version\" or \"desired_image_family\" is specified and there is more than one node pool on the cluster."
},
"desiredMasterVersion": {
"type": "string",
"description": "The Kubernetes version to change the master to. The only valid value is the latest supported version. Use \"-\" to have the server automatically select the latest version."
}
}
},
@ -371,17 +524,24 @@
"items": {
"$ref": "Operation"
}
},
"missingZones": {
"type": "array",
"description": "If any zones are listed here, the list of operations returned may be missing the operations from those zones.",
"items": {
"type": "string"
}
}
}
},
"ServerConfig": {
"id": "ServerConfig",
"type": "object",
"description": "Container Engine Server configuration.",
"description": "Container Engine service configuration.",
"properties": {
"defaultClusterVersion": {
"type": "string",
"description": "What version this server deploys by default."
"description": "Version of Kubernetes the service deploys by default."
},
"validNodeVersions": {
"type": "array",
@ -389,6 +549,42 @@
"items": {
"type": "string"
}
},
"defaultImageFamily": {
"type": "string",
"description": "Default image family."
},
"validImageFamilies": {
"type": "array",
"description": "List of valid image families.",
"items": {
"type": "string"
}
}
}
},
"ListNodePoolsResponse": {
"id": "ListNodePoolsResponse",
"type": "object",
"description": "ListNodePoolsResponse is the result of ListNodePoolsRequest.",
"properties": {
"nodePools": {
"type": "array",
"description": "A list of node pools for a cluster.",
"items": {
"$ref": "NodePool"
}
}
}
},
"CreateNodePoolRequest": {
"id": "CreateNodePoolRequest",
"type": "object",
"description": "CreateNodePoolRequest creates a node pool for a cluster.",
"properties": {
"nodePool": {
"$ref": "NodePool",
"description": "The node pool to create."
}
}
}
@ -406,13 +602,13 @@
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"description": "The Google Developers Console [project ID or project number](https://support.google.com/cloud/answer/6158840).",
"required": true,
"location": "path"
},
"zone": {
"type": "string",
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) to return operations for, or \"-\" for all zones.",
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) to return operations for.",
"required": true,
"location": "path"
}
@ -440,7 +636,7 @@
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"description": "The Google Developers Console [project ID or project number](https://support.google.com/cloud/answer/6158840).",
"required": true,
"location": "path"
},
@ -466,11 +662,11 @@
"id": "container.projects.zones.clusters.get",
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}",
"httpMethod": "GET",
"description": "Gets a specific cluster.",
"description": "Gets the details of a specific cluster.",
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"description": "The Google Developers Console [project ID or project number](https://support.google.com/cloud/answer/6158840).",
"required": true,
"location": "path"
},
@ -503,11 +699,11 @@
"id": "container.projects.zones.clusters.create",
"path": "v1/projects/{projectId}/zones/{zone}/clusters",
"httpMethod": "POST",
"description": "Creates a cluster, consisting of the specified number and type of Google Compute Engine instances, plus a Kubernetes master endpoint. By default, the cluster is created in the project's [default network](/compute/docs/networking#networks_1). One firewall is added for the cluster. After cluster creation, the cluster creates routes for each node to allow the containers on that node to communicate with all other instances in the cluster. Finally, an entry is added to the project's global metadata indicating which CIDR range is being used by the cluster.",
"description": "Creates a cluster, consisting of the specified number and type of Google Compute Engine instances. By default, the cluster is created in the project's [default network](/compute/docs/networks-and-firewalls#networks). One firewall is added for the cluster. After cluster creation, the cluster creates routes for each node to allow the containers on that node to communicate with all other instances in the cluster. Finally, an entry is added to the project's global metadata indicating which CIDR range is being used by the cluster.",
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"description": "The Google Developers Console [project ID or project number](https://support.google.com/cloud/answer/6158840).",
"required": true,
"location": "path"
},
@ -536,11 +732,11 @@
"id": "container.projects.zones.clusters.update",
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}",
"httpMethod": "PUT",
"description": "Update settings of a specific cluster.",
"description": "Updates the settings of a specific cluster.",
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"description": "The Google Developers Console [project ID or project number](https://support.google.com/cloud/answer/6158840).",
"required": true,
"location": "path"
},
@ -576,11 +772,11 @@
"id": "container.projects.zones.clusters.delete",
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}",
"httpMethod": "DELETE",
"description": "Deletes the cluster, including the Kubernetes endpoint and all worker nodes. Firewalls and routes that were configured during cluster creation are also deleted.",
"description": "Deletes the cluster, including the Kubernetes endpoint and all worker nodes. Firewalls and routes that were configured during cluster creation are also deleted. Other Google Compute Engine resources that might be in use by the cluster (e.g. load balancer resources) will not be deleted if they weren't present at the initial create time.",
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"description": "The Google Developers Console [project ID or project number](https://support.google.com/cloud/answer/6158840).",
"required": true,
"location": "path"
},
@ -609,6 +805,177 @@
"https://www.googleapis.com/auth/cloud-platform"
]
}
},
"resources": {
"nodePools": {
"methods": {
"list": {
"id": "container.projects.zones.clusters.nodePools.list",
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools",
"httpMethod": "GET",
"description": "Lists the node pools for a cluster.",
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"required": true,
"location": "path"
},
"zone": {
"type": "string",
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) in which the cluster resides.",
"required": true,
"location": "path"
},
"clusterId": {
"type": "string",
"description": "The name of the cluster.",
"required": true,
"location": "path"
}
},
"parameterOrder": [
"projectId",
"zone",
"clusterId"
],
"response": {
"$ref": "ListNodePoolsResponse"
},
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
},
"get": {
"id": "container.projects.zones.clusters.nodePools.get",
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools/{nodePoolId}",
"httpMethod": "GET",
"description": "Retrieves the node pool requested.",
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"required": true,
"location": "path"
},
"zone": {
"type": "string",
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) in which the cluster resides.",
"required": true,
"location": "path"
},
"clusterId": {
"type": "string",
"description": "The name of the cluster.",
"required": true,
"location": "path"
},
"nodePoolId": {
"type": "string",
"description": "The name of the node pool.",
"required": true,
"location": "path"
}
},
"parameterOrder": [
"projectId",
"zone",
"clusterId",
"nodePoolId"
],
"response": {
"$ref": "NodePool"
},
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
},
"create": {
"id": "container.projects.zones.clusters.nodePools.create",
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools",
"httpMethod": "POST",
"description": "Creates a node pool for a cluster.",
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"required": true,
"location": "path"
},
"zone": {
"type": "string",
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) in which the cluster resides.",
"required": true,
"location": "path"
},
"clusterId": {
"type": "string",
"description": "The name of the cluster.",
"required": true,
"location": "path"
}
},
"parameterOrder": [
"projectId",
"zone",
"clusterId"
],
"request": {
"$ref": "CreateNodePoolRequest"
},
"response": {
"$ref": "Operation"
},
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
},
"delete": {
"id": "container.projects.zones.clusters.nodePools.delete",
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}/nodePools/{nodePoolId}",
"httpMethod": "DELETE",
"description": "Deletes a node pool from a cluster.",
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"required": true,
"location": "path"
},
"zone": {
"type": "string",
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) in which the cluster resides.",
"required": true,
"location": "path"
},
"clusterId": {
"type": "string",
"description": "The name of the cluster.",
"required": true,
"location": "path"
},
"nodePoolId": {
"type": "string",
"description": "The name of the node pool to delete.",
"required": true,
"location": "path"
}
},
"parameterOrder": [
"projectId",
"zone",
"clusterId",
"nodePoolId"
],
"response": {
"$ref": "Operation"
},
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
}
}
}
}
},
"operations": {
@ -621,13 +988,13 @@
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"description": "The Google Developers Console [project ID or project number](https://support.google.com/cloud/answer/6158840).",
"required": true,
"location": "path"
},
"zone": {
"type": "string",
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) to return operations for, or \"-\" for all zones.",
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) to return operations for, or `-` for all zones.",
"required": true,
"location": "path"
}
@ -651,7 +1018,7 @@
"parameters": {
"projectId": {
"type": "string",
"description": "The Google Developers Console [project ID or project number](https://developers.google.com/console/help/new/#projectnumber).",
"description": "The Google Developers Console [project ID or project number](https://support.google.com/cloud/answer/6158840).",
"required": true,
"location": "path"
},

File diff suppressed because it is too large Load Diff

46
vendor/google.golang.org/api/gensupport/backoff.go generated vendored Normal file
View File

@ -0,0 +1,46 @@
// 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 (
"math/rand"
"time"
)
type BackoffStrategy interface {
// Pause returns the duration of the next pause and true if the operation should be
// retried, or false if no further retries should be attempted.
Pause() (time.Duration, bool)
// Reset restores the strategy to its initial state.
Reset()
}
// ExponentialBackoff performs exponential backoff as per https://en.wikipedia.org/wiki/Exponential_backoff.
// The initial pause time is given by Base.
// Once the total pause time exceeds Max, Pause will indicate no further retries.
type ExponentialBackoff struct {
Base time.Duration
Max time.Duration
total time.Duration
n uint
}
func (eb *ExponentialBackoff) Pause() (time.Duration, bool) {
if eb.total > eb.Max {
return 0, false
}
// The next pause is selected from randomly from [0, 2^n * Base).
d := time.Duration(rand.Int63n((1 << eb.n) * int64(eb.Base)))
eb.total += d
eb.n++
return d, true
}
func (eb *ExponentialBackoff) Reset() {
eb.n = 0
eb.total = 0
}

77
vendor/google.golang.org/api/gensupport/buffer.go generated vendored Normal file
View File

@ -0,0 +1,77 @@
// 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 (
"bytes"
"io"
"google.golang.org/api/googleapi"
)
// MediaBuffer buffers data from an io.Reader to support uploading media in retryable chunks.
type MediaBuffer struct {
media io.Reader
chunk []byte // The current chunk which is pending upload. The capacity is the chunk size.
err error // Any error generated when populating chunk by reading media.
// The absolute position of chunk in the underlying media.
off int64
}
func NewMediaBuffer(media io.Reader, chunkSize int) *MediaBuffer {
return &MediaBuffer{media: media, chunk: make([]byte, 0, chunkSize)}
}
// Chunk returns the current buffered chunk, the offset in the underlying media
// from which the chunk is drawn, and the size of the chunk.
// Successive calls to Chunk return the same chunk between calls to Next.
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.
if mb.err == nil && len(mb.chunk) == 0 {
mb.err = mb.loadChunk()
}
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.
func (mb *MediaBuffer) loadChunk() error {
bufSize := cap(mb.chunk)
mb.chunk = mb.chunk[:bufSize]
read := 0
var err error
for err == nil && read < bufSize {
var n int
n, err = mb.media.Read(mb.chunk[read:])
read += n
}
mb.chunk = mb.chunk[:read]
return err
}
// 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.
func (mb *MediaBuffer) Next() {
mb.off += int64(len(mb.chunk))
mb.chunk = mb.chunk[0:0]
}
type readerTyper struct {
io.Reader
googleapi.ContentTyper
}
// ReaderAtToReader adapts a ReaderAt to be used as a Reader.
// If ra implements googleapi.ContentTyper, then the returned reader
// will also implement googleapi.ContentTyper, delegating to ra.
func ReaderAtToReader(ra io.ReaderAt, size int64) io.Reader {
r := io.NewSectionReader(ra, 0, size)
if typer, ok := ra.(googleapi.ContentTyper); ok {
return readerTyper{r, typer}
}
return r
}

View File

@ -5,7 +5,6 @@
package gensupport
import (
"errors"
"fmt"
"io"
"io/ioutil"
@ -18,12 +17,12 @@ import (
const sniffBuffSize = 512
func NewContentSniffer(r io.Reader) *ContentSniffer {
return &ContentSniffer{r: r}
func newContentSniffer(r io.Reader) *contentSniffer {
return &contentSniffer{r: r}
}
// ContentSniffer wraps a Reader, and reports the content type determined by sniffing up to 512 bytes from the Reader.
type ContentSniffer struct {
// contentSniffer wraps a Reader, and reports the content type determined by sniffing up to 512 bytes from the Reader.
type contentSniffer struct {
r io.Reader
start []byte // buffer for the sniffed bytes.
err error // set to any error encountered while reading bytes to be sniffed.
@ -32,133 +31,169 @@ type ContentSniffer struct {
sniffed bool // set to true on first sniff.
}
func (sct *ContentSniffer) Read(p []byte) (n int, err error) {
func (cs *contentSniffer) Read(p []byte) (n int, err error) {
// Ensure that the content type is sniffed before any data is consumed from Reader.
_, _ = sct.ContentType()
_, _ = cs.ContentType()
if len(sct.start) > 0 {
n := copy(p, sct.start)
sct.start = sct.start[n:]
if len(cs.start) > 0 {
n := copy(p, cs.start)
cs.start = cs.start[n:]
return n, nil
}
// We may have read some bytes into start while sniffing, even if the read ended in an error.
// We should first return those bytes, then the error.
if sct.err != nil {
return 0, sct.err
if cs.err != nil {
return 0, cs.err
}
// Now we have handled all bytes that were buffered while sniffing. Now just delegate to the underlying reader.
return sct.r.Read(p)
return cs.r.Read(p)
}
// ContentType returns the sniffed content type, and whether the content type was succesfully sniffed.
func (sct *ContentSniffer) ContentType() (string, bool) {
if sct.sniffed {
return sct.ctype, sct.ctype != ""
func (cs *contentSniffer) ContentType() (string, bool) {
if cs.sniffed {
return cs.ctype, cs.ctype != ""
}
sct.sniffed = true
cs.sniffed = true
// If ReadAll hits EOF, it returns err==nil.
sct.start, sct.err = ioutil.ReadAll(io.LimitReader(sct.r, sniffBuffSize))
cs.start, cs.err = ioutil.ReadAll(io.LimitReader(cs.r, sniffBuffSize))
// Don't try to detect the content type based on possibly incomplete data.
if sct.err != nil {
if cs.err != nil {
return "", false
}
sct.ctype = http.DetectContentType(sct.start)
return sct.ctype, true
cs.ctype = http.DetectContentType(cs.start)
return cs.ctype, true
}
// IncludeMedia combines an existing HTTP body with media content to create a multipart/related HTTP body.
//
// bodyp is an in/out parameter. It should initially point to the
// reader of the application/json (or whatever) payload to send in the
// API request. It's updated to point to the multipart body reader.
//
// ctypep is an in/out parameter. It should initially point to the
// content type of the bodyp, usually "application/json". It's updated
// to the "multipart/related" content type, with random boundary.
//
// The return value is a function that can be used to close the bodyp Reader with an error.
func IncludeMedia(media io.Reader, bodyp *io.Reader, ctypep *string) func() {
var mediaType string
media, mediaType = getMediaType(media)
// DetermineContentType determines the content type of the supplied reader.
// If the content type is already known, it can be specified via ctype.
// Otherwise, the content of media will be sniffed to determine the content type.
// If media implements googleapi.ContentTyper (deprecated), this will be used
// instead of sniffing the content.
// After calling DetectContentType the caller must not perform further reads on
// media, but rather read from the Reader that is returned.
func DetermineContentType(media io.Reader, ctype string) (io.Reader, string) {
// Note: callers could avoid calling DetectContentType if ctype != "",
// but doing the check inside this function reduces the amount of
// generated code.
if ctype != "" {
return media, ctype
}
body, bodyType := *bodyp, *ctypep
pr, pw := io.Pipe()
mpw := multipart.NewWriter(pw)
*bodyp = pr
*ctypep = "multipart/related; boundary=" + mpw.Boundary()
go func() {
w, err := mpw.CreatePart(typeHeader(bodyType))
if err != nil {
mpw.Close()
pw.CloseWithError(fmt.Errorf("googleapi: body CreatePart failed: %v", err))
return
}
_, err = io.Copy(w, body)
if err != nil {
mpw.Close()
pw.CloseWithError(fmt.Errorf("googleapi: body Copy failed: %v", err))
return
}
w, err = mpw.CreatePart(typeHeader(mediaType))
if err != nil {
mpw.Close()
pw.CloseWithError(fmt.Errorf("googleapi: media CreatePart failed: %v", err))
return
}
_, err = io.Copy(w, media)
if err != nil {
mpw.Close()
pw.CloseWithError(fmt.Errorf("googleapi: media Copy failed: %v", err))
return
}
mpw.Close()
pw.Close()
}()
return func() { pw.CloseWithError(errAborted) }
}
var errAborted = errors.New("googleapi: upload aborted")
func getMediaType(media io.Reader) (io.Reader, string) {
// For backwards compatability, allow clients to set content
// type by providing a ContentTyper for media.
if typer, ok := media.(googleapi.ContentTyper); ok {
return media, typer.ContentType()
}
sniffer := NewContentSniffer(media)
typ, ok := sniffer.ContentType()
if !ok {
// TODO(mcgreevy): Remove this default. It maintains the semantics of the existing code,
// but should not be relied on.
typ = "application/octet-stream"
sniffer := newContentSniffer(media)
if ctype, ok := sniffer.ContentType(); ok {
return sniffer, ctype
}
return sniffer, typ
// If content type could not be sniffed, reads from sniffer will eventually fail with an error.
return sniffer, ""
}
// DetectMediaType detects and returns the content type of the provided media.
// If the type can not be determined, "application/octet-stream" is returned.
func DetectMediaType(media io.ReaderAt) string {
if typer, ok := media.(googleapi.ContentTyper); ok {
return typer.ContentType()
}
type typeReader struct {
io.Reader
typ string
}
typ := "application/octet-stream"
buf := make([]byte, 1024)
n, err := media.ReadAt(buf, 0)
buf = buf[:n]
if err == nil || err == io.EOF {
typ = http.DetectContentType(buf)
// multipartReader combines the contents of multiple readers to creat a multipart/related HTTP body.
// Close must be called if reads from the multipartReader are abandoned before reaching EOF.
type multipartReader struct {
pr *io.PipeReader
pipeOpen bool
ctype string
}
func newMultipartReader(parts []typeReader) *multipartReader {
mp := &multipartReader{pipeOpen: true}
var pw *io.PipeWriter
mp.pr, pw = io.Pipe()
mpw := multipart.NewWriter(pw)
mp.ctype = "multipart/related; boundary=" + mpw.Boundary()
go func() {
for _, part := range parts {
w, err := mpw.CreatePart(typeHeader(part.typ))
if err != nil {
mpw.Close()
pw.CloseWithError(fmt.Errorf("googleapi: CreatePart failed: %v", err))
return
}
_, err = io.Copy(w, part.Reader)
if err != nil {
mpw.Close()
pw.CloseWithError(fmt.Errorf("googleapi: Copy failed: %v", err))
return
}
}
mpw.Close()
pw.Close()
}()
return mp
}
func (mp *multipartReader) Read(data []byte) (n int, err error) {
return mp.pr.Read(data)
}
func (mp *multipartReader) Close() error {
if !mp.pipeOpen {
return nil
}
return typ
mp.pipeOpen = false
return mp.pr.Close()
}
// CombineBodyMedia combines a json body with media content to create a multipart/related HTTP body.
// It returns a ReadCloser containing the combined body, and the overall "multipart/related" content type, with random boundary.
//
// The caller must call Close on the returned ReadCloser if reads are abandoned before reaching EOF.
func CombineBodyMedia(body io.Reader, bodyContentType string, media io.Reader, mediaContentType string) (io.ReadCloser, string) {
mp := newMultipartReader([]typeReader{
{body, bodyContentType},
{media, mediaContentType},
})
return mp, mp.ctype
}
func typeHeader(contentType string) textproto.MIMEHeader {
h := make(textproto.MIMEHeader)
h.Set("Content-Type", contentType)
if contentType != "" {
h.Set("Content-Type", contentType)
}
return h
}
// PrepareUpload determines whether the data in the supplied reader should be
// uploaded in a single request, or in sequential chunks.
// 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
// MediaBuffer. Otherwise, media will be returned as a Reader.
//
// After PrepareUpload has been called, media should no longer be used: the
// media content should be accessed via one of the return values.
func PrepareUpload(media io.Reader, chunkSize int) (io.Reader, *MediaBuffer) {
if chunkSize == 0 { // do not chunk
return media, nil
}
mb := NewMediaBuffer(media, chunkSize)
rdr, _, _, err := mb.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 mb.Chunk will
// return the same error. Returning a MediaBuffer ensures that this error
// will be handled at some point.
return nil, mb
}

View File

@ -4,12 +4,25 @@
package gensupport
import "net/url"
import (
"net/url"
"google.golang.org/api/googleapi"
)
// URLParams is a simplified replacement for url.Values
// that safely builds up URL parameters for encoding.
type URLParams map[string][]string
// Get returns the first value for the given key, or "".
func (u URLParams) Get(key string) string {
vs := u[key]
if len(vs) == 0 {
return ""
}
return vs[0]
}
// Set sets the key to value.
// It replaces any existing values.
func (u URLParams) Set(key, value string) {
@ -29,3 +42,9 @@ func (u URLParams) SetMulti(key string, values []string) {
func (u URLParams) Encode() string {
return url.Values(u).Encode()
}
func SetOptions(u URLParams, opts ...googleapi.CallOption) {
for _, o := range opts {
u.Set(o.Get())
}
}

198
vendor/google.golang.org/api/gensupport/resumable.go generated vendored Normal file
View File

@ -0,0 +1,198 @@
// 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 (
"fmt"
"io"
"net/http"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)
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
// per-project limits have been temporarily exceeded. The request
// should be retried.
// https://cloud.google.com/storage/docs/json_api/v1/status-codes#standardcodes
statusTooManyRequests = 429
)
// ResumableUpload is used by the generated APIs to provide resumable uploads.
// It is not used by developers directly.
type ResumableUpload struct {
Client *http.Client
// URI is the resumable resource destination provided by the server after specifying "&uploadType=resumable".
URI string
UserAgent string // User-Agent for header of the request
// Media is the object being uploaded.
Media *MediaBuffer
// MediaType defines the media type, e.g. "image/jpeg".
MediaType string
mu sync.Mutex // guards progress
progress int64 // number of bytes uploaded so far
// Callback is an optional function that will be periodically called with the cumulative number of bytes uploaded.
Callback func(int64)
// If not specified, a default exponential backoff strategy will be used.
Backoff BackoffStrategy
}
// Progress returns the number of bytes uploaded at this point.
func (rx *ResumableUpload) Progress() int64 {
rx.mu.Lock()
defer rx.mu.Unlock()
return rx.progress
}
// doUploadRequest performs a single HTTP request to upload data.
// off specifies the offset in rx.Media from which data is drawn.
// size is the number of bytes in data.
// final specifies whether data is the final chunk to be uploaded.
func (rx *ResumableUpload) doUploadRequest(ctx context.Context, data io.Reader, off, size int64, final bool) (*http.Response, error) {
req, err := http.NewRequest("POST", rx.URI, data)
if err != nil {
return nil, err
}
req.ContentLength = size
var contentRange string
if final {
if size == 0 {
contentRange = fmt.Sprintf("bytes */%v", off)
} else {
contentRange = fmt.Sprintf("bytes %v-%v/%v", off, off+size-1, off+size)
}
} else {
contentRange = fmt.Sprintf("bytes %v-%v/*", off, off+size-1)
}
req.Header.Set("Content-Range", contentRange)
req.Header.Set("Content-Type", rx.MediaType)
req.Header.Set("User-Agent", rx.UserAgent)
return ctxhttp.Do(ctx, rx.Client, req)
}
// reportProgress calls a user-supplied callback to report upload progress.
// If old==updated, the callback is not called.
func (rx *ResumableUpload) reportProgress(old, updated int64) {
if updated-old == 0 {
return
}
rx.mu.Lock()
rx.progress = updated
rx.mu.Unlock()
if rx.Callback != nil {
rx.Callback(updated)
}
}
// transferChunk performs a single HTTP request to upload a single chunk from rx.Media.
func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, error) {
chunk, off, size, err := rx.Media.Chunk()
done := err == io.EOF
if !done && err != nil {
return nil, err
}
res, err := rx.doUploadRequest(ctx, chunk, off, int64(size), done)
if err != nil {
return res, err
}
if res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK {
rx.reportProgress(off, off+int64(size))
}
if res.StatusCode == statusResumeIncomplete {
rx.Media.Next()
}
return res, nil
}
func contextDone(ctx context.Context) bool {
select {
case <-ctx.Done():
return true
default:
return false
}
}
// Upload starts the process of a resumable upload with a cancellable context.
// It retries using the provided back off strategy until cancelled or the
// strategy indicates to stop retrying.
// It is called from the auto-generated API code and is not visible to the user.
// 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.
func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) {
var pause time.Duration
backoff := rx.Backoff
if backoff == nil {
backoff = DefaultBackoffStrategy()
}
for {
// Ensure that we return in the case of cancelled context, even if pause is 0.
if contextDone(ctx) {
return nil, ctx.Err()
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(pause):
}
resp, err = rx.transferChunk(ctx)
var status int
if resp != nil {
status = resp.StatusCode
}
// Check if we should retry the request.
if shouldRetry(status, err) {
var retry bool
pause, retry = backoff.Pause()
if retry {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
continue
}
}
// If the chunk was uploaded successfully, but there's still
// more to go, upload the next chunk without any delay.
if status == statusResumeIncomplete {
pause = 0
backoff.Reset()
resp.Body.Close()
continue
}
// It's possible for err and resp to both be non-nil here, but we expose a simpler
// contract to our callers: exactly one of resp and err will be non-nil. This means
// that any response body must be closed here before returning a non-nil error.
if err != nil {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
return nil, err
}
return resp, nil
}
}

77
vendor/google.golang.org/api/gensupport/retry.go generated vendored Normal file
View File

@ -0,0 +1,77 @@
package gensupport
import (
"io"
"net"
"net/http"
"time"
"golang.org/x/net/context"
)
// Retry invokes the given function, retrying it multiple times if the connection failed or
// the HTTP status response indicates the request should be attempted again. ctx may be nil.
func Retry(ctx context.Context, f func() (*http.Response, error), backoff BackoffStrategy) (*http.Response, error) {
for {
resp, err := f()
var status int
if resp != nil {
status = resp.StatusCode
}
// Return if we shouldn't retry.
pause, retry := backoff.Pause()
if !shouldRetry(status, err) || !retry {
return resp, err
}
// Ensure the response body is closed, if any.
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
// Pause, but still listen to ctx.Done if context is not nil.
var done <-chan struct{}
if ctx != nil {
done = ctx.Done()
}
select {
case <-done:
return nil, ctx.Err()
case <-time.After(pause):
}
}
}
// DefaultBackoffStrategy returns a default strategy to use for retrying failed upload requests.
func DefaultBackoffStrategy() BackoffStrategy {
return &ExponentialBackoff{
Base: 250 * time.Millisecond,
Max: 16 * time.Second,
}
}
// shouldRetry returns true if the HTTP response / error indicates that the
// request should be attempted again.
func shouldRetry(status int, err error) bool {
// Retry for 5xx response codes.
if 500 <= status && status < 600 {
return true
}
// Retry on statusTooManyRequests{
if status == statusTooManyRequests {
return true
}
// Retry on unexpected EOFs and temporary network errors.
if err == io.ErrUnexpectedEOF {
return true
}
if err, ok := err.(net.Error); ok {
return err.Temporary()
}
return false
}

View File

@ -14,14 +14,8 @@ import (
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
"google.golang.org/api/googleapi/internal/uritemplates"
)
@ -53,14 +47,15 @@ type ServerResponse struct {
const (
Version = "0.5"
// statusResumeIncomplete is the code returned by the Google uploader when the transfer is not yet complete.
statusResumeIncomplete = 308
// UserAgent is the header string used to identify this package.
UserAgent = "google-api-go-client/" + Version
// uploadPause determines the delay between failed upload attempts
uploadPause = 1 * time.Second
// The default chunk size to use for resumable uplods if not specified by the user.
DefaultUploadChunkSize = 8 * 1024 * 1024
// The minimum chunk size that can be used for resumable uploads. All
// user-specified chunk sizes must be multiple of this value.
MinUploadChunkSize = 256 * 1024
)
// Error contains an error response from the server.
@ -217,134 +212,60 @@ func (w countingWriter) Write(p []byte) (int, error) {
// The remaining usable pieces of resumable uploads is exposed in each auto-generated API.
type ProgressUpdater func(current, total int64)
// ResumableUpload is used by the generated APIs to provide resumable uploads.
type MediaOption interface {
setOptions(o *MediaOptions)
}
type contentTypeOption string
func (ct contentTypeOption) setOptions(o *MediaOptions) {
o.ContentType = string(ct)
if o.ContentType == "" {
o.ForceEmptyContentType = true
}
}
// ContentType returns a MediaOption which sets the Content-Type header for media uploads.
// If ctype is empty, the Content-Type header will be omitted.
func ContentType(ctype string) MediaOption {
return contentTypeOption(ctype)
}
type chunkSizeOption int
func (cs chunkSizeOption) setOptions(o *MediaOptions) {
size := int(cs)
if size%MinUploadChunkSize != 0 {
size += MinUploadChunkSize - (size % MinUploadChunkSize)
}
o.ChunkSize = size
}
// ChunkSize returns a MediaOption which sets the chunk size for media uploads.
// size will be rounded up to the nearest multiple of 256K.
// Media which contains fewer than size bytes will be uploaded in a single request.
// Media which contains size bytes or more will be uploaded in separate chunks.
// If size is zero, media will be uploaded in a single request.
func ChunkSize(size int) MediaOption {
return chunkSizeOption(size)
}
// MediaOptions stores options for customizing media upload. It is not used by developers directly.
type MediaOptions struct {
ContentType string
ForceEmptyContentType bool
ChunkSize int
}
// ProcessMediaOptions stores options from opts in a MediaOptions.
// It is not used by developers directly.
type ResumableUpload struct {
Client *http.Client
// URI is the resumable resource destination provided by the server after specifying "&uploadType=resumable".
URI string
UserAgent string // User-Agent for header of the request
// Media is the object being uploaded.
Media io.ReaderAt
// MediaType defines the media type, e.g. "image/jpeg".
MediaType string
// ContentLength is the full size of the object being uploaded.
ContentLength int64
mu sync.Mutex // guards progress
progress int64 // number of bytes uploaded so far
// Callback is an optional function that will be periodically called with the cumulative number of bytes uploaded.
Callback func(int64)
}
var (
// rangeRE matches the transfer status response from the server. $1 is the last byte index uploaded.
rangeRE = regexp.MustCompile(`^bytes=0\-(\d+)$`)
// chunkSize is the size of the chunks created during a resumable upload and should be a power of two.
// 1<<18 is the minimum size supported by the Google uploader, and there is no maximum.
chunkSize int64 = 1 << 18
)
// Progress returns the number of bytes uploaded at this point.
func (rx *ResumableUpload) Progress() int64 {
rx.mu.Lock()
defer rx.mu.Unlock()
return rx.progress
}
func (rx *ResumableUpload) transferStatus(ctx context.Context) (int64, *http.Response, error) {
req, _ := http.NewRequest("POST", rx.URI, nil)
req.ContentLength = 0
req.Header.Set("User-Agent", rx.UserAgent)
req.Header.Set("Content-Range", fmt.Sprintf("bytes */%v", rx.ContentLength))
res, err := ctxhttp.Do(ctx, rx.Client, req)
if err != nil || res.StatusCode != statusResumeIncomplete {
return 0, res, err
func ProcessMediaOptions(opts []MediaOption) *MediaOptions {
mo := &MediaOptions{ChunkSize: DefaultUploadChunkSize}
for _, o := range opts {
o.setOptions(mo)
}
var start int64
if m := rangeRE.FindStringSubmatch(res.Header.Get("Range")); len(m) == 2 {
start, err = strconv.ParseInt(m[1], 10, 64)
if err != nil {
return 0, nil, fmt.Errorf("unable to parse range size %v", m[1])
}
start += 1 // Start at the next byte
}
return start, res, nil
}
type chunk struct {
body io.Reader
size int64
err error
}
func (rx *ResumableUpload) transferChunks(ctx context.Context) (*http.Response, error) {
start, res, err := rx.transferStatus(ctx)
if err != nil || res.StatusCode != statusResumeIncomplete {
if err == context.Canceled {
return &http.Response{StatusCode: http.StatusRequestTimeout}, err
}
return res, err
}
for {
select { // Check for cancellation
case <-ctx.Done():
res.StatusCode = http.StatusRequestTimeout
return res, ctx.Err()
default:
}
reqSize := rx.ContentLength - start
if reqSize > chunkSize {
reqSize = chunkSize
}
r := io.NewSectionReader(rx.Media, start, reqSize)
req, _ := http.NewRequest("POST", rx.URI, r)
req.ContentLength = reqSize
req.Header.Set("Content-Range", fmt.Sprintf("bytes %v-%v/%v", start, start+reqSize-1, rx.ContentLength))
req.Header.Set("Content-Type", rx.MediaType)
req.Header.Set("User-Agent", rx.UserAgent)
res, err = ctxhttp.Do(ctx, rx.Client, req)
start += reqSize
if err == nil && (res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK) {
rx.mu.Lock()
rx.progress = start // keep track of number of bytes sent so far
rx.mu.Unlock()
if rx.Callback != nil {
rx.Callback(start)
}
}
if err != nil || res.StatusCode != statusResumeIncomplete {
break
}
}
return res, err
}
var sleep = time.Sleep // override in unit tests
// Upload starts the process of a resumable upload with a cancellable context.
// It retries indefinitely (with a pause of uploadPause between attempts) until cancelled.
// It is called from the auto-generated API code and is not visible to the user.
// rx is private to the auto-generated API code.
func (rx *ResumableUpload) Upload(ctx context.Context) (*http.Response, error) {
var res *http.Response
var err error
for {
res, err = rx.transferChunks(ctx)
if err != nil || res.StatusCode == http.StatusCreated || res.StatusCode == http.StatusOK {
return res, err
}
select { // Check for cancellation
case <-ctx.Done():
res.StatusCode = http.StatusRequestTimeout
return res, ctx.Err()
default:
}
sleep(uploadPause)
}
return res, err
return mo
}
func ResolveRelative(basestr, relstr string) string {
@ -471,3 +392,41 @@ func CombineFields(s []Field) string {
}
return strings.Join(r, ",")
}
// A CallOption is an optional argument to an API call.
// It should be treated as an opaque value by users of Google APIs.
//
// A CallOption is something that configures an API call in a way that is
// not specific to that API; for instance, controlling the quota user for
// an API call is common across many APIs, and is thus a CallOption.
type CallOption interface {
Get() (key, value string)
}
// QuotaUser returns a CallOption that will set the quota user for a call.
// The quota user can be used by server-side applications to control accounting.
// It can be an arbitrary string up to 40 characters, and will override UserIP
// if both are provided.
func QuotaUser(u string) CallOption { return quotaUser(u) }
type quotaUser string
func (q quotaUser) Get() (string, string) { return "quotaUser", string(q) }
// UserIP returns a CallOption that will set the "userIp" parameter of a call.
// This should be the IP address of the originating request.
func UserIP(ip string) CallOption { return userIP(ip) }
type userIP string
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

View File

@ -2,26 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package uritemplates is a level 4 implementation of RFC 6570 (URI
// Package uritemplates is a level 3 implementation of RFC 6570 (URI
// Template, http://tools.ietf.org/html/rfc6570).
//
// To use uritemplates, parse a template string and expand it with a value
// map:
//
// template, _ := uritemplates.Parse("https://api.github.com/repos{/user,repo}")
// values := make(map[string]interface{})
// values["user"] = "jtacoma"
// values["repo"] = "uritemplates"
// expanded, _ := template.ExpandString(values)
// fmt.Printf(expanded)
//
// uritemplates does not support composite values (in Go: slices or maps)
// and so does not qualify as a level 4 implementation.
package uritemplates
import (
"bytes"
"errors"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
@ -45,52 +34,47 @@ func pctEncode(src []byte) []byte {
return dst
}
func escape(s string, allowReserved bool) (escaped string) {
func escape(s string, allowReserved bool) string {
if allowReserved {
escaped = string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
} else {
escaped = string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
return string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
}
return escaped
return string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
}
// A UriTemplate is a parsed representation of a URI template.
type UriTemplate struct {
// A uriTemplate is a parsed representation of a URI template.
type uriTemplate struct {
raw string
parts []templatePart
}
// Parse parses a URI template string into a UriTemplate object.
func Parse(rawtemplate string) (template *UriTemplate, err error) {
template = new(UriTemplate)
template.raw = rawtemplate
split := strings.Split(rawtemplate, "{")
template.parts = make([]templatePart, len(split)*2-1)
// parse parses a URI template string into a uriTemplate object.
func parse(rawTemplate string) (*uriTemplate, error) {
split := strings.Split(rawTemplate, "{")
parts := make([]templatePart, len(split)*2-1)
for i, s := range split {
if i == 0 {
if strings.Contains(s, "}") {
err = errors.New("unexpected }")
break
return nil, errors.New("unexpected }")
}
template.parts[i].raw = s
} else {
subsplit := strings.Split(s, "}")
if len(subsplit) != 2 {
err = errors.New("malformed template")
break
}
expression := subsplit[0]
template.parts[i*2-1], err = parseExpression(expression)
if err != nil {
break
}
template.parts[i*2].raw = subsplit[1]
parts[i].raw = s
continue
}
subsplit := strings.Split(s, "}")
if len(subsplit) != 2 {
return nil, errors.New("malformed template")
}
expression := subsplit[0]
var err error
parts[i*2-1], err = parseExpression(expression)
if err != nil {
return nil, err
}
parts[i*2].raw = subsplit[1]
}
if err != nil {
template = nil
}
return template, err
return &uriTemplate{
raw: rawTemplate,
parts: parts,
}, nil
}
type templatePart struct {
@ -160,6 +144,8 @@ func parseExpression(expression string) (result templatePart, err error) {
}
func parseTerm(term string) (result templateTerm, err error) {
// TODO(djd): Remove "*" suffix parsing once we check that no APIs have
// mistakenly used that attribute.
if strings.HasSuffix(term, "*") {
result.explode = true
term = term[:len(term)-1]
@ -185,175 +171,50 @@ func parseTerm(term string) (result templateTerm, err error) {
}
// Expand expands a URI template with a set of values to produce a string.
func (self *UriTemplate) Expand(value interface{}) (string, error) {
values, ismap := value.(map[string]interface{})
if !ismap {
if m, ismap := struct2map(value); !ismap {
return "", errors.New("expected map[string]interface{}, struct, or pointer to struct.")
} else {
return self.Expand(m)
}
}
func (t *uriTemplate) Expand(values map[string]string) string {
var buf bytes.Buffer
for _, p := range self.parts {
err := p.expand(&buf, values)
if err != nil {
return "", err
}
for _, p := range t.parts {
p.expand(&buf, values)
}
return buf.String(), nil
return buf.String()
}
func (self *templatePart) expand(buf *bytes.Buffer, values map[string]interface{}) error {
if len(self.raw) > 0 {
buf.WriteString(self.raw)
return nil
func (tp *templatePart) expand(buf *bytes.Buffer, values map[string]string) {
if len(tp.raw) > 0 {
buf.WriteString(tp.raw)
return
}
var zeroLen = buf.Len()
buf.WriteString(self.first)
var firstLen = buf.Len()
for _, term := range self.terms {
var first = true
for _, term := range tp.terms {
value, exists := values[term.name]
if !exists {
continue
}
if buf.Len() != firstLen {
buf.WriteString(self.sep)
}
switch v := value.(type) {
case string:
self.expandString(buf, term, v)
case []interface{}:
self.expandArray(buf, term, v)
case map[string]interface{}:
if term.truncate > 0 {
return errors.New("cannot truncate a map expansion")
}
self.expandMap(buf, term, v)
default:
if m, ismap := struct2map(value); ismap {
if term.truncate > 0 {
return errors.New("cannot truncate a map expansion")
}
self.expandMap(buf, term, m)
} else {
str := fmt.Sprintf("%v", value)
self.expandString(buf, term, str)
}
if first {
buf.WriteString(tp.first)
first = false
} else {
buf.WriteString(tp.sep)
}
tp.expandString(buf, term, value)
}
if buf.Len() == firstLen {
original := buf.Bytes()[:zeroLen]
buf.Reset()
buf.Write(original)
}
return nil
}
func (self *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
if self.named {
func (tp *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
if tp.named {
buf.WriteString(name)
if empty {
buf.WriteString(self.ifemp)
buf.WriteString(tp.ifemp)
} else {
buf.WriteString("=")
}
}
}
func (self *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
func (tp *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
if len(s) > t.truncate && t.truncate > 0 {
s = s[:t.truncate]
}
self.expandName(buf, t.name, len(s) == 0)
buf.WriteString(escape(s, self.allowReserved))
}
func (self *templatePart) expandArray(buf *bytes.Buffer, t templateTerm, a []interface{}) {
if len(a) == 0 {
return
} else if !t.explode {
self.expandName(buf, t.name, false)
}
for i, value := range a {
if t.explode && i > 0 {
buf.WriteString(self.sep)
} else if i > 0 {
buf.WriteString(",")
}
var s string
switch v := value.(type) {
case string:
s = v
default:
s = fmt.Sprintf("%v", v)
}
if len(s) > t.truncate && t.truncate > 0 {
s = s[:t.truncate]
}
if self.named && t.explode {
self.expandName(buf, t.name, len(s) == 0)
}
buf.WriteString(escape(s, self.allowReserved))
}
}
func (self *templatePart) expandMap(buf *bytes.Buffer, t templateTerm, m map[string]interface{}) {
if len(m) == 0 {
return
}
if !t.explode {
self.expandName(buf, t.name, len(m) == 0)
}
var firstLen = buf.Len()
for k, value := range m {
if firstLen != buf.Len() {
if t.explode {
buf.WriteString(self.sep)
} else {
buf.WriteString(",")
}
}
var s string
switch v := value.(type) {
case string:
s = v
default:
s = fmt.Sprintf("%v", v)
}
if t.explode {
buf.WriteString(escape(k, self.allowReserved))
buf.WriteRune('=')
buf.WriteString(escape(s, self.allowReserved))
} else {
buf.WriteString(escape(k, self.allowReserved))
buf.WriteRune(',')
buf.WriteString(escape(s, self.allowReserved))
}
}
}
func struct2map(v interface{}) (map[string]interface{}, bool) {
value := reflect.ValueOf(v)
switch value.Type().Kind() {
case reflect.Ptr:
return struct2map(value.Elem().Interface())
case reflect.Struct:
m := make(map[string]interface{})
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag
var name string
if strings.Contains(string(tag), ":") {
name = tag.Get("uri")
} else {
name = strings.TrimSpace(string(tag))
}
if len(name) == 0 {
name = value.Type().Field(i).Name
}
m[name] = value.Field(i).Interface()
}
return m, true
}
return nil, false
tp.expandName(buf, t.name, len(s) == 0)
buf.WriteString(escape(s, tp.allowReserved))
}

View File

@ -1,13 +1,13 @@
// 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 uritemplates
func Expand(path string, expansions map[string]string) (string, error) {
template, err := Parse(path)
func Expand(path string, values map[string]string) (string, error) {
template, err := parse(path)
if err != nil {
return "", err
}
values := make(map[string]interface{})
for k, v := range expansions {
values[k] = v
}
return template.Expand(values)
return template.Expand(values), nil
}