git mv Ingress ingress
This commit is contained in:
parent
34b949c134
commit
3da4e74e5a
2185 changed files with 754743 additions and 0 deletions
13965
Godeps/_workspace/src/google.golang.org/api/compute/v1/compute-api.json
generated
vendored
Normal file
13965
Godeps/_workspace/src/google.golang.org/api/compute/v1/compute-api.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
40618
Godeps/_workspace/src/google.golang.org/api/compute/v1/compute-gen.go
generated
vendored
Normal file
40618
Godeps/_workspace/src/google.golang.org/api/compute/v1/compute-gen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
690
Godeps/_workspace/src/google.golang.org/api/container/v1/container-api.json
generated
vendored
Normal file
690
Godeps/_workspace/src/google.golang.org/api/container/v1/container-api.json
generated
vendored
Normal file
|
|
@ -0,0 +1,690 @@
|
|||
{
|
||||
"kind": "discovery#restDescription",
|
||||
"etag": "\"ye6orv2F-1npMW3u9suM3a7C5Bo/FD4oCwspiZqN6eCjsBmxDzsj5B8\"",
|
||||
"discoveryVersion": "v1",
|
||||
"id": "container:v1",
|
||||
"name": "container",
|
||||
"version": "v1",
|
||||
"revision": "20150603",
|
||||
"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.",
|
||||
"ownerDomain": "google.com",
|
||||
"ownerName": "Google",
|
||||
"icons": {
|
||||
"x16": "http://www.google.com/images/icons/product/search-16.gif",
|
||||
"x32": "http://www.google.com/images/icons/product/search-32.gif"
|
||||
},
|
||||
"documentationLink": "https://cloud.google.com/container-engine/",
|
||||
"protocol": "rest",
|
||||
"baseUrl": "https://container.googleapis.com/",
|
||||
"basePath": "/",
|
||||
"rootUrl": "https://container.googleapis.com/",
|
||||
"servicePath": "",
|
||||
"batchPath": "batch",
|
||||
"parameters": {
|
||||
"access_token": {
|
||||
"type": "string",
|
||||
"description": "OAuth access token.",
|
||||
"location": "query"
|
||||
},
|
||||
"alt": {
|
||||
"type": "string",
|
||||
"description": "Data format for response.",
|
||||
"default": "json",
|
||||
"enumDescriptions": [
|
||||
"Responses with Content-Type of application/json",
|
||||
"Media download with context-dependent Content-Type",
|
||||
"Responses with Content-Type of application/x-protobuf"
|
||||
],
|
||||
"location": "query"
|
||||
},
|
||||
"bearer_token": {
|
||||
"type": "string",
|
||||
"description": "OAuth bearer token.",
|
||||
"location": "query"
|
||||
},
|
||||
"callback": {
|
||||
"type": "string",
|
||||
"description": "JSONP",
|
||||
"location": "query"
|
||||
},
|
||||
"fields": {
|
||||
"type": "string",
|
||||
"description": "Selector specifying which fields to include in a partial response.",
|
||||
"location": "query"
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
|
||||
"location": "query"
|
||||
},
|
||||
"oauth_token": {
|
||||
"type": "string",
|
||||
"description": "OAuth 2.0 token for the current user.",
|
||||
"location": "query"
|
||||
},
|
||||
"pp": {
|
||||
"type": "boolean",
|
||||
"description": "Pretty-print response.",
|
||||
"default": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"prettyPrint": {
|
||||
"type": "boolean",
|
||||
"description": "Returns response with indentations and line breaks.",
|
||||
"default": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"quotaUser": {
|
||||
"type": "string",
|
||||
"description": "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.",
|
||||
"location": "query"
|
||||
},
|
||||
"upload_protocol": {
|
||||
"type": "string",
|
||||
"description": "Upload protocol for media (e.g. \"raw\", \"multipart\").",
|
||||
"location": "query"
|
||||
},
|
||||
"uploadType": {
|
||||
"type": "string",
|
||||
"description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\").",
|
||||
"location": "query"
|
||||
},
|
||||
"$.xgafv": {
|
||||
"type": "string",
|
||||
"description": "V1 error format.",
|
||||
"enumDescriptions": [
|
||||
"v1 error format",
|
||||
"v2 error format"
|
||||
],
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"oauth2": {
|
||||
"scopes": {
|
||||
"https://www.googleapis.com/auth/cloud-platform": {
|
||||
"description": "View and manage your data across Google Cloud Platform services"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"ListClustersResponse": {
|
||||
"id": "ListClustersResponse",
|
||||
"type": "object",
|
||||
"description": "ListClustersResponse is the result of ListClustersRequest.",
|
||||
"properties": {
|
||||
"clusters": {
|
||||
"type": "array",
|
||||
"description": "A list of clusters in the project in the specified zone, or across all ones.",
|
||||
"items": {
|
||||
"$ref": "Cluster"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Cluster": {
|
||||
"id": "Cluster",
|
||||
"type": "object",
|
||||
"description": "A Google Container Engine cluster.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of this cluster. The name must be unique within this project and zone, and can be up to 40 characters with the following restrictions: * Lowercase letters, numbers, and hyphens only. * Must start with a letter. * Must end with a number or a letter."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "An optional description of this cluster."
|
||||
},
|
||||
"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.",
|
||||
"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."
|
||||
},
|
||||
"masterAuth": {
|
||||
"$ref": "MasterAuth",
|
||||
"description": "The authentication information for accessing the master."
|
||||
},
|
||||
"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\""
|
||||
},
|
||||
"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\""
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"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`."
|
||||
},
|
||||
"selfLink": {
|
||||
"type": "string",
|
||||
"description": "[Output only] Server-defined URL for the resource."
|
||||
},
|
||||
"zone": {
|
||||
"type": "string",
|
||||
"description": "[Output only] The name of the Google Compute Engine [zone](/compute/docs/zones#available) in which the cluster resides."
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"currentMasterVersion": {
|
||||
"type": "string",
|
||||
"description": "[Output only] The current software version of the master endpoint."
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"createTime": {
|
||||
"type": "string",
|
||||
"description": "[Output only] The time the cluster was created, in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) text format."
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "[Output only] The current status of this cluster.",
|
||||
"enum": [
|
||||
"STATUS_UNSPECIFIED",
|
||||
"PROVISIONING",
|
||||
"RUNNING",
|
||||
"RECONCILING",
|
||||
"STOPPING",
|
||||
"ERROR"
|
||||
]
|
||||
},
|
||||
"statusMessage": {
|
||||
"type": "string",
|
||||
"description": "[Output only] Additional information about the current status of this cluster, if available."
|
||||
},
|
||||
"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.",
|
||||
"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."
|
||||
},
|
||||
"instanceGroupUrls": {
|
||||
"type": "array",
|
||||
"description": "[Output only] The resource URLs of [instance groups](/compute/docs/instance-groups/) associated with this cluster.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"NodeConfig": {
|
||||
"id": "NodeConfig",
|
||||
"type": "object",
|
||||
"description": "Per-node parameters.",
|
||||
"properties": {
|
||||
"machineType": {
|
||||
"type": "string",
|
||||
"description": "The name of a Google Compute Engine [machine type](/compute/docs/machine-types) (e.g. `n1-standard-1`). If unspecified, the default machine type is `n1-standard-1`."
|
||||
},
|
||||
"diskSizeGb": {
|
||||
"type": "integer",
|
||||
"description": "Size of the disk attached to each node, specified in GB. The smallest allowed disk size is 10GB. If unspecified, the default disk size is 100GB.",
|
||||
"format": "int32"
|
||||
},
|
||||
"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.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"MasterAuth": {
|
||||
"id": "MasterAuth",
|
||||
"type": "object",
|
||||
"description": "The authentication information for accessing the master endpoint. Authentication can be done using HTTP basic auth or using client certificates.",
|
||||
"properties": {
|
||||
"username": {
|
||||
"type": "string",
|
||||
"description": "The username to use for HTTP basic authentication when accessing the Kubernetes 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."
|
||||
},
|
||||
"clusterCaCertificate": {
|
||||
"type": "string",
|
||||
"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."
|
||||
},
|
||||
"clientKey": {
|
||||
"type": "string",
|
||||
"description": "[Output only] Base64 encoded private key used by clients to authenticate to the cluster endpoint."
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateClusterRequest": {
|
||||
"id": "CreateClusterRequest",
|
||||
"type": "object",
|
||||
"description": "CreateClusterRequest creates a cluster.",
|
||||
"properties": {
|
||||
"cluster": {
|
||||
"$ref": "Cluster",
|
||||
"description": "A [cluster resource](/container-engine/reference/rest/v1/projects.zones.clusters)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Operation": {
|
||||
"id": "Operation",
|
||||
"type": "object",
|
||||
"description": "Defines the operation resource. All fields are output only.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The server-assigned ID for the operation."
|
||||
},
|
||||
"zone": {
|
||||
"type": "string",
|
||||
"description": "The name of the Google Compute Engine [zone](/compute/docs/zones#available) in which the operation is taking place."
|
||||
},
|
||||
"operationType": {
|
||||
"type": "string",
|
||||
"description": "The operation type.",
|
||||
"enum": [
|
||||
"TYPE_UNSPECIFIED",
|
||||
"CREATE_CLUSTER",
|
||||
"DELETE_CLUSTER",
|
||||
"UPGRADE_MASTER",
|
||||
"UPGRADE_NODES",
|
||||
"REPAIR_CLUSTER"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "The current status of the operation.",
|
||||
"enum": [
|
||||
"STATUS_UNSPECIFIED",
|
||||
"PENDING",
|
||||
"RUNNING",
|
||||
"DONE"
|
||||
]
|
||||
},
|
||||
"statusMessage": {
|
||||
"type": "string",
|
||||
"description": "If an error has occurred, a textual description of the error."
|
||||
},
|
||||
"selfLink": {
|
||||
"type": "string",
|
||||
"description": "Server-defined URL for the resource."
|
||||
},
|
||||
"targetLink": {
|
||||
"type": "string",
|
||||
"description": "Server-defined URL for the target of the operation."
|
||||
}
|
||||
}
|
||||
},
|
||||
"UpdateClusterRequest": {
|
||||
"id": "UpdateClusterRequest",
|
||||
"type": "object",
|
||||
"description": "UpdateClusterRequest updates a cluster.",
|
||||
"properties": {
|
||||
"update": {
|
||||
"$ref": "ClusterUpdate",
|
||||
"description": "A description of the update."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ClusterUpdate": {
|
||||
"id": "ClusterUpdate",
|
||||
"type": "object",
|
||||
"description": "ClusterUpdate describes an update to the cluster.",
|
||||
"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."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ListOperationsResponse": {
|
||||
"id": "ListOperationsResponse",
|
||||
"type": "object",
|
||||
"description": "ListOperationsResponse is the result of ListOperationsRequest.",
|
||||
"properties": {
|
||||
"operations": {
|
||||
"type": "array",
|
||||
"description": "A list of operations in the project in the specified zone.",
|
||||
"items": {
|
||||
"$ref": "Operation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ServerConfig": {
|
||||
"id": "ServerConfig",
|
||||
"type": "object",
|
||||
"description": "Container Engine Server configuration.",
|
||||
"properties": {
|
||||
"defaultClusterVersion": {
|
||||
"type": "string",
|
||||
"description": "What version this server deploys by default."
|
||||
},
|
||||
"validNodeVersions": {
|
||||
"type": "array",
|
||||
"description": "List of valid node upgrade target versions.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"projects": {
|
||||
"resources": {
|
||||
"zones": {
|
||||
"methods": {
|
||||
"getServerconfig": {
|
||||
"id": "container.projects.zones.getServerconfig",
|
||||
"path": "v1/projects/{projectId}/zones/{zone}/serverconfig",
|
||||
"httpMethod": "GET",
|
||||
"description": "Returns configuration info about the Container Engine service.",
|
||||
"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) to return operations for, or \"-\" for all zones.",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"projectId",
|
||||
"zone"
|
||||
],
|
||||
"response": {
|
||||
"$ref": "ServerConfig"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform"
|
||||
]
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"clusters": {
|
||||
"methods": {
|
||||
"list": {
|
||||
"id": "container.projects.zones.clusters.list",
|
||||
"path": "v1/projects/{projectId}/zones/{zone}/clusters",
|
||||
"httpMethod": "GET",
|
||||
"description": "Lists all clusters owned by a project in either the specified zone or all zones.",
|
||||
"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, or \"-\" for all zones.",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"projectId",
|
||||
"zone"
|
||||
],
|
||||
"response": {
|
||||
"$ref": "ListClustersResponse"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform"
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"id": "container.projects.zones.clusters.get",
|
||||
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}",
|
||||
"httpMethod": "GET",
|
||||
"description": "Gets 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).",
|
||||
"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 to retrieve.",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"projectId",
|
||||
"zone",
|
||||
"clusterId"
|
||||
],
|
||||
"response": {
|
||||
"$ref": "Cluster"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform"
|
||||
]
|
||||
},
|
||||
"create": {
|
||||
"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.",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"projectId",
|
||||
"zone"
|
||||
],
|
||||
"request": {
|
||||
"$ref": "CreateClusterRequest"
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Operation"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform"
|
||||
]
|
||||
},
|
||||
"update": {
|
||||
"id": "container.projects.zones.clusters.update",
|
||||
"path": "v1/projects/{projectId}/zones/{zone}/clusters/{clusterId}",
|
||||
"httpMethod": "PUT",
|
||||
"description": "Update 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).",
|
||||
"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 to upgrade.",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"projectId",
|
||||
"zone",
|
||||
"clusterId"
|
||||
],
|
||||
"request": {
|
||||
"$ref": "UpdateClusterRequest"
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Operation"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform"
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"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.",
|
||||
"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 to delete.",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"projectId",
|
||||
"zone",
|
||||
"clusterId"
|
||||
],
|
||||
"response": {
|
||||
"$ref": "Operation"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"operations": {
|
||||
"methods": {
|
||||
"list": {
|
||||
"id": "container.projects.zones.operations.list",
|
||||
"path": "v1/projects/{projectId}/zones/{zone}/operations",
|
||||
"httpMethod": "GET",
|
||||
"description": "Lists all operations in a project in a specific zone or all zones.",
|
||||
"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) to return operations for, or \"-\" for all zones.",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"projectId",
|
||||
"zone"
|
||||
],
|
||||
"response": {
|
||||
"$ref": "ListOperationsResponse"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform"
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"id": "container.projects.zones.operations.get",
|
||||
"path": "v1/projects/{projectId}/zones/{zone}/operations/{operationId}",
|
||||
"httpMethod": "GET",
|
||||
"description": "Gets the specified operation.",
|
||||
"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"
|
||||
},
|
||||
"operationId": {
|
||||
"type": "string",
|
||||
"description": "The server-assigned `name` of the operation.",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"projectId",
|
||||
"zone",
|
||||
"operationId"
|
||||
],
|
||||
"response": {
|
||||
"$ref": "Operation"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1695
Godeps/_workspace/src/google.golang.org/api/container/v1/container-gen.go
generated
vendored
Normal file
1695
Godeps/_workspace/src/google.golang.org/api/container/v1/container-gen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
10
Godeps/_workspace/src/google.golang.org/api/gensupport/doc.go
generated
vendored
Normal file
10
Godeps/_workspace/src/google.golang.org/api/gensupport/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// 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 is an internal implementation detail used by code
|
||||
// generated by the google-api-go-generator tool.
|
||||
//
|
||||
// This package may be modified at any time without regard for backwards
|
||||
// compatibility. It should not be used directly by API users.
|
||||
package gensupport
|
||||
172
Godeps/_workspace/src/google.golang.org/api/gensupport/json.go
generated
vendored
Normal file
172
Godeps/_workspace/src/google.golang.org/api/gensupport/json.go
generated
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright 2015 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MarshalJSON returns a JSON encoding of schema containing only selected fields.
|
||||
// A field is selected if:
|
||||
// * it has a non-empty value, or
|
||||
// * its field name is present in forceSendFields, and
|
||||
// * it is not a nil pointer or nil interface.
|
||||
// The JSON key for each selected field is taken from the field's json: struct tag.
|
||||
func MarshalJSON(schema interface{}, forceSendFields []string) ([]byte, error) {
|
||||
if len(forceSendFields) == 0 {
|
||||
return json.Marshal(schema)
|
||||
}
|
||||
|
||||
mustInclude := make(map[string]struct{})
|
||||
for _, f := range forceSendFields {
|
||||
mustInclude[f] = struct{}{}
|
||||
}
|
||||
|
||||
dataMap, err := schemaToMap(schema, mustInclude)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(dataMap)
|
||||
}
|
||||
|
||||
func schemaToMap(schema interface{}, mustInclude map[string]struct{}) (map[string]interface{}, error) {
|
||||
m := make(map[string]interface{})
|
||||
s := reflect.ValueOf(schema)
|
||||
st := s.Type()
|
||||
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
jsonTag := st.Field(i).Tag.Get("json")
|
||||
if jsonTag == "" {
|
||||
continue
|
||||
}
|
||||
tag, err := parseJSONTag(jsonTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tag.ignore {
|
||||
continue
|
||||
}
|
||||
|
||||
v := s.Field(i)
|
||||
f := st.Field(i)
|
||||
if !includeField(v, f, mustInclude) {
|
||||
continue
|
||||
}
|
||||
|
||||
// nil maps are treated as empty maps.
|
||||
if f.Type.Kind() == reflect.Map && v.IsNil() {
|
||||
m[tag.apiName] = map[string]string{}
|
||||
continue
|
||||
}
|
||||
|
||||
// nil slices are treated as empty slices.
|
||||
if f.Type.Kind() == reflect.Slice && v.IsNil() {
|
||||
m[tag.apiName] = []bool{}
|
||||
continue
|
||||
}
|
||||
|
||||
if tag.stringFormat {
|
||||
m[tag.apiName] = formatAsString(v, f.Type.Kind())
|
||||
} else {
|
||||
m[tag.apiName] = v.Interface()
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// formatAsString returns a string representation of v, dereferencing it first if possible.
|
||||
func formatAsString(v reflect.Value, kind reflect.Kind) string {
|
||||
if kind == reflect.Ptr && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", v.Interface())
|
||||
}
|
||||
|
||||
// jsonTag represents a restricted version of the struct tag format used by encoding/json.
|
||||
// It is used to describe the JSON encoding of fields in a Schema struct.
|
||||
type jsonTag struct {
|
||||
apiName string
|
||||
stringFormat bool
|
||||
ignore bool
|
||||
}
|
||||
|
||||
// parseJSONTag parses a restricted version of the struct tag format used by encoding/json.
|
||||
// The format of the tag must match that generated by the Schema.writeSchemaStruct method
|
||||
// in the api generator.
|
||||
func parseJSONTag(val string) (jsonTag, error) {
|
||||
if val == "-" {
|
||||
return jsonTag{ignore: true}, nil
|
||||
}
|
||||
|
||||
var tag jsonTag
|
||||
|
||||
i := strings.Index(val, ",")
|
||||
if i == -1 || val[:i] == "" {
|
||||
return tag, fmt.Errorf("malformed json tag: %s", val)
|
||||
}
|
||||
|
||||
tag = jsonTag{
|
||||
apiName: val[:i],
|
||||
}
|
||||
|
||||
switch val[i+1:] {
|
||||
case "omitempty":
|
||||
case "omitempty,string":
|
||||
tag.stringFormat = true
|
||||
default:
|
||||
return tag, fmt.Errorf("malformed json tag: %s", val)
|
||||
}
|
||||
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// 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.
|
||||
// However, many fields are not pointers, so there would be no way to delete these fields.
|
||||
// Rather than partially supporting field deletion, we ignore mustInclude for nil pointer fields.
|
||||
// Deletion will be handled by a separate mechanism.
|
||||
if f.Type.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
// The "any" type is represented as an interface{}. If this interface
|
||||
// is nil, there is no reasonable representation to send. We ignore
|
||||
// these fields, for the same reasons as given above for pointers.
|
||||
if f.Type.Kind() == reflect.Interface && v.IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := mustInclude[f.Name]
|
||||
return ok || !isEmptyValue(v)
|
||||
}
|
||||
|
||||
// isEmptyValue reports whether v is the empty value for its type. This
|
||||
// implementation is based on that of the encoding/json package, but its
|
||||
// correctness does not depend on it being identical. What's important is that
|
||||
// this function return false in situations where v should not be sent as part
|
||||
// of a PATCH operation.
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
367
Godeps/_workspace/src/google.golang.org/api/gensupport/json_test.go
generated
vendored
Normal file
367
Godeps/_workspace/src/google.golang.org/api/gensupport/json_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
// Copyright 2015 Google Inc. 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 (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
type schema struct {
|
||||
// Basic types
|
||||
B bool `json:"b,omitempty"`
|
||||
F float64 `json:"f,omitempty"`
|
||||
I int64 `json:"i,omitempty"`
|
||||
Istr int64 `json:"istr,omitempty,string"`
|
||||
Str string `json:"str,omitempty"`
|
||||
|
||||
// Pointers to basic types
|
||||
PB *bool `json:"pb,omitempty"`
|
||||
PF *float64 `json:"pf,omitempty"`
|
||||
PI *int64 `json:"pi,omitempty"`
|
||||
PIStr *int64 `json:"pistr,omitempty,string"`
|
||||
PStr *string `json:"pstr,omitempty"`
|
||||
|
||||
// Other types
|
||||
Int64s googleapi.Int64s `json:"i64s,omitempty"`
|
||||
S []int `json:"s,omitempty"`
|
||||
M map[string]string `json:"m,omitempty"`
|
||||
Any interface{} `json:"any,omitempty"`
|
||||
Child *child `json:"child,omitempty"`
|
||||
|
||||
ForceSendFields []string `json:"-"`
|
||||
}
|
||||
|
||||
type child struct {
|
||||
B bool `json:"childbool,omitempty"`
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
s schema
|
||||
want string
|
||||
}
|
||||
|
||||
func TestBasics(t *testing.T) {
|
||||
for _, tc := range []testCase{
|
||||
{
|
||||
s: schema{},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
ForceSendFields: []string{"B", "F", "I", "Istr", "Str", "PB", "PF", "PI", "PIStr", "PStr"},
|
||||
},
|
||||
want: `{"b":false,"f":0.0,"i":0,"istr":"0","str":""}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
B: true,
|
||||
F: 1.2,
|
||||
I: 1,
|
||||
Istr: 2,
|
||||
Str: "a",
|
||||
PB: googleapi.Bool(true),
|
||||
PF: googleapi.Float64(1.2),
|
||||
PI: googleapi.Int64(int64(1)),
|
||||
PIStr: googleapi.Int64(int64(2)),
|
||||
PStr: googleapi.String("a"),
|
||||
},
|
||||
want: `{"b":true,"f":1.2,"i":1,"istr":"2","str":"a","pb":true,"pf":1.2,"pi":1,"pistr":"2","pstr":"a"}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
B: false,
|
||||
F: 0.0,
|
||||
I: 0,
|
||||
Istr: 0,
|
||||
Str: "",
|
||||
PB: googleapi.Bool(false),
|
||||
PF: googleapi.Float64(0.0),
|
||||
PI: googleapi.Int64(int64(0)),
|
||||
PIStr: googleapi.Int64(int64(0)),
|
||||
PStr: googleapi.String(""),
|
||||
},
|
||||
want: `{"pb":false,"pf":0.0,"pi":0,"pistr":"0","pstr":""}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
B: false,
|
||||
F: 0.0,
|
||||
I: 0,
|
||||
Istr: 0,
|
||||
Str: "",
|
||||
PB: googleapi.Bool(false),
|
||||
PF: googleapi.Float64(0.0),
|
||||
PI: googleapi.Int64(int64(0)),
|
||||
PIStr: googleapi.Int64(int64(0)),
|
||||
PStr: googleapi.String(""),
|
||||
ForceSendFields: []string{"B", "F", "I", "Istr", "Str", "PB", "PF", "PI", "PIStr", "PStr"},
|
||||
},
|
||||
want: `{"b":false,"f":0.0,"i":0,"istr":"0","str":"","pb":false,"pf":0.0,"pi":0,"pistr":"0","pstr":""}`,
|
||||
},
|
||||
} {
|
||||
checkMarshalJSON(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSliceFields(t *testing.T) {
|
||||
for _, tc := range []testCase{
|
||||
{
|
||||
s: schema{},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{S: []int{}, Int64s: googleapi.Int64s{}},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{S: []int{1}, Int64s: googleapi.Int64s{1}},
|
||||
want: `{"s":[1],"i64s":["1"]}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
ForceSendFields: []string{"S", "Int64s"},
|
||||
},
|
||||
want: `{"s":[],"i64s":[]}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
S: []int{},
|
||||
Int64s: googleapi.Int64s{},
|
||||
ForceSendFields: []string{"S", "Int64s"},
|
||||
},
|
||||
want: `{"s":[],"i64s":[]}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
S: []int{1},
|
||||
Int64s: googleapi.Int64s{1},
|
||||
ForceSendFields: []string{"S", "Int64s"},
|
||||
},
|
||||
want: `{"s":[1],"i64s":["1"]}`,
|
||||
},
|
||||
} {
|
||||
checkMarshalJSON(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapField(t *testing.T) {
|
||||
for _, tc := range []testCase{
|
||||
{
|
||||
s: schema{},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{M: make(map[string]string)},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{M: map[string]string{"a": "b"}},
|
||||
want: `{"m":{"a":"b"}}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
ForceSendFields: []string{"M"},
|
||||
},
|
||||
want: `{"m":{}}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
M: make(map[string]string),
|
||||
ForceSendFields: []string{"M"},
|
||||
},
|
||||
want: `{"m":{}}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
M: map[string]string{"a": "b"},
|
||||
ForceSendFields: []string{"M"},
|
||||
},
|
||||
want: `{"m":{"a":"b"}}`,
|
||||
},
|
||||
} {
|
||||
checkMarshalJSON(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
type anyType struct {
|
||||
Field int
|
||||
}
|
||||
|
||||
func (a anyType) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"anyType value"`), nil
|
||||
}
|
||||
|
||||
func TestAnyField(t *testing.T) {
|
||||
// ForceSendFields has no effect on nil interfaces and interfaces that contain nil pointers.
|
||||
var nilAny *anyType
|
||||
for _, tc := range []testCase{
|
||||
{
|
||||
s: schema{},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{Any: nilAny},
|
||||
want: `{"any": null}`,
|
||||
},
|
||||
{
|
||||
s: schema{Any: &anyType{}},
|
||||
want: `{"any":"anyType value"}`,
|
||||
},
|
||||
{
|
||||
s: schema{Any: anyType{}},
|
||||
want: `{"any":"anyType value"}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
ForceSendFields: []string{"Any"},
|
||||
},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
Any: nilAny,
|
||||
ForceSendFields: []string{"Any"},
|
||||
},
|
||||
want: `{"any": null}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
Any: &anyType{},
|
||||
ForceSendFields: []string{"Any"},
|
||||
},
|
||||
want: `{"any":"anyType value"}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
Any: anyType{},
|
||||
ForceSendFields: []string{"Any"},
|
||||
},
|
||||
want: `{"any":"anyType value"}`,
|
||||
},
|
||||
} {
|
||||
checkMarshalJSON(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubschema(t *testing.T) {
|
||||
// Subschemas are always stored as pointers, so ForceSendFields has no effect on them.
|
||||
for _, tc := range []testCase{
|
||||
{
|
||||
s: schema{},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
ForceSendFields: []string{"Child"},
|
||||
},
|
||||
want: `{}`,
|
||||
},
|
||||
{
|
||||
s: schema{Child: &child{}},
|
||||
want: `{"child":{}}`,
|
||||
},
|
||||
{
|
||||
s: schema{
|
||||
Child: &child{},
|
||||
ForceSendFields: []string{"Child"},
|
||||
},
|
||||
want: `{"child":{}}`,
|
||||
},
|
||||
{
|
||||
s: schema{Child: &child{B: true}},
|
||||
want: `{"child":{"childbool":true}}`,
|
||||
},
|
||||
|
||||
{
|
||||
s: schema{
|
||||
Child: &child{B: true},
|
||||
ForceSendFields: []string{"Child"},
|
||||
},
|
||||
want: `{"child":{"childbool":true}}`,
|
||||
},
|
||||
} {
|
||||
checkMarshalJSON(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
// checkMarshalJSON verifies that calling schemaToMap on tc.s yields a result which is equivalent to tc.want.
|
||||
func checkMarshalJSON(t *testing.T, tc testCase) {
|
||||
doCheckMarshalJSON(t, tc.s, tc.s.ForceSendFields, tc.want)
|
||||
if len(tc.s.ForceSendFields) == 0 {
|
||||
// verify that the code path used when ForceSendFields
|
||||
// is non-empty produces the same output as the fast
|
||||
// path that is used when it is empty.
|
||||
doCheckMarshalJSON(t, tc.s, []string{"dummy"}, tc.want)
|
||||
}
|
||||
}
|
||||
|
||||
func doCheckMarshalJSON(t *testing.T, s schema, forceSendFields []string, wantJSON string) {
|
||||
encoded, err := MarshalJSON(s, forceSendFields)
|
||||
if err != nil {
|
||||
t.Fatalf("encoding json:\n got err: %v", err)
|
||||
}
|
||||
|
||||
// The expected and obtained JSON can differ in field ordering, so unmarshal before comparing.
|
||||
var got interface{}
|
||||
var want interface{}
|
||||
err = json.Unmarshal(encoded, &got)
|
||||
if err != nil {
|
||||
t.Fatalf("decoding json:\n got err: %v", err)
|
||||
}
|
||||
err = json.Unmarshal([]byte(wantJSON), &want)
|
||||
if err != nil {
|
||||
t.Fatalf("decoding json:\n got err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("schemaToMap:\ngot :%s\nwant:%s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseJSONTag(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
tag string
|
||||
want jsonTag
|
||||
}{
|
||||
{
|
||||
tag: "-",
|
||||
want: jsonTag{ignore: true},
|
||||
}, {
|
||||
tag: "name,omitempty",
|
||||
want: jsonTag{apiName: "name"},
|
||||
}, {
|
||||
tag: "name,omitempty,string",
|
||||
want: jsonTag{apiName: "name", stringFormat: true},
|
||||
},
|
||||
} {
|
||||
got, err := parseJSONTag(tc.tag)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing json:\n got err: %v\ntag: %q", err, tc.tag)
|
||||
}
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("parseJSONTage:\ngot :%s\nwant:%s", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestParseMalformedJSONTag(t *testing.T) {
|
||||
for _, tag := range []string{
|
||||
"",
|
||||
"name",
|
||||
"name,",
|
||||
"name,blah",
|
||||
"name,blah,string",
|
||||
",omitempty",
|
||||
",omitempty,string",
|
||||
"name,omitempty,string,blah",
|
||||
} {
|
||||
_, err := parseJSONTag(tag)
|
||||
if err == nil {
|
||||
t.Fatalf("parsing json: expected err, got nil for tag: %v", tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
164
Godeps/_workspace/src/google.golang.org/api/gensupport/media.go
generated
vendored
Normal file
164
Godeps/_workspace/src/google.golang.org/api/gensupport/media.go
generated
vendored
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// 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"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
const sniffBuffSize = 512
|
||||
|
||||
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 {
|
||||
r io.Reader
|
||||
start []byte // buffer for the sniffed bytes.
|
||||
err error // set to any error encountered while reading bytes to be sniffed.
|
||||
|
||||
ctype string // set on first sniff.
|
||||
sniffed bool // set to true on first sniff.
|
||||
}
|
||||
|
||||
func (sct *ContentSniffer) Read(p []byte) (n int, err error) {
|
||||
// Ensure that the content type is sniffed before any data is consumed from Reader.
|
||||
_, _ = sct.ContentType()
|
||||
|
||||
if len(sct.start) > 0 {
|
||||
n := copy(p, sct.start)
|
||||
sct.start = sct.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
|
||||
}
|
||||
|
||||
// Now we have handled all bytes that were buffered while sniffing. Now just delegate to the underlying reader.
|
||||
return sct.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 != ""
|
||||
}
|
||||
sct.sniffed = true
|
||||
// If ReadAll hits EOF, it returns err==nil.
|
||||
sct.start, sct.err = ioutil.ReadAll(io.LimitReader(sct.r, sniffBuffSize))
|
||||
|
||||
// Don't try to detect the content type based on possibly incomplete data.
|
||||
if sct.err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
sct.ctype = http.DetectContentType(sct.start)
|
||||
return sct.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)
|
||||
|
||||
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) {
|
||||
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"
|
||||
}
|
||||
return sniffer, typ
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
func typeHeader(contentType string) textproto.MIMEHeader {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Type", contentType)
|
||||
return h
|
||||
}
|
||||
113
Godeps/_workspace/src/google.golang.org/api/gensupport/media_test.go
generated
vendored
Normal file
113
Godeps/_workspace/src/google.golang.org/api/gensupport/media_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2015 Google Inc. 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"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// errReader reads out of a buffer until it is empty, then returns the specified error.
|
||||
type errReader struct {
|
||||
buf []byte
|
||||
err error
|
||||
}
|
||||
|
||||
var errBang error = errors.New("bang")
|
||||
|
||||
func (er *errReader) Read(p []byte) (int, error) {
|
||||
if len(er.buf) == 0 {
|
||||
if er.err == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return 0, er.err
|
||||
}
|
||||
n := copy(p, er.buf)
|
||||
er.buf = er.buf[n:]
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
type testCase struct {
|
||||
data []byte // the data to read from the Reader
|
||||
finalErr error // error to return after data has been read
|
||||
|
||||
wantContentType string
|
||||
wantContentTypeResult bool
|
||||
}
|
||||
|
||||
for _, tc := range []testCase{
|
||||
{
|
||||
data: []byte{0, 0, 0, 0},
|
||||
finalErr: nil,
|
||||
wantContentType: "application/octet-stream",
|
||||
wantContentTypeResult: true,
|
||||
},
|
||||
{
|
||||
data: []byte(""),
|
||||
finalErr: nil,
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantContentTypeResult: true,
|
||||
},
|
||||
{
|
||||
data: []byte(""),
|
||||
finalErr: errBang,
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantContentTypeResult: false,
|
||||
},
|
||||
{
|
||||
data: []byte("abc"),
|
||||
finalErr: nil,
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantContentTypeResult: true,
|
||||
},
|
||||
{
|
||||
data: []byte("abc"),
|
||||
finalErr: errBang,
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantContentTypeResult: false,
|
||||
},
|
||||
// The following examples contain more bytes than are buffered for sniffing.
|
||||
{
|
||||
data: bytes.Repeat([]byte("a"), 513),
|
||||
finalErr: nil,
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantContentTypeResult: true,
|
||||
},
|
||||
{
|
||||
data: bytes.Repeat([]byte("a"), 513),
|
||||
finalErr: errBang,
|
||||
wantContentType: "text/plain; charset=utf-8",
|
||||
wantContentTypeResult: true, // true because error is after first 512 bytes.
|
||||
},
|
||||
} {
|
||||
er := &errReader{buf: tc.data, err: tc.finalErr}
|
||||
|
||||
sct := NewContentSniffer(er)
|
||||
|
||||
// Even if was an error during the first 512 bytes, we should still be able to read those bytes.
|
||||
buf, err := ioutil.ReadAll(sct)
|
||||
|
||||
if !reflect.DeepEqual(buf, tc.data) {
|
||||
t.Fatalf("Failed reading buffer: got: %q; want:%q", buf, tc.data)
|
||||
}
|
||||
|
||||
if err != tc.finalErr {
|
||||
t.Fatalf("Reading buffer error: got: %v; want: %v", err, tc.finalErr)
|
||||
}
|
||||
|
||||
ct, ok := sct.ContentType()
|
||||
if ok != tc.wantContentTypeResult {
|
||||
t.Fatalf("Content type result got: %v; want: %v", ok, tc.wantContentTypeResult)
|
||||
}
|
||||
if ok && ct != tc.wantContentType {
|
||||
t.Fatalf("Content type got: %q; want: %q", ct, tc.wantContentType)
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Godeps/_workspace/src/google.golang.org/api/gensupport/params.go
generated
vendored
Normal file
31
Godeps/_workspace/src/google.golang.org/api/gensupport/params.go
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2015 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 "net/url"
|
||||
|
||||
// URLParams is a simplified replacement for url.Values
|
||||
// that safely builds up URL parameters for encoding.
|
||||
type URLParams map[string][]string
|
||||
|
||||
// Set sets the key to value.
|
||||
// It replaces any existing values.
|
||||
func (u URLParams) Set(key, value string) {
|
||||
u[key] = []string{value}
|
||||
}
|
||||
|
||||
// SetMulti sets the key to an array of values.
|
||||
// It replaces any existing values.
|
||||
// Note that values must not be modified after calling SetMulti
|
||||
// so the caller is responsible for making a copy if necessary.
|
||||
func (u URLParams) SetMulti(key string, values []string) {
|
||||
u[key] = values
|
||||
}
|
||||
|
||||
// Encode encodes the values into ``URL encoded'' form
|
||||
// ("bar=baz&foo=quux") sorted by key.
|
||||
func (u URLParams) Encode() string {
|
||||
return url.Values(u).Encode()
|
||||
}
|
||||
473
Godeps/_workspace/src/google.golang.org/api/googleapi/googleapi.go
generated
vendored
Normal file
473
Godeps/_workspace/src/google.golang.org/api/googleapi/googleapi.go
generated
vendored
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package googleapi contains the common code shared by all Google API
|
||||
// libraries.
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"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"
|
||||
)
|
||||
|
||||
// ContentTyper is an interface for Readers which know (or would like
|
||||
// to override) their Content-Type. If a media body doesn't implement
|
||||
// ContentTyper, the type is sniffed from the content using
|
||||
// http.DetectContentType.
|
||||
type ContentTyper interface {
|
||||
ContentType() string
|
||||
}
|
||||
|
||||
// A SizeReaderAt is a ReaderAt with a Size method.
|
||||
// An io.SectionReader implements SizeReaderAt.
|
||||
type SizeReaderAt interface {
|
||||
io.ReaderAt
|
||||
Size() int64
|
||||
}
|
||||
|
||||
// ServerResponse is embedded in each Do response and
|
||||
// provides the HTTP status code and header sent by the server.
|
||||
type ServerResponse struct {
|
||||
// HTTPStatusCode is the server's response status code.
|
||||
// When using a resource method's Do call, this will always be in the 2xx range.
|
||||
HTTPStatusCode int
|
||||
// Header contains the response header fields from the server.
|
||||
Header http.Header
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
// Error contains an error response from the server.
|
||||
type Error struct {
|
||||
// Code is the HTTP response status code and will always be populated.
|
||||
Code int `json:"code"`
|
||||
// Message is the server response message and is only populated when
|
||||
// explicitly referenced by the JSON server response.
|
||||
Message string `json:"message"`
|
||||
// Body is the raw response returned by the server.
|
||||
// It is often but not always JSON, depending on how the request fails.
|
||||
Body string
|
||||
// Header contains the response header fields from the server.
|
||||
Header http.Header
|
||||
|
||||
Errors []ErrorItem
|
||||
}
|
||||
|
||||
// ErrorItem is a detailed error code & message from the Google API frontend.
|
||||
type ErrorItem struct {
|
||||
// Reason is the typed error code. For example: "some_example".
|
||||
Reason string `json:"reason"`
|
||||
// Message is the human-readable description of the error.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
if len(e.Errors) == 0 && e.Message == "" {
|
||||
return fmt.Sprintf("googleapi: got HTTP response code %d with body: %v", e.Code, e.Body)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "googleapi: Error %d: ", e.Code)
|
||||
if e.Message != "" {
|
||||
fmt.Fprintf(&buf, "%s", e.Message)
|
||||
}
|
||||
if len(e.Errors) == 0 {
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
if len(e.Errors) == 1 && e.Errors[0].Message == e.Message {
|
||||
fmt.Fprintf(&buf, ", %s", e.Errors[0].Reason)
|
||||
return buf.String()
|
||||
}
|
||||
fmt.Fprintln(&buf, "\nMore details:")
|
||||
for _, v := range e.Errors {
|
||||
fmt.Fprintf(&buf, "Reason: %s, Message: %s\n", v.Reason, v.Message)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type errorReply struct {
|
||||
Error *Error `json:"error"`
|
||||
}
|
||||
|
||||
// CheckResponse returns an error (of type *Error) if the response
|
||||
// status code is not 2xx.
|
||||
func CheckResponse(res *http.Response) error {
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
slurp, err := ioutil.ReadAll(res.Body)
|
||||
if err == nil {
|
||||
jerr := new(errorReply)
|
||||
err = json.Unmarshal(slurp, jerr)
|
||||
if err == nil && jerr.Error != nil {
|
||||
if jerr.Error.Code == 0 {
|
||||
jerr.Error.Code = res.StatusCode
|
||||
}
|
||||
jerr.Error.Body = string(slurp)
|
||||
return jerr.Error
|
||||
}
|
||||
}
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(slurp),
|
||||
Header: res.Header,
|
||||
}
|
||||
}
|
||||
|
||||
// IsNotModified reports whether err is the result of the
|
||||
// server replying with http.StatusNotModified.
|
||||
// Such error values are sometimes returned by "Do" methods
|
||||
// on calls when If-None-Match is used.
|
||||
func IsNotModified(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
ae, ok := err.(*Error)
|
||||
return ok && ae.Code == http.StatusNotModified
|
||||
}
|
||||
|
||||
// CheckMediaResponse returns an error (of type *Error) if the response
|
||||
// status code is not 2xx. Unlike CheckResponse it does not assume the
|
||||
// body is a JSON error document.
|
||||
func CheckMediaResponse(res *http.Response) error {
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
slurp, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
|
||||
res.Body.Close()
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(slurp),
|
||||
}
|
||||
}
|
||||
|
||||
type MarshalStyle bool
|
||||
|
||||
var WithDataWrapper = MarshalStyle(true)
|
||||
var WithoutDataWrapper = MarshalStyle(false)
|
||||
|
||||
func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if wrap {
|
||||
buf.Write([]byte(`{"data": `))
|
||||
}
|
||||
err := json.NewEncoder(buf).Encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wrap {
|
||||
buf.Write([]byte(`}`))
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// endingWithErrorReader from r until it returns an error. If the
|
||||
// final error from r is io.EOF and e is non-nil, e is used instead.
|
||||
type endingWithErrorReader struct {
|
||||
r io.Reader
|
||||
e error
|
||||
}
|
||||
|
||||
func (er endingWithErrorReader) Read(p []byte) (n int, err error) {
|
||||
n, err = er.r.Read(p)
|
||||
if err == io.EOF && er.e != nil {
|
||||
err = er.e
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// countingWriter counts the number of bytes it receives to write, but
|
||||
// discards them.
|
||||
type countingWriter struct {
|
||||
n *int64
|
||||
}
|
||||
|
||||
func (w countingWriter) Write(p []byte) (int, error) {
|
||||
*w.n += int64(len(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// ProgressUpdater is a function that is called upon every progress update of a resumable upload.
|
||||
// This is the only part of a resumable upload (from googleapi) that is usable by the developer.
|
||||
// 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.
|
||||
// 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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func ResolveRelative(basestr, relstr string) string {
|
||||
u, _ := url.Parse(basestr)
|
||||
rel, _ := url.Parse(relstr)
|
||||
u = u.ResolveReference(rel)
|
||||
us := u.String()
|
||||
us = strings.Replace(us, "%7B", "{", -1)
|
||||
us = strings.Replace(us, "%7D", "}", -1)
|
||||
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
|
||||
// the map supplied.
|
||||
//
|
||||
// This calls SetOpaque to avoid encoding of the parameters in the URL path.
|
||||
func Expand(u *url.URL, expansions map[string]string) {
|
||||
expanded, err := uritemplates.Expand(u.Path, expansions)
|
||||
if err == nil {
|
||||
u.Path = expanded
|
||||
SetOpaque(u)
|
||||
}
|
||||
}
|
||||
|
||||
// CloseBody is used to close res.Body.
|
||||
// Prior to calling Close, it also tries to Read a small amount to see an EOF.
|
||||
// Not seeing an EOF can prevent HTTP Transports from reusing connections.
|
||||
func CloseBody(res *http.Response) {
|
||||
if res == nil || res.Body == nil {
|
||||
return
|
||||
}
|
||||
// Justification for 3 byte reads: two for up to "\r\n" after
|
||||
// a JSON/XML document, and then 1 to see EOF if we haven't yet.
|
||||
// TODO(bradfitz): detect Go 1.3+ and skip these reads.
|
||||
// See https://codereview.appspot.com/58240043
|
||||
// and https://codereview.appspot.com/49570044
|
||||
buf := make([]byte, 1)
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err := res.Body.Read(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
}
|
||||
|
||||
// VariantType returns the type name of the given variant.
|
||||
// If the map doesn't contain the named key or the value is not a []interface{}, "" is returned.
|
||||
// This is used to support "variant" APIs that can return one of a number of different types.
|
||||
func VariantType(t map[string]interface{}) string {
|
||||
s, _ := t["type"].(string)
|
||||
return s
|
||||
}
|
||||
|
||||
// ConvertVariant uses the JSON encoder/decoder to fill in the struct 'dst' with the fields found in variant 'v'.
|
||||
// This is used to support "variant" APIs that can return one of a number of different types.
|
||||
// It reports whether the conversion was successful.
|
||||
func ConvertVariant(v map[string]interface{}, dst interface{}) bool {
|
||||
var buf bytes.Buffer
|
||||
err := json.NewEncoder(&buf).Encode(v)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return json.Unmarshal(buf.Bytes(), dst) == nil
|
||||
}
|
||||
|
||||
// A Field names a field to be retrieved with a partial response.
|
||||
// See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
|
||||
//
|
||||
// Partial responses can dramatically reduce the amount of data that must be sent to your application.
|
||||
// In order to request partial responses, you can specify the full list of fields
|
||||
// that your application needs by adding the Fields option to your request.
|
||||
//
|
||||
// Field strings use camelCase with leading lower-case characters to identify fields within the response.
|
||||
//
|
||||
// For example, if your response has a "NextPageToken" and a slice of "Items" with "Id" fields,
|
||||
// you could request just those fields like this:
|
||||
//
|
||||
// svc.Events.List().Fields("nextPageToken", "items/id").Do()
|
||||
//
|
||||
// or if you were also interested in each Item's "Updated" field, you can combine them like this:
|
||||
//
|
||||
// svc.Events.List().Fields("nextPageToken", "items(id,updated)").Do()
|
||||
//
|
||||
// More information about field formatting can be found here:
|
||||
// https://developers.google.com/+/api/#fields-syntax
|
||||
//
|
||||
// Another way to find field names is through the Google API explorer:
|
||||
// https://developers.google.com/apis-explorer/#p/
|
||||
type Field string
|
||||
|
||||
// CombineFields combines fields into a single string.
|
||||
func CombineFields(s []Field) string {
|
||||
r := make([]string, len(s))
|
||||
for i, v := range s {
|
||||
r[i] = string(v)
|
||||
}
|
||||
return strings.Join(r, ",")
|
||||
}
|
||||
599
Godeps/_workspace/src/google.golang.org/api/googleapi/googleapi_test.go
generated
vendored
Normal file
599
Godeps/_workspace/src/google.golang.org/api/googleapi/googleapi_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type SetOpaqueTest struct {
|
||||
in *url.URL
|
||||
wantRequestURI string
|
||||
}
|
||||
|
||||
var setOpaqueTests = []SetOpaqueTest{
|
||||
// no path
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "www.golang.org",
|
||||
},
|
||||
"http://www.golang.org",
|
||||
},
|
||||
// path
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "www.golang.org",
|
||||
Path: "/",
|
||||
},
|
||||
"http://www.golang.org/",
|
||||
},
|
||||
// file with hex escaping
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "https",
|
||||
Host: "www.golang.org",
|
||||
Path: "/file%20one&two",
|
||||
},
|
||||
"https://www.golang.org/file%20one&two",
|
||||
},
|
||||
// query
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "www.golang.org",
|
||||
Path: "/",
|
||||
RawQuery: "q=go+language",
|
||||
},
|
||||
"http://www.golang.org/?q=go+language",
|
||||
},
|
||||
// file with hex escaping in path plus query
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "https",
|
||||
Host: "www.golang.org",
|
||||
Path: "/file%20one&two",
|
||||
RawQuery: "q=go+language",
|
||||
},
|
||||
"https://www.golang.org/file%20one&two?q=go+language",
|
||||
},
|
||||
// query with hex escaping
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "www.golang.org",
|
||||
Path: "/",
|
||||
RawQuery: "q=go%20language",
|
||||
},
|
||||
"http://www.golang.org/?q=go%20language",
|
||||
},
|
||||
}
|
||||
|
||||
// prefixTmpl is a template for the expected prefix of the output of writing
|
||||
// an HTTP request.
|
||||
const prefixTmpl = "GET %v HTTP/1.1\r\nHost: %v\r\n"
|
||||
|
||||
func TestSetOpaque(t *testing.T) {
|
||||
for _, test := range setOpaqueTests {
|
||||
u := *test.in
|
||||
SetOpaque(&u)
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
r := &http.Request{URL: &u}
|
||||
if err := r.Write(w); err != nil {
|
||||
t.Errorf("write request: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
prefix := fmt.Sprintf(prefixTmpl, test.wantRequestURI, test.in.Host)
|
||||
if got := string(w.Bytes()); !strings.HasPrefix(got, prefix) {
|
||||
t.Errorf("got %q expected prefix %q", got, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ExpandTest struct {
|
||||
in string
|
||||
expansions map[string]string
|
||||
want string
|
||||
}
|
||||
|
||||
var expandTests = []ExpandTest{
|
||||
// no expansions
|
||||
{
|
||||
"http://www.golang.org/",
|
||||
map[string]string{},
|
||||
"http://www.golang.org/",
|
||||
},
|
||||
// one expansion, no escaping
|
||||
{
|
||||
"http://www.golang.org/{bucket}/delete",
|
||||
map[string]string{
|
||||
"bucket": "red",
|
||||
},
|
||||
"http://www.golang.org/red/delete",
|
||||
},
|
||||
// one expansion, with hex escapes
|
||||
{
|
||||
"http://www.golang.org/{bucket}/delete",
|
||||
map[string]string{
|
||||
"bucket": "red/blue",
|
||||
},
|
||||
"http://www.golang.org/red%2Fblue/delete",
|
||||
},
|
||||
// one expansion, with space
|
||||
{
|
||||
"http://www.golang.org/{bucket}/delete",
|
||||
map[string]string{
|
||||
"bucket": "red or blue",
|
||||
},
|
||||
"http://www.golang.org/red%20or%20blue/delete",
|
||||
},
|
||||
// expansion not found
|
||||
{
|
||||
"http://www.golang.org/{object}/delete",
|
||||
map[string]string{
|
||||
"bucket": "red or blue",
|
||||
},
|
||||
"http://www.golang.org//delete",
|
||||
},
|
||||
// multiple expansions
|
||||
{
|
||||
"http://www.golang.org/{one}/{two}/{three}/get",
|
||||
map[string]string{
|
||||
"one": "ONE",
|
||||
"two": "TWO",
|
||||
"three": "THREE",
|
||||
},
|
||||
"http://www.golang.org/ONE/TWO/THREE/get",
|
||||
},
|
||||
// utf-8 characters
|
||||
{
|
||||
"http://www.golang.org/{bucket}/get",
|
||||
map[string]string{
|
||||
"bucket": "£100",
|
||||
},
|
||||
"http://www.golang.org/%C2%A3100/get",
|
||||
},
|
||||
// punctuations
|
||||
{
|
||||
"http://www.golang.org/{bucket}/get",
|
||||
map[string]string{
|
||||
"bucket": `/\@:,.`,
|
||||
},
|
||||
"http://www.golang.org/%2F%5C%40%3A%2C./get",
|
||||
},
|
||||
// mis-matched brackets
|
||||
{
|
||||
"http://www.golang.org/{bucket/get",
|
||||
map[string]string{
|
||||
"bucket": "red",
|
||||
},
|
||||
"http://www.golang.org/{bucket/get",
|
||||
},
|
||||
// "+" prefix for suppressing escape
|
||||
// See also: http://tools.ietf.org/html/rfc6570#section-3.2.3
|
||||
{
|
||||
"http://www.golang.org/{+topic}",
|
||||
map[string]string{
|
||||
"topic": "/topics/myproject/mytopic",
|
||||
},
|
||||
// The double slashes here look weird, but it's intentional
|
||||
"http://www.golang.org//topics/myproject/mytopic",
|
||||
},
|
||||
}
|
||||
|
||||
func TestExpand(t *testing.T) {
|
||||
for i, test := range expandTests {
|
||||
u := url.URL{
|
||||
Path: test.in,
|
||||
}
|
||||
Expand(&u, test.expansions)
|
||||
got := u.Path
|
||||
if got != test.want {
|
||||
t.Errorf("got %q expected %q in test %d", got, test.want, i+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CheckResponseTest struct {
|
||||
in *http.Response
|
||||
bodyText string
|
||||
want error
|
||||
errText string
|
||||
}
|
||||
|
||||
var checkResponseTests = []CheckResponseTest{
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
},
|
||||
"",
|
||||
nil,
|
||||
"",
|
||||
},
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
`{"error":{}}`,
|
||||
&Error{
|
||||
Code: http.StatusInternalServerError,
|
||||
Body: `{"error":{}}`,
|
||||
},
|
||||
`googleapi: got HTTP response code 500 with body: {"error":{}}`,
|
||||
},
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
},
|
||||
`{"error":{"message":"Error message for StatusNotFound."}}`,
|
||||
&Error{
|
||||
Code: http.StatusNotFound,
|
||||
Message: "Error message for StatusNotFound.",
|
||||
Body: `{"error":{"message":"Error message for StatusNotFound."}}`,
|
||||
},
|
||||
"googleapi: Error 404: Error message for StatusNotFound.",
|
||||
},
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
`{"error":"invalid_token","error_description":"Invalid Value"}`,
|
||||
&Error{
|
||||
Code: http.StatusBadRequest,
|
||||
Body: `{"error":"invalid_token","error_description":"Invalid Value"}`,
|
||||
},
|
||||
`googleapi: got HTTP response code 400 with body: {"error":"invalid_token","error_description":"Invalid Value"}`,
|
||||
},
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
`{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
|
||||
&Error{
|
||||
Code: http.StatusBadRequest,
|
||||
Errors: []ErrorItem{
|
||||
{
|
||||
Reason: "keyInvalid",
|
||||
Message: "Bad Request",
|
||||
},
|
||||
},
|
||||
Body: `{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
|
||||
Message: "Bad Request",
|
||||
},
|
||||
"googleapi: Error 400: Bad Request, keyInvalid",
|
||||
},
|
||||
}
|
||||
|
||||
func TestCheckResponse(t *testing.T) {
|
||||
for _, test := range checkResponseTests {
|
||||
res := test.in
|
||||
if test.bodyText != "" {
|
||||
res.Body = ioutil.NopCloser(strings.NewReader(test.bodyText))
|
||||
}
|
||||
g := CheckResponse(res)
|
||||
if !reflect.DeepEqual(g, test.want) {
|
||||
t.Errorf("CheckResponse: got %v, want %v", g, test.want)
|
||||
gotJson, err := json.Marshal(g)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
wantJson, err := json.Marshal(test.want)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Errorf("json(got): %q\njson(want): %q", string(gotJson), string(wantJson))
|
||||
}
|
||||
if g != nil && g.Error() != test.errText {
|
||||
t.Errorf("CheckResponse: unexpected error message.\nGot: %q\nwant: %q", g, test.errText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type VariantPoint struct {
|
||||
Type string
|
||||
Coordinates []float64
|
||||
}
|
||||
|
||||
type VariantTest struct {
|
||||
in map[string]interface{}
|
||||
result bool
|
||||
want VariantPoint
|
||||
}
|
||||
|
||||
var coords = []interface{}{1.0, 2.0}
|
||||
|
||||
var variantTests = []VariantTest{
|
||||
{
|
||||
in: map[string]interface{}{
|
||||
"type": "Point",
|
||||
"coordinates": coords,
|
||||
},
|
||||
result: true,
|
||||
want: VariantPoint{
|
||||
Type: "Point",
|
||||
Coordinates: []float64{1.0, 2.0},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: map[string]interface{}{
|
||||
"type": "Point",
|
||||
"bogus": coords,
|
||||
},
|
||||
result: true,
|
||||
want: VariantPoint{
|
||||
Type: "Point",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestVariantType(t *testing.T) {
|
||||
for _, test := range variantTests {
|
||||
if g := VariantType(test.in); g != test.want.Type {
|
||||
t.Errorf("VariantType(%v): got %v, want %v", test.in, g, test.want.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertVariant(t *testing.T) {
|
||||
for _, test := range variantTests {
|
||||
g := VariantPoint{}
|
||||
r := ConvertVariant(test.in, &g)
|
||||
if r != test.result {
|
||||
t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, r, test.result)
|
||||
}
|
||||
if !reflect.DeepEqual(g, test.want) {
|
||||
t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, g, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type unexpectedReader struct{}
|
||||
|
||||
func (unexpectedReader) Read([]byte) (int, error) {
|
||||
return 0, fmt.Errorf("unexpected read in test.")
|
||||
}
|
||||
|
||||
var contentRangeRE = regexp.MustCompile(`^bytes (\d+)\-(\d+)/(\d+)$`)
|
||||
|
||||
func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
t.req = req
|
||||
if rng := req.Header.Get("Content-Range"); rng != "" && !strings.HasPrefix(rng, "bytes */") { // Read the data
|
||||
m := contentRangeRE.FindStringSubmatch(rng)
|
||||
if len(m) != 4 {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
start, err := strconv.ParseInt(m[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
end, err := strconv.ParseInt(m[2], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
totalSize, err := strconv.ParseInt(m[3], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
partialSize := end - start + 1
|
||||
t.buf, err = ioutil.ReadAll(req.Body)
|
||||
if err != nil || int64(len(t.buf)) != partialSize {
|
||||
return nil, fmt.Errorf("unable to read %v bytes from request data, n=%v: %v", partialSize, len(t.buf), err)
|
||||
}
|
||||
if totalSize == end+1 {
|
||||
t.statusCode = 200 // signify completion of transfer
|
||||
}
|
||||
}
|
||||
f := ioutil.NopCloser(unexpectedReader{})
|
||||
res := &http.Response{
|
||||
Body: f,
|
||||
StatusCode: t.statusCode,
|
||||
Header: http.Header{},
|
||||
}
|
||||
if t.rangeVal != "" {
|
||||
res.Header.Set("Range", t.rangeVal)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type testTransport struct {
|
||||
req *http.Request
|
||||
statusCode int
|
||||
rangeVal string
|
||||
want int64
|
||||
buf []byte
|
||||
}
|
||||
|
||||
var statusTests = []*testTransport{
|
||||
&testTransport{statusCode: 308, want: 0},
|
||||
&testTransport{statusCode: 308, rangeVal: "bytes=0-0", want: 1},
|
||||
&testTransport{statusCode: 308, rangeVal: "bytes=0-42", want: 43},
|
||||
}
|
||||
|
||||
func TestTransferStatus(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
for _, tr := range statusTests {
|
||||
rx := &ResumableUpload{
|
||||
Client: &http.Client{Transport: tr},
|
||||
}
|
||||
g, _, err := rx.transferStatus(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if g != tr.want {
|
||||
t.Errorf("transferStatus got %v, want %v", g, tr.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *interruptedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
t.req = req
|
||||
if rng := req.Header.Get("Content-Range"); rng != "" && !strings.HasPrefix(rng, "bytes */") {
|
||||
t.interruptCount += 1
|
||||
if t.interruptCount%7 == 0 { // Respond with a "service unavailable" error
|
||||
res := &http.Response{
|
||||
StatusCode: http.StatusServiceUnavailable,
|
||||
Header: http.Header{},
|
||||
}
|
||||
t.rangeVal = fmt.Sprintf("bytes=0-%v", len(t.buf)-1) // Set the response for next time
|
||||
return res, nil
|
||||
}
|
||||
m := contentRangeRE.FindStringSubmatch(rng)
|
||||
if len(m) != 4 {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
start, err := strconv.ParseInt(m[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
end, err := strconv.ParseInt(m[2], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
totalSize, err := strconv.ParseInt(m[3], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
partialSize := end - start + 1
|
||||
buf, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil || int64(len(buf)) != partialSize {
|
||||
return nil, fmt.Errorf("unable to read %v bytes from request data, n=%v: %v", partialSize, len(buf), err)
|
||||
}
|
||||
t.buf = append(t.buf, buf...)
|
||||
if totalSize == end+1 {
|
||||
t.statusCode = 200 // signify completion of transfer
|
||||
}
|
||||
}
|
||||
f := ioutil.NopCloser(unexpectedReader{})
|
||||
res := &http.Response{
|
||||
Body: f,
|
||||
StatusCode: t.statusCode,
|
||||
Header: http.Header{},
|
||||
}
|
||||
if t.rangeVal != "" {
|
||||
res.Header.Set("Range", t.rangeVal)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type interruptedTransport struct {
|
||||
req *http.Request
|
||||
statusCode int
|
||||
rangeVal string
|
||||
interruptCount int
|
||||
buf []byte
|
||||
progressUpdates []int64
|
||||
}
|
||||
|
||||
func (tr *interruptedTransport) ProgressUpdate(current int64) {
|
||||
tr.progressUpdates = append(tr.progressUpdates, current)
|
||||
}
|
||||
|
||||
func TestInterruptedTransferChunks(t *testing.T) {
|
||||
f, err := os.Open("googleapi.go")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open googleapi.go: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
slurp, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to slurp file: %v", err)
|
||||
}
|
||||
st, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to stat googleapi.go: %v", err)
|
||||
}
|
||||
tr := &interruptedTransport{
|
||||
statusCode: 308,
|
||||
buf: make([]byte, 0, st.Size()),
|
||||
}
|
||||
oldChunkSize := chunkSize
|
||||
defer func() { chunkSize = oldChunkSize }()
|
||||
chunkSize = 100 // override to process small chunks for test.
|
||||
|
||||
sleep = func(time.Duration) {} // override time.Sleep
|
||||
rx := &ResumableUpload{
|
||||
Client: &http.Client{Transport: tr},
|
||||
Media: f,
|
||||
MediaType: "text/plain",
|
||||
ContentLength: st.Size(),
|
||||
Callback: tr.ProgressUpdate,
|
||||
}
|
||||
res, err := rx.Upload(context.Background())
|
||||
if err != nil || res == nil || res.StatusCode != http.StatusOK {
|
||||
if res == nil {
|
||||
t.Errorf("transferChunks not successful, res=nil: %v", err)
|
||||
} else {
|
||||
t.Errorf("transferChunks not successful, statusCode=%v: %v", res.StatusCode, err)
|
||||
}
|
||||
}
|
||||
if len(tr.buf) != len(slurp) || bytes.Compare(tr.buf, slurp) != 0 {
|
||||
t.Errorf("transferred file corrupted:\ngot %s\nwant %s", tr.buf, slurp)
|
||||
}
|
||||
want := []int64{}
|
||||
for i := chunkSize; i <= st.Size(); i += chunkSize {
|
||||
want = append(want, i)
|
||||
}
|
||||
if st.Size()%chunkSize != 0 {
|
||||
want = append(want, st.Size())
|
||||
}
|
||||
if !reflect.DeepEqual(tr.progressUpdates, want) {
|
||||
t.Errorf("progress update error, got %v, want %v", tr.progressUpdates, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelUpload(t *testing.T) {
|
||||
f, err := os.Open("googleapi.go")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open googleapi.go: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
st, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to stat googleapi.go: %v", err)
|
||||
}
|
||||
tr := &interruptedTransport{
|
||||
statusCode: 308,
|
||||
buf: make([]byte, 0, st.Size()),
|
||||
}
|
||||
oldChunkSize := chunkSize
|
||||
defer func() { chunkSize = oldChunkSize }()
|
||||
chunkSize = 100 // override to process small chunks for test.
|
||||
|
||||
sleep = func(time.Duration) {} // override time.Sleep
|
||||
rx := &ResumableUpload{
|
||||
Client: &http.Client{Transport: tr},
|
||||
Media: f,
|
||||
MediaType: "text/plain",
|
||||
ContentLength: st.Size(),
|
||||
Callback: tr.ProgressUpdate,
|
||||
}
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
cancelFunc() // stop the upload that hasn't started yet
|
||||
res, err := rx.Upload(ctx)
|
||||
if err == nil || res == nil || res.StatusCode != http.StatusRequestTimeout {
|
||||
if res == nil {
|
||||
t.Errorf("transferChunks not successful, got res=nil, err=%v, want StatusRequestTimeout", err)
|
||||
} else {
|
||||
t.Errorf("transferChunks not successful, got statusCode=%v, err=%v, want StatusRequestTimeout", res.StatusCode, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Godeps/_workspace/src/google.golang.org/api/googleapi/internal/uritemplates/LICENSE
generated
vendored
Normal file
18
Godeps/_workspace/src/google.golang.org/api/googleapi/internal/uritemplates/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Copyright (c) 2013 Joshua Tacoma
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
359
Godeps/_workspace/src/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go
generated
vendored
Normal file
359
Godeps/_workspace/src/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go
generated
vendored
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
// Copyright 2013 Joshua Tacoma. 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 is a level 4 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)
|
||||
//
|
||||
package uritemplates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
|
||||
reserved = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
|
||||
validname = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
|
||||
hex = []byte("0123456789ABCDEF")
|
||||
)
|
||||
|
||||
func pctEncode(src []byte) []byte {
|
||||
dst := make([]byte, len(src)*3)
|
||||
for i, b := range src {
|
||||
buf := dst[i*3 : i*3+3]
|
||||
buf[0] = 0x25
|
||||
buf[1] = hex[b/16]
|
||||
buf[2] = hex[b%16]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func escape(s string, allowReserved bool) (escaped string) {
|
||||
if allowReserved {
|
||||
escaped = string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
} else {
|
||||
escaped = string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
}
|
||||
return escaped
|
||||
}
|
||||
|
||||
// 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)
|
||||
for i, s := range split {
|
||||
if i == 0 {
|
||||
if strings.Contains(s, "}") {
|
||||
err = errors.New("unexpected }")
|
||||
break
|
||||
}
|
||||
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]
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
template = nil
|
||||
}
|
||||
return template, err
|
||||
}
|
||||
|
||||
type templatePart struct {
|
||||
raw string
|
||||
terms []templateTerm
|
||||
first string
|
||||
sep string
|
||||
named bool
|
||||
ifemp string
|
||||
allowReserved bool
|
||||
}
|
||||
|
||||
type templateTerm struct {
|
||||
name string
|
||||
explode bool
|
||||
truncate int
|
||||
}
|
||||
|
||||
func parseExpression(expression string) (result templatePart, err error) {
|
||||
switch expression[0] {
|
||||
case '+':
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
case '.':
|
||||
result.first = "."
|
||||
result.sep = "."
|
||||
expression = expression[1:]
|
||||
case '/':
|
||||
result.first = "/"
|
||||
result.sep = "/"
|
||||
expression = expression[1:]
|
||||
case ';':
|
||||
result.first = ";"
|
||||
result.sep = ";"
|
||||
result.named = true
|
||||
expression = expression[1:]
|
||||
case '?':
|
||||
result.first = "?"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '&':
|
||||
result.first = "&"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '#':
|
||||
result.first = "#"
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
default:
|
||||
result.sep = ","
|
||||
}
|
||||
rawterms := strings.Split(expression, ",")
|
||||
result.terms = make([]templateTerm, len(rawterms))
|
||||
for i, raw := range rawterms {
|
||||
result.terms[i], err = parseTerm(raw)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func parseTerm(term string) (result templateTerm, err error) {
|
||||
if strings.HasSuffix(term, "*") {
|
||||
result.explode = true
|
||||
term = term[:len(term)-1]
|
||||
}
|
||||
split := strings.Split(term, ":")
|
||||
if len(split) == 1 {
|
||||
result.name = term
|
||||
} else if len(split) == 2 {
|
||||
result.name = split[0]
|
||||
var parsed int64
|
||||
parsed, err = strconv.ParseInt(split[1], 10, 0)
|
||||
result.truncate = int(parsed)
|
||||
} else {
|
||||
err = errors.New("multiple colons in same term")
|
||||
}
|
||||
if !validname.MatchString(result.name) {
|
||||
err = errors.New("not a valid name: " + result.name)
|
||||
}
|
||||
if result.explode && result.truncate > 0 {
|
||||
err = errors.New("both explode and prefix modifers on same term")
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for _, p := range self.parts {
|
||||
err := p.expand(&buf, values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (self *templatePart) expand(buf *bytes.Buffer, values map[string]interface{}) error {
|
||||
if len(self.raw) > 0 {
|
||||
buf.WriteString(self.raw)
|
||||
return nil
|
||||
}
|
||||
var zeroLen = buf.Len()
|
||||
buf.WriteString(self.first)
|
||||
var firstLen = buf.Len()
|
||||
for _, term := range self.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 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 {
|
||||
buf.WriteString(name)
|
||||
if empty {
|
||||
buf.WriteString(self.ifemp)
|
||||
} else {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *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
|
||||
}
|
||||
13
Godeps/_workspace/src/google.golang.org/api/googleapi/internal/uritemplates/utils.go
generated
vendored
Normal file
13
Godeps/_workspace/src/google.golang.org/api/googleapi/internal/uritemplates/utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package uritemplates
|
||||
|
||||
func Expand(path string, expansions 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)
|
||||
}
|
||||
38
Godeps/_workspace/src/google.golang.org/api/googleapi/transport/apikey.go
generated
vendored
Normal file
38
Godeps/_workspace/src/google.golang.org/api/googleapi/transport/apikey.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2012 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package transport contains HTTP transports used to make
|
||||
// authenticated API requests.
|
||||
package transport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// APIKey is an HTTP Transport which wraps an underlying transport and
|
||||
// appends an API Key "key" parameter to the URL of outgoing requests.
|
||||
type APIKey struct {
|
||||
// Key is the API Key to set on requests.
|
||||
Key string
|
||||
|
||||
// Transport is the underlying HTTP transport.
|
||||
// If nil, http.DefaultTransport is used.
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *APIKey) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
rt := t.Transport
|
||||
if rt == nil {
|
||||
rt = http.DefaultTransport
|
||||
if rt == nil {
|
||||
return nil, errors.New("googleapi/transport: no Transport specified or available")
|
||||
}
|
||||
}
|
||||
newReq := *req
|
||||
args := newReq.URL.Query()
|
||||
args.Set("key", t.Key)
|
||||
newReq.URL.RawQuery = args.Encode()
|
||||
return rt.RoundTrip(&newReq)
|
||||
}
|
||||
182
Godeps/_workspace/src/google.golang.org/api/googleapi/types.go
generated
vendored
Normal file
182
Godeps/_workspace/src/google.golang.org/api/googleapi/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Int64s is a slice of int64s that marshal as quoted strings in JSON.
|
||||
type Int64s []int64
|
||||
|
||||
func (q *Int64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, int64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Int32s is a slice of int32s that marshal as quoted strings in JSON.
|
||||
type Int32s []int32
|
||||
|
||||
func (q *Int32s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, int32(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint64s is a slice of uint64s that marshal as quoted strings in JSON.
|
||||
type Uint64s []uint64
|
||||
|
||||
func (q *Uint64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, uint64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint32s is a slice of uint32s that marshal as quoted strings in JSON.
|
||||
type Uint32s []uint32
|
||||
|
||||
func (q *Uint32s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseUint(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, uint32(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float64s is a slice of float64s that marshal as quoted strings in JSON.
|
||||
type Float64s []float64
|
||||
|
||||
func (q *Float64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, float64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func quotedList(n int, fn func(dst []byte, i int) []byte) ([]byte, error) {
|
||||
dst := make([]byte, 0, 2+n*10) // somewhat arbitrary
|
||||
dst = append(dst, '[')
|
||||
for i := 0; i < n; i++ {
|
||||
if i > 0 {
|
||||
dst = append(dst, ',')
|
||||
}
|
||||
dst = append(dst, '"')
|
||||
dst = fn(dst, i)
|
||||
dst = append(dst, '"')
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func (s Int64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendInt(dst, s[i], 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Int32s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendInt(dst, int64(s[i]), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Uint64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendUint(dst, s[i], 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Uint32s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendUint(dst, uint64(s[i]), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Float64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendFloat(dst, s[i], 'g', -1, 64)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routines for simplifying the creation of optional fields of basic type.
|
||||
*/
|
||||
|
||||
// Bool is a helper routine that allocates a new bool value
|
||||
// to store v and returns a pointer to it.
|
||||
func Bool(v bool) *bool { return &v }
|
||||
|
||||
// Int32 is a helper routine that allocates a new int32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int32(v int32) *int32 { return &v }
|
||||
|
||||
// Int64 is a helper routine that allocates a new int64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int64(v int64) *int64 { return &v }
|
||||
|
||||
// Float64 is a helper routine that allocates a new float64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Float64(v float64) *float64 { return &v }
|
||||
|
||||
// Uint32 is a helper routine that allocates a new uint32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint32(v uint32) *uint32 { return &v }
|
||||
|
||||
// Uint64 is a helper routine that allocates a new uint64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint64(v uint64) *uint64 { return &v }
|
||||
|
||||
// String is a helper routine that allocates a new string value
|
||||
// to store v and returns a pointer to it.
|
||||
func String(v string) *string { return &v }
|
||||
44
Godeps/_workspace/src/google.golang.org/api/googleapi/types_test.go
generated
vendored
Normal file
44
Godeps/_workspace/src/google.golang.org/api/googleapi/types_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTypes(t *testing.T) {
|
||||
type T struct {
|
||||
I32 Int32s
|
||||
I64 Int64s
|
||||
U32 Uint32s
|
||||
U64 Uint64s
|
||||
F64 Float64s
|
||||
}
|
||||
v := &T{
|
||||
I32: Int32s{-1, 2, 3},
|
||||
I64: Int64s{-1, 2, 1 << 33},
|
||||
U32: Uint32s{1, 2},
|
||||
U64: Uint64s{1, 2, 1 << 33},
|
||||
F64: Float64s{1.5, 3.33},
|
||||
}
|
||||
got, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := `{"I32":["-1","2","3"],"I64":["-1","2","8589934592"],"U32":["1","2"],"U64":["1","2","8589934592"],"F64":["1.5","3.33"]}`
|
||||
if string(got) != want {
|
||||
t.Fatalf("Marshal mismatch.\n got: %s\nwant: %s\n", got, want)
|
||||
}
|
||||
|
||||
v2 := new(T)
|
||||
if err := json.Unmarshal(got, v2); err != nil {
|
||||
t.Fatalf("Unmarshal: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(v, v2) {
|
||||
t.Fatalf("Unmarshal didn't produce same results.\n got: %#v\nwant: %#v\n", v, v2)
|
||||
}
|
||||
}
|
||||
37
Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/go13.go
generated
vendored
Normal file
37
Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/go13.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build go1.3
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This is a workaround for https://github.com/golang/oauth2/issues/70, where
|
||||
// net.Dialer.KeepAlive is unavailable on Go 1.2 (which App Engine as of
|
||||
// Jan 2015 still runs).
|
||||
//
|
||||
// TODO(bradfitz,jbd,adg): remove this once App Engine supports Go
|
||||
// 1.3+.
|
||||
func init() {
|
||||
go13Dialer = func() *net.Dialer {
|
||||
return &net.Dialer{
|
||||
Timeout: 750 * time.Millisecond,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
}
|
||||
267
Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/metadata.go
generated
vendored
Normal file
267
Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/metadata.go
generated
vendored
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
// Copyright 2014 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 metadata provides access to Google Compute Engine (GCE)
|
||||
// metadata and API service accounts.
|
||||
//
|
||||
// This package is a wrapper around the GCE metadata service,
|
||||
// as documented at https://developers.google.com/compute/docs/metadata.
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/cloud/internal"
|
||||
)
|
||||
|
||||
type cachedValue struct {
|
||||
k string
|
||||
trim bool
|
||||
mu sync.Mutex
|
||||
v string
|
||||
}
|
||||
|
||||
var (
|
||||
projID = &cachedValue{k: "project/project-id", trim: true}
|
||||
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
|
||||
instID = &cachedValue{k: "instance/id", trim: true}
|
||||
)
|
||||
|
||||
var metaClient = &http.Client{
|
||||
Transport: &internal.Transport{
|
||||
Base: &http.Transport{
|
||||
Dial: dialer().Dial,
|
||||
ResponseHeaderTimeout: 750 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// go13Dialer is nil until we're using Go 1.3+.
|
||||
// This is a workaround for https://github.com/golang/oauth2/issues/70, where
|
||||
// net.Dialer.KeepAlive is unavailable on Go 1.2 (which App Engine as of
|
||||
// Jan 2015 still runs).
|
||||
//
|
||||
// TODO(bradfitz,jbd,adg,dsymonds): remove this once App Engine supports Go
|
||||
// 1.3+ and go-app-builder also supports 1.3+, or when Go 1.2 is no longer an
|
||||
// option on App Engine.
|
||||
var go13Dialer func() *net.Dialer
|
||||
|
||||
func dialer() *net.Dialer {
|
||||
if fn := go13Dialer; fn != nil {
|
||||
return fn()
|
||||
}
|
||||
return &net.Dialer{
|
||||
Timeout: 750 * time.Millisecond,
|
||||
}
|
||||
}
|
||||
|
||||
// NotDefinedError is returned when requested metadata is not defined.
|
||||
//
|
||||
// The underlying string is the suffix after "/computeMetadata/v1/".
|
||||
//
|
||||
// This error is not returned if the value is defined to be the empty
|
||||
// string.
|
||||
type NotDefinedError string
|
||||
|
||||
func (suffix NotDefinedError) Error() string {
|
||||
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
|
||||
}
|
||||
|
||||
// Get returns a value from the metadata service.
|
||||
// The suffix is appended to "http://metadata/computeMetadata/v1/".
|
||||
//
|
||||
// If the requested metadata is not defined, the returned error will
|
||||
// be of type NotDefinedError.
|
||||
func Get(suffix string) (string, error) {
|
||||
// Using 169.254.169.254 instead of "metadata" here because Go
|
||||
// binaries built with the "netgo" tag and without cgo won't
|
||||
// know the search suffix for "metadata" is
|
||||
// ".google.internal", and this IP address is documented as
|
||||
// being stable anyway.
|
||||
url := "http://169.254.169.254/computeMetadata/v1/" + suffix
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Metadata-Flavor", "Google")
|
||||
res, err := metaClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode == http.StatusNotFound {
|
||||
return "", NotDefinedError(suffix)
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
return "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
|
||||
}
|
||||
all, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(all), nil
|
||||
}
|
||||
|
||||
func getTrimmed(suffix string) (s string, err error) {
|
||||
s, err = Get(suffix)
|
||||
s = strings.TrimSpace(s)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cachedValue) get() (v string, err error) {
|
||||
defer c.mu.Unlock()
|
||||
c.mu.Lock()
|
||||
if c.v != "" {
|
||||
return c.v, nil
|
||||
}
|
||||
if c.trim {
|
||||
v, err = getTrimmed(c.k)
|
||||
} else {
|
||||
v, err = Get(c.k)
|
||||
}
|
||||
if err == nil {
|
||||
c.v = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var onGCE struct {
|
||||
sync.Mutex
|
||||
set bool
|
||||
v bool
|
||||
}
|
||||
|
||||
// OnGCE reports whether this process is running on Google Compute Engine.
|
||||
func OnGCE() bool {
|
||||
defer onGCE.Unlock()
|
||||
onGCE.Lock()
|
||||
if onGCE.set {
|
||||
return onGCE.v
|
||||
}
|
||||
onGCE.set = true
|
||||
|
||||
// We use the DNS name of the metadata service here instead of the IP address
|
||||
// because we expect that to fail faster in the not-on-GCE case.
|
||||
res, err := metaClient.Get("http://metadata.google.internal")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
onGCE.v = res.Header.Get("Metadata-Flavor") == "Google"
|
||||
return onGCE.v
|
||||
}
|
||||
|
||||
// ProjectID returns the current instance's project ID string.
|
||||
func ProjectID() (string, error) { return projID.get() }
|
||||
|
||||
// NumericProjectID returns the current instance's numeric project ID.
|
||||
func NumericProjectID() (string, error) { return projNum.get() }
|
||||
|
||||
// InternalIP returns the instance's primary internal IP address.
|
||||
func InternalIP() (string, error) {
|
||||
return getTrimmed("instance/network-interfaces/0/ip")
|
||||
}
|
||||
|
||||
// ExternalIP returns the instance's primary external (public) IP address.
|
||||
func ExternalIP() (string, error) {
|
||||
return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
|
||||
}
|
||||
|
||||
// Hostname returns the instance's hostname. This will probably be of
|
||||
// the form "INSTANCENAME.c.PROJECT.internal" but that isn't
|
||||
// guaranteed.
|
||||
//
|
||||
// TODO: what is this defined to be? Docs say "The host name of the
|
||||
// instance."
|
||||
func Hostname() (string, error) {
|
||||
return getTrimmed("network-interfaces/0/ip")
|
||||
}
|
||||
|
||||
// InstanceTags returns the list of user-defined instance tags,
|
||||
// assigned when initially creating a GCE instance.
|
||||
func InstanceTags() ([]string, error) {
|
||||
var s []string
|
||||
j, err := Get("instance/tags")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// InstanceID returns the current VM's numeric instance ID.
|
||||
func InstanceID() (string, error) {
|
||||
return instID.get()
|
||||
}
|
||||
|
||||
// InstanceAttributes returns the list of user-defined attributes,
|
||||
// assigned when initially creating a GCE VM instance. The value of an
|
||||
// attribute can be obtained with InstanceAttributeValue.
|
||||
func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }
|
||||
|
||||
// ProjectAttributes returns the list of user-defined attributes
|
||||
// applying to the project as a whole, not just this VM. The value of
|
||||
// an attribute can be obtained with ProjectAttributeValue.
|
||||
func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }
|
||||
|
||||
func lines(suffix string) ([]string, error) {
|
||||
j, err := Get(suffix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := strings.Split(strings.TrimSpace(j), "\n")
|
||||
for i := range s {
|
||||
s[i] = strings.TrimSpace(s[i])
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// InstanceAttributeValue returns the value of the provided VM
|
||||
// instance attribute.
|
||||
//
|
||||
// If the requested attribute is not defined, the returned error will
|
||||
// be of type NotDefinedError.
|
||||
//
|
||||
// InstanceAttributeValue may return ("", nil) if the attribute was
|
||||
// defined to be the empty string.
|
||||
func InstanceAttributeValue(attr string) (string, error) {
|
||||
return Get("instance/attributes/" + attr)
|
||||
}
|
||||
|
||||
// ProjectAttributeValue returns the value of the provided
|
||||
// project attribute.
|
||||
//
|
||||
// If the requested attribute is not defined, the returned error will
|
||||
// be of type NotDefinedError.
|
||||
//
|
||||
// ProjectAttributeValue may return ("", nil) if the attribute was
|
||||
// defined to be the empty string.
|
||||
func ProjectAttributeValue(attr string) (string, error) {
|
||||
return Get("project/attributes/" + attr)
|
||||
}
|
||||
|
||||
// Scopes returns the service account scopes for the given account.
|
||||
// The account may be empty or the string "default" to use the instance's
|
||||
// main account.
|
||||
func Scopes(serviceAccount string) ([]string, error) {
|
||||
if serviceAccount == "" {
|
||||
serviceAccount = "default"
|
||||
}
|
||||
return lines("instance/service-accounts/" + serviceAccount + "/scopes")
|
||||
}
|
||||
128
Godeps/_workspace/src/google.golang.org/cloud/internal/cloud.go
generated
vendored
Normal file
128
Godeps/_workspace/src/google.golang.org/cloud/internal/cloud.go
generated
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2014 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 internal provides support for the cloud packages.
|
||||
//
|
||||
// Users should not import this package directly.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
func WithContext(parent context.Context, projID string, c *http.Client) context.Context {
|
||||
if c == nil {
|
||||
panic("nil *http.Client passed to WithContext")
|
||||
}
|
||||
if projID == "" {
|
||||
panic("empty project ID passed to WithContext")
|
||||
}
|
||||
return context.WithValue(parent, contextKey{}, &cloudContext{
|
||||
ProjectID: projID,
|
||||
HTTPClient: c,
|
||||
})
|
||||
}
|
||||
|
||||
const userAgent = "gcloud-golang/0.1"
|
||||
|
||||
type cloudContext struct {
|
||||
ProjectID string
|
||||
HTTPClient *http.Client
|
||||
|
||||
mu sync.Mutex // guards svc
|
||||
svc map[string]interface{} // e.g. "storage" => *rawStorage.Service
|
||||
}
|
||||
|
||||
// Service returns the result of the fill function if it's never been
|
||||
// called before for the given name (which is assumed to be an API
|
||||
// service name, like "datastore"). If it has already been cached, the fill
|
||||
// func is not run.
|
||||
// It's safe for concurrent use by multiple goroutines.
|
||||
func Service(ctx context.Context, name string, fill func(*http.Client) interface{}) interface{} {
|
||||
return cc(ctx).service(name, fill)
|
||||
}
|
||||
|
||||
func (c *cloudContext) service(name string, fill func(*http.Client) interface{}) interface{} {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.svc == nil {
|
||||
c.svc = make(map[string]interface{})
|
||||
} else if v, ok := c.svc[name]; ok {
|
||||
return v
|
||||
}
|
||||
v := fill(c.HTTPClient)
|
||||
c.svc[name] = v
|
||||
return v
|
||||
}
|
||||
|
||||
// Transport is an http.RoundTripper that appends
|
||||
// Google Cloud client's user-agent to the original
|
||||
// request's user-agent header.
|
||||
type Transport struct {
|
||||
// Base represents the actual http.RoundTripper
|
||||
// the requests will be delegated to.
|
||||
Base http.RoundTripper
|
||||
}
|
||||
|
||||
// RoundTrip appends a user-agent to the existing user-agent
|
||||
// header and delegates the request to the base http.RoundTripper.
|
||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req = cloneRequest(req)
|
||||
ua := req.Header.Get("User-Agent")
|
||||
if ua == "" {
|
||||
ua = userAgent
|
||||
} else {
|
||||
ua = fmt.Sprintf("%s;%s", ua, userAgent)
|
||||
}
|
||||
req.Header.Set("User-Agent", ua)
|
||||
return t.Base.RoundTrip(req)
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map.
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
// shallow copy of the struct
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
// deep copy of the Header
|
||||
r2.Header = make(http.Header)
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = s
|
||||
}
|
||||
return r2
|
||||
}
|
||||
|
||||
func ProjID(ctx context.Context) string {
|
||||
return cc(ctx).ProjectID
|
||||
}
|
||||
|
||||
func HTTPClient(ctx context.Context) *http.Client {
|
||||
return cc(ctx).HTTPClient
|
||||
}
|
||||
|
||||
// cc returns the internal *cloudContext (cc) state for a context.Context.
|
||||
// It panics if the user did it wrong.
|
||||
func cc(ctx context.Context) *cloudContext {
|
||||
if c, ok := ctx.Value(contextKey{}).(*cloudContext); ok {
|
||||
return c
|
||||
}
|
||||
panic("invalid context.Context type; it should be created with cloud.NewContext")
|
||||
}
|
||||
1633
Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go
generated
vendored
Normal file
1633
Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
594
Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto
generated
vendored
Normal file
594
Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto
generated
vendored
Normal file
|
|
@ -0,0 +1,594 @@
|
|||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// The datastore v1 service proto definitions
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package pb;
|
||||
option java_package = "com.google.api.services.datastore";
|
||||
|
||||
|
||||
// An identifier for a particular subset of entities.
|
||||
//
|
||||
// Entities are partitioned into various subsets, each used by different
|
||||
// datasets and different namespaces within a dataset and so forth.
|
||||
//
|
||||
// All input partition IDs are normalized before use.
|
||||
// A partition ID is normalized as follows:
|
||||
// If the partition ID is unset or is set to an empty partition ID, replace it
|
||||
// with the context partition ID.
|
||||
// Otherwise, if the partition ID has no dataset ID, assign it the context
|
||||
// partition ID's dataset ID.
|
||||
// Unless otherwise documented, the context partition ID has the dataset ID set
|
||||
// to the context dataset ID and no other partition dimension set.
|
||||
//
|
||||
// A partition ID is empty if all of its fields are unset.
|
||||
//
|
||||
// Partition dimension:
|
||||
// A dimension may be unset.
|
||||
// A dimension's value must never be "".
|
||||
// A dimension's value must match [A-Za-z\d\.\-_]{1,100}
|
||||
// If the value of any dimension matches regex "__.*__",
|
||||
// the partition is reserved/read-only.
|
||||
// A reserved/read-only partition ID is forbidden in certain documented contexts.
|
||||
//
|
||||
// Dataset ID:
|
||||
// A dataset id's value must never be "".
|
||||
// A dataset id's value must match
|
||||
// ([a-z\d\-]{1,100}~)?([a-z\d][a-z\d\-\.]{0,99}:)?([a-z\d][a-z\d\-]{0,99}
|
||||
message PartitionId {
|
||||
// The dataset ID.
|
||||
optional string dataset_id = 3;
|
||||
// The namespace.
|
||||
optional string namespace = 4;
|
||||
}
|
||||
|
||||
// A unique identifier for an entity.
|
||||
// If a key's partition id or any of its path kinds or names are
|
||||
// reserved/read-only, the key is reserved/read-only.
|
||||
// A reserved/read-only key is forbidden in certain documented contexts.
|
||||
message Key {
|
||||
// Entities are partitioned into subsets, currently identified by a dataset
|
||||
// (usually implicitly specified by the project) and namespace ID.
|
||||
// Queries are scoped to a single partition.
|
||||
optional PartitionId partition_id = 1;
|
||||
|
||||
// A (kind, ID/name) pair used to construct a key path.
|
||||
//
|
||||
// At most one of name or ID may be set.
|
||||
// If either is set, the element is complete.
|
||||
// If neither is set, the element is incomplete.
|
||||
message PathElement {
|
||||
// The kind of the entity.
|
||||
// A kind matching regex "__.*__" is reserved/read-only.
|
||||
// A kind must not contain more than 500 characters.
|
||||
// Cannot be "".
|
||||
required string kind = 1;
|
||||
// The ID of the entity.
|
||||
// Never equal to zero. Values less than zero are discouraged and will not
|
||||
// be supported in the future.
|
||||
optional int64 id = 2;
|
||||
// The name of the entity.
|
||||
// A name matching regex "__.*__" is reserved/read-only.
|
||||
// A name must not be more than 500 characters.
|
||||
// Cannot be "".
|
||||
optional string name = 3;
|
||||
}
|
||||
|
||||
// The entity path.
|
||||
// An entity path consists of one or more elements composed of a kind and a
|
||||
// string or numerical identifier, which identify entities. The first
|
||||
// element identifies a <em>root entity</em>, the second element identifies
|
||||
// a <em>child</em> of the root entity, the third element a child of the
|
||||
// second entity, and so forth. The entities identified by all prefixes of
|
||||
// the path are called the element's <em>ancestors</em>.
|
||||
// An entity path is always fully complete: ALL of the entity's ancestors
|
||||
// are required to be in the path along with the entity identifier itself.
|
||||
// The only exception is that in some documented cases, the identifier in the
|
||||
// last path element (for the entity) itself may be omitted. A path can never
|
||||
// be empty.
|
||||
repeated PathElement path_element = 2;
|
||||
}
|
||||
|
||||
// A message that can hold any of the supported value types and associated
|
||||
// metadata.
|
||||
//
|
||||
// At most one of the <type>Value fields may be set.
|
||||
// If none are set the value is "null".
|
||||
//
|
||||
message Value {
|
||||
// A boolean value.
|
||||
optional bool boolean_value = 1;
|
||||
// An integer value.
|
||||
optional int64 integer_value = 2;
|
||||
// A double value.
|
||||
optional double double_value = 3;
|
||||
// A timestamp value.
|
||||
optional int64 timestamp_microseconds_value = 4;
|
||||
// A key value.
|
||||
optional Key key_value = 5;
|
||||
// A blob key value.
|
||||
optional string blob_key_value = 16;
|
||||
// A UTF-8 encoded string value.
|
||||
optional string string_value = 17;
|
||||
// A blob value.
|
||||
optional bytes blob_value = 18;
|
||||
// An entity value.
|
||||
// May have no key.
|
||||
// May have a key with an incomplete key path.
|
||||
// May have a reserved/read-only key.
|
||||
optional Entity entity_value = 6;
|
||||
// A list value.
|
||||
// Cannot contain another list value.
|
||||
// Cannot also have a meaning and indexing set.
|
||||
repeated Value list_value = 7;
|
||||
|
||||
// The <code>meaning</code> field is reserved and should not be used.
|
||||
optional int32 meaning = 14;
|
||||
|
||||
// If the value should be indexed.
|
||||
//
|
||||
// The <code>indexed</code> property may be set for a
|
||||
// <code>null</code> value.
|
||||
// When <code>indexed</code> is <code>true</code>, <code>stringValue</code>
|
||||
// is limited to 500 characters and the blob value is limited to 500 bytes.
|
||||
// Exception: If meaning is set to 2, string_value is limited to 2038
|
||||
// characters regardless of indexed.
|
||||
// When indexed is true, meaning 15 and 22 are not allowed, and meaning 16
|
||||
// will be ignored on input (and will never be set on output).
|
||||
// Input values by default have <code>indexed</code> set to
|
||||
// <code>true</code>; however, you can explicitly set <code>indexed</code> to
|
||||
// <code>true</code> if you want. (An output value never has
|
||||
// <code>indexed</code> explicitly set to <code>true</code>.) If a value is
|
||||
// itself an entity, it cannot have <code>indexed</code> set to
|
||||
// <code>true</code>.
|
||||
// Exception: An entity value with meaning 9, 20 or 21 may be indexed.
|
||||
optional bool indexed = 15 [default = true];
|
||||
}
|
||||
|
||||
// An entity property.
|
||||
message Property {
|
||||
// The name of the property.
|
||||
// A property name matching regex "__.*__" is reserved.
|
||||
// A reserved property name is forbidden in certain documented contexts.
|
||||
// The name must not contain more than 500 characters.
|
||||
// Cannot be "".
|
||||
required string name = 1;
|
||||
|
||||
// The value(s) of the property.
|
||||
// Each value can have only one value property populated. For example,
|
||||
// you cannot have a values list of <code>{ value: { integerValue: 22,
|
||||
// stringValue: "a" } }</code>, but you can have <code>{ value: { listValue:
|
||||
// [ { integerValue: 22 }, { stringValue: "a" } ] }</code>.
|
||||
required Value value = 4;
|
||||
}
|
||||
|
||||
// An entity.
|
||||
//
|
||||
// An entity is limited to 1 megabyte when stored. That <em>roughly</em>
|
||||
// corresponds to a limit of 1 megabyte for the serialized form of this
|
||||
// message.
|
||||
message Entity {
|
||||
// The entity's key.
|
||||
//
|
||||
// An entity must have a key, unless otherwise documented (for example,
|
||||
// an entity in <code>Value.entityValue</code> may have no key).
|
||||
// An entity's kind is its key's path's last element's kind,
|
||||
// or null if it has no key.
|
||||
optional Key key = 1;
|
||||
// The entity's properties.
|
||||
// Each property's name must be unique for its entity.
|
||||
repeated Property property = 2;
|
||||
}
|
||||
|
||||
// The result of fetching an entity from the datastore.
|
||||
message EntityResult {
|
||||
// Specifies what data the 'entity' field contains.
|
||||
// A ResultType is either implied (for example, in LookupResponse.found it
|
||||
// is always FULL) or specified by context (for example, in message
|
||||
// QueryResultBatch, field 'entity_result_type' specifies a ResultType
|
||||
// for all the values in field 'entity_result').
|
||||
enum ResultType {
|
||||
FULL = 1; // The entire entity.
|
||||
PROJECTION = 2; // A projected subset of properties.
|
||||
// The entity may have no key.
|
||||
// A property value may have meaning 18.
|
||||
KEY_ONLY = 3; // Only the key.
|
||||
}
|
||||
|
||||
// The resulting entity.
|
||||
required Entity entity = 1;
|
||||
}
|
||||
|
||||
// A query.
|
||||
message Query {
|
||||
// The projection to return. If not set the entire entity is returned.
|
||||
repeated PropertyExpression projection = 2;
|
||||
|
||||
// The kinds to query (if empty, returns entities from all kinds).
|
||||
repeated KindExpression kind = 3;
|
||||
|
||||
// The filter to apply (optional).
|
||||
optional Filter filter = 4;
|
||||
|
||||
// The order to apply to the query results (if empty, order is unspecified).
|
||||
repeated PropertyOrder order = 5;
|
||||
|
||||
// The properties to group by (if empty, no grouping is applied to the
|
||||
// result set).
|
||||
repeated PropertyReference group_by = 6;
|
||||
|
||||
// A starting point for the query results. Optional. Query cursors are
|
||||
// returned in query result batches.
|
||||
optional bytes /* serialized QueryCursor */ start_cursor = 7;
|
||||
|
||||
// An ending point for the query results. Optional. Query cursors are
|
||||
// returned in query result batches.
|
||||
optional bytes /* serialized QueryCursor */ end_cursor = 8;
|
||||
|
||||
// The number of results to skip. Applies before limit, but after all other
|
||||
// constraints (optional, defaults to 0).
|
||||
optional int32 offset = 10 [default=0];
|
||||
|
||||
// The maximum number of results to return. Applies after all other
|
||||
// constraints. Optional.
|
||||
optional int32 limit = 11;
|
||||
}
|
||||
|
||||
// A representation of a kind.
|
||||
message KindExpression {
|
||||
// The name of the kind.
|
||||
required string name = 1;
|
||||
}
|
||||
|
||||
// A reference to a property relative to the kind expressions.
|
||||
// exactly.
|
||||
message PropertyReference {
|
||||
// The name of the property.
|
||||
required string name = 2;
|
||||
}
|
||||
|
||||
// A representation of a property in a projection.
|
||||
message PropertyExpression {
|
||||
enum AggregationFunction {
|
||||
FIRST = 1;
|
||||
}
|
||||
// The property to project.
|
||||
required PropertyReference property = 1;
|
||||
// The aggregation function to apply to the property. Optional.
|
||||
// Can only be used when grouping by at least one property. Must
|
||||
// then be set on all properties in the projection that are not
|
||||
// being grouped by.
|
||||
optional AggregationFunction aggregation_function = 2;
|
||||
}
|
||||
|
||||
// The desired order for a specific property.
|
||||
message PropertyOrder {
|
||||
enum Direction {
|
||||
ASCENDING = 1;
|
||||
DESCENDING = 2;
|
||||
}
|
||||
// The property to order by.
|
||||
required PropertyReference property = 1;
|
||||
// The direction to order by.
|
||||
optional Direction direction = 2 [default=ASCENDING];
|
||||
}
|
||||
|
||||
// A holder for any type of filter. Exactly one field should be specified.
|
||||
message Filter {
|
||||
// A composite filter.
|
||||
optional CompositeFilter composite_filter = 1;
|
||||
// A filter on a property.
|
||||
optional PropertyFilter property_filter = 2;
|
||||
}
|
||||
|
||||
// A filter that merges the multiple other filters using the given operation.
|
||||
message CompositeFilter {
|
||||
enum Operator {
|
||||
AND = 1;
|
||||
}
|
||||
|
||||
// The operator for combining multiple filters.
|
||||
required Operator operator = 1;
|
||||
// The list of filters to combine.
|
||||
// Must contain at least one filter.
|
||||
repeated Filter filter = 2;
|
||||
}
|
||||
|
||||
// A filter on a specific property.
|
||||
message PropertyFilter {
|
||||
enum Operator {
|
||||
LESS_THAN = 1;
|
||||
LESS_THAN_OR_EQUAL = 2;
|
||||
GREATER_THAN = 3;
|
||||
GREATER_THAN_OR_EQUAL = 4;
|
||||
EQUAL = 5;
|
||||
|
||||
HAS_ANCESTOR = 11;
|
||||
}
|
||||
|
||||
// The property to filter by.
|
||||
required PropertyReference property = 1;
|
||||
// The operator to filter by.
|
||||
required Operator operator = 2;
|
||||
// The value to compare the property to.
|
||||
required Value value = 3;
|
||||
}
|
||||
|
||||
// A GQL query.
|
||||
message GqlQuery {
|
||||
required string query_string = 1;
|
||||
// When false, the query string must not contain a literal.
|
||||
optional bool allow_literal = 2 [default = false];
|
||||
// A named argument must set field GqlQueryArg.name.
|
||||
// No two named arguments may have the same name.
|
||||
// For each non-reserved named binding site in the query string,
|
||||
// there must be a named argument with that name,
|
||||
// but not necessarily the inverse.
|
||||
repeated GqlQueryArg name_arg = 3;
|
||||
// Numbered binding site @1 references the first numbered argument,
|
||||
// effectively using 1-based indexing, rather than the usual 0.
|
||||
// A numbered argument must NOT set field GqlQueryArg.name.
|
||||
// For each binding site numbered i in query_string,
|
||||
// there must be an ith numbered argument.
|
||||
// The inverse must also be true.
|
||||
repeated GqlQueryArg number_arg = 4;
|
||||
}
|
||||
|
||||
// A binding argument for a GQL query.
|
||||
// Exactly one of fields value and cursor must be set.
|
||||
message GqlQueryArg {
|
||||
// Must match regex "[A-Za-z_$][A-Za-z_$0-9]*".
|
||||
// Must not match regex "__.*__".
|
||||
// Must not be "".
|
||||
optional string name = 1;
|
||||
optional Value value = 2;
|
||||
optional bytes cursor = 3;
|
||||
}
|
||||
|
||||
// A batch of results produced by a query.
|
||||
message QueryResultBatch {
|
||||
// The possible values for the 'more_results' field.
|
||||
enum MoreResultsType {
|
||||
NOT_FINISHED = 1; // There are additional batches to fetch from this query.
|
||||
MORE_RESULTS_AFTER_LIMIT = 2; // The query is finished, but there are more
|
||||
// results after the limit.
|
||||
NO_MORE_RESULTS = 3; // The query has been exhausted.
|
||||
}
|
||||
|
||||
// The result type for every entity in entityResults.
|
||||
required EntityResult.ResultType entity_result_type = 1;
|
||||
// The results for this batch.
|
||||
repeated EntityResult entity_result = 2;
|
||||
|
||||
// A cursor that points to the position after the last result in the batch.
|
||||
// May be absent.
|
||||
optional bytes /* serialized QueryCursor */ end_cursor = 4;
|
||||
|
||||
// The state of the query after the current batch.
|
||||
required MoreResultsType more_results = 5;
|
||||
|
||||
// The number of results skipped because of <code>Query.offset</code>.
|
||||
optional int32 skipped_results = 6;
|
||||
}
|
||||
|
||||
// A set of changes to apply.
|
||||
//
|
||||
// No entity in this message may have a reserved property name,
|
||||
// not even a property in an entity in a value.
|
||||
// No value in this message may have meaning 18,
|
||||
// not even a value in an entity in another value.
|
||||
//
|
||||
// If entities with duplicate keys are present, an arbitrary choice will
|
||||
// be made as to which is written.
|
||||
message Mutation {
|
||||
// Entities to upsert.
|
||||
// Each upserted entity's key must have a complete path and
|
||||
// must not be reserved/read-only.
|
||||
repeated Entity upsert = 1;
|
||||
// Entities to update.
|
||||
// Each updated entity's key must have a complete path and
|
||||
// must not be reserved/read-only.
|
||||
repeated Entity update = 2;
|
||||
// Entities to insert.
|
||||
// Each inserted entity's key must have a complete path and
|
||||
// must not be reserved/read-only.
|
||||
repeated Entity insert = 3;
|
||||
// Insert entities with a newly allocated ID.
|
||||
// Each inserted entity's key must omit the final identifier in its path and
|
||||
// must not be reserved/read-only.
|
||||
repeated Entity insert_auto_id = 4;
|
||||
// Keys of entities to delete.
|
||||
// Each key must have a complete key path and must not be reserved/read-only.
|
||||
repeated Key delete = 5;
|
||||
// Ignore a user specified read-only period. Optional.
|
||||
optional bool force = 6;
|
||||
}
|
||||
|
||||
// The result of applying a mutation.
|
||||
message MutationResult {
|
||||
// Number of index writes.
|
||||
required int32 index_updates = 1;
|
||||
// Keys for <code>insertAutoId</code> entities. One per entity from the
|
||||
// request, in the same order.
|
||||
repeated Key insert_auto_id_key = 2;
|
||||
}
|
||||
|
||||
// Options shared by read requests.
|
||||
message ReadOptions {
|
||||
enum ReadConsistency {
|
||||
DEFAULT = 0;
|
||||
STRONG = 1;
|
||||
EVENTUAL = 2;
|
||||
}
|
||||
|
||||
// The read consistency to use.
|
||||
// Cannot be set when transaction is set.
|
||||
// Lookup and ancestor queries default to STRONG, global queries default to
|
||||
// EVENTUAL and cannot be set to STRONG.
|
||||
optional ReadConsistency read_consistency = 1 [default=DEFAULT];
|
||||
|
||||
// The transaction to use. Optional.
|
||||
optional bytes /* serialized Transaction */ transaction = 2;
|
||||
}
|
||||
|
||||
// The request for Lookup.
|
||||
message LookupRequest {
|
||||
|
||||
// Options for this lookup request. Optional.
|
||||
optional ReadOptions read_options = 1;
|
||||
// Keys of entities to look up from the datastore.
|
||||
repeated Key key = 3;
|
||||
}
|
||||
|
||||
// The response for Lookup.
|
||||
message LookupResponse {
|
||||
|
||||
// The order of results in these fields is undefined and has no relation to
|
||||
// the order of the keys in the input.
|
||||
|
||||
// Entities found as ResultType.FULL entities.
|
||||
repeated EntityResult found = 1;
|
||||
|
||||
// Entities not found as ResultType.KEY_ONLY entities.
|
||||
repeated EntityResult missing = 2;
|
||||
|
||||
// A list of keys that were not looked up due to resource constraints.
|
||||
repeated Key deferred = 3;
|
||||
}
|
||||
|
||||
|
||||
// The request for RunQuery.
|
||||
message RunQueryRequest {
|
||||
|
||||
// The options for this query.
|
||||
optional ReadOptions read_options = 1;
|
||||
|
||||
// Entities are partitioned into subsets, identified by a dataset (usually
|
||||
// implicitly specified by the project) and namespace ID. Queries are scoped
|
||||
// to a single partition.
|
||||
// This partition ID is normalized with the standard default context
|
||||
// partition ID, but all other partition IDs in RunQueryRequest are
|
||||
// normalized with this partition ID as the context partition ID.
|
||||
optional PartitionId partition_id = 2;
|
||||
|
||||
// The query to run.
|
||||
// Either this field or field gql_query must be set, but not both.
|
||||
optional Query query = 3;
|
||||
// The GQL query to run.
|
||||
// Either this field or field query must be set, but not both.
|
||||
optional GqlQuery gql_query = 7;
|
||||
}
|
||||
|
||||
// The response for RunQuery.
|
||||
message RunQueryResponse {
|
||||
|
||||
// A batch of query results (always present).
|
||||
optional QueryResultBatch batch = 1;
|
||||
|
||||
}
|
||||
|
||||
// The request for BeginTransaction.
|
||||
message BeginTransactionRequest {
|
||||
|
||||
enum IsolationLevel {
|
||||
SNAPSHOT = 0; // Read from a consistent snapshot. Concurrent transactions
|
||||
// conflict if their mutations conflict. For example:
|
||||
// Read(A),Write(B) may not conflict with Read(B),Write(A),
|
||||
// but Read(B),Write(B) does conflict with Read(B),Write(B).
|
||||
SERIALIZABLE = 1; // Read from a consistent snapshot. Concurrent
|
||||
// transactions conflict if they cannot be serialized.
|
||||
// For example Read(A),Write(B) does conflict with
|
||||
// Read(B),Write(A) but Read(A) may not conflict with
|
||||
// Write(A).
|
||||
}
|
||||
|
||||
// The transaction isolation level.
|
||||
optional IsolationLevel isolation_level = 1 [default=SNAPSHOT];
|
||||
}
|
||||
|
||||
// The response for BeginTransaction.
|
||||
message BeginTransactionResponse {
|
||||
|
||||
// The transaction identifier (always present).
|
||||
optional bytes /* serialized Transaction */ transaction = 1;
|
||||
}
|
||||
|
||||
// The request for Rollback.
|
||||
message RollbackRequest {
|
||||
|
||||
// The transaction identifier, returned by a call to
|
||||
// <code>beginTransaction</code>.
|
||||
required bytes /* serialized Transaction */ transaction = 1;
|
||||
}
|
||||
|
||||
// The response for Rollback.
|
||||
message RollbackResponse {
|
||||
// Empty
|
||||
}
|
||||
|
||||
// The request for Commit.
|
||||
message CommitRequest {
|
||||
|
||||
enum Mode {
|
||||
TRANSACTIONAL = 1;
|
||||
NON_TRANSACTIONAL = 2;
|
||||
}
|
||||
|
||||
// The transaction identifier, returned by a call to
|
||||
// <code>beginTransaction</code>. Must be set when mode is TRANSACTIONAL.
|
||||
optional bytes /* serialized Transaction */ transaction = 1;
|
||||
// The mutation to perform. Optional.
|
||||
optional Mutation mutation = 2;
|
||||
// The type of commit to perform. Either TRANSACTIONAL or NON_TRANSACTIONAL.
|
||||
optional Mode mode = 5 [default=TRANSACTIONAL];
|
||||
}
|
||||
|
||||
// The response for Commit.
|
||||
message CommitResponse {
|
||||
|
||||
// The result of performing the mutation (if any).
|
||||
optional MutationResult mutation_result = 1;
|
||||
}
|
||||
|
||||
// The request for AllocateIds.
|
||||
message AllocateIdsRequest {
|
||||
|
||||
// A list of keys with incomplete key paths to allocate IDs for.
|
||||
// No key may be reserved/read-only.
|
||||
repeated Key key = 1;
|
||||
}
|
||||
|
||||
// The response for AllocateIds.
|
||||
message AllocateIdsResponse {
|
||||
|
||||
// The keys specified in the request (in the same order), each with
|
||||
// its key path completed with a newly allocated ID.
|
||||
repeated Key key = 1;
|
||||
}
|
||||
|
||||
// Each rpc normalizes the partition IDs of the keys in its input entities,
|
||||
// and always returns entities with keys with normalized partition IDs.
|
||||
// (Note that applies to all entities, including entities in values.)
|
||||
service DatastoreService {
|
||||
// Look up some entities by key.
|
||||
rpc Lookup(LookupRequest) returns (LookupResponse) {
|
||||
};
|
||||
// Query for entities.
|
||||
rpc RunQuery(RunQueryRequest) returns (RunQueryResponse) {
|
||||
};
|
||||
// Begin a new transaction.
|
||||
rpc BeginTransaction(BeginTransactionRequest) returns (BeginTransactionResponse) {
|
||||
};
|
||||
// Commit a transaction, optionally creating, deleting or modifying some
|
||||
// entities.
|
||||
rpc Commit(CommitRequest) returns (CommitResponse) {
|
||||
};
|
||||
// Roll back a transaction.
|
||||
rpc Rollback(RollbackRequest) returns (RollbackResponse) {
|
||||
};
|
||||
// Allocate IDs for incomplete keys (useful for referencing an entity before
|
||||
// it is inserted).
|
||||
rpc AllocateIds(AllocateIdsRequest) returns (AllocateIdsResponse) {
|
||||
};
|
||||
}
|
||||
57
Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go
generated
vendored
Normal file
57
Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2014 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 testutil contains helper functions for writing tests.
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/cloud"
|
||||
)
|
||||
|
||||
const (
|
||||
envProjID = "GCLOUD_TESTS_GOLANG_PROJECT_ID"
|
||||
envPrivateKey = "GCLOUD_TESTS_GOLANG_KEY"
|
||||
)
|
||||
|
||||
func Context(scopes ...string) context.Context {
|
||||
key, projID := os.Getenv(envPrivateKey), os.Getenv(envProjID)
|
||||
if key == "" || projID == "" {
|
||||
log.Fatal("GCLOUD_TESTS_GOLANG_KEY and GCLOUD_TESTS_GOLANG_PROJECT_ID must be set. See CONTRIBUTING.md for details.")
|
||||
}
|
||||
jsonKey, err := ioutil.ReadFile(key)
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot read the JSON key file, err: %v", err)
|
||||
}
|
||||
conf, err := google.JWTConfigFromJSON(jsonKey, scopes...)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return cloud.NewContext(projID, conf.Client(oauth2.NoContext))
|
||||
}
|
||||
|
||||
func NoAuthContext() context.Context {
|
||||
projID := os.Getenv(envProjID)
|
||||
if projID == "" {
|
||||
log.Fatal("GCLOUD_TESTS_GOLANG_PROJECT_ID must be set. See CONTRIBUTING.md for details.")
|
||||
}
|
||||
return cloud.NewContext(projID, &http.Client{Transport: http.DefaultTransport})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue