Merge pull request #12238 from eparis/godeps

Auto commit by PR queue bot
pull/6/head
Alex Mohr 2015-08-05 01:06:11 -07:00
commit 159ba48932
38 changed files with 23178 additions and 11125 deletions

52
Godeps/Godeps.json generated
View File

@ -14,26 +14,6 @@
"ImportPath": "code.google.com/p/gcfg",
"Rev": "c2d3050044d05357eaf6c3547249ba57c5e235cb"
},
{
"ImportPath": "code.google.com/p/go-uuid/uuid",
"Comment": "null-12",
"Rev": "7dda39b2e7d5e265014674c5af696ba4186679e9"
},
{
"ImportPath": "code.google.com/p/google-api-go-client/compute/v1",
"Comment": "release-105",
"Rev": "98c78185197025f935947caac56a7b6d022f89d2"
},
{
"ImportPath": "code.google.com/p/google-api-go-client/container/v1beta1",
"Comment": "release-105",
"Rev": "98c78185197025f935947caac56a7b6d022f89d2"
},
{
"ImportPath": "code.google.com/p/google-api-go-client/googleapi",
"Comment": "release-105",
"Rev": "98c78185197025f935947caac56a7b6d022f89d2"
},
{
"ImportPath": "github.com/GoogleCloudPlatform/gcloud-golang/compute/metadata",
"Rev": "e34a32f9b0ecbc0784865fb2d47f3818c09521d4"
@ -390,35 +370,35 @@
},
{
"ImportPath": "github.com/mesos/mesos-go/auth",
"Rev": "6440c09c9d8a1b365f3c3e9b2297dd856abd017c"
"Rev": "65cb9ffec50a76f4ed9fe4808405b66b3bb7010d"
},
{
"ImportPath": "github.com/mesos/mesos-go/detector",
"Rev": "6440c09c9d8a1b365f3c3e9b2297dd856abd017c"
"Rev": "65cb9ffec50a76f4ed9fe4808405b66b3bb7010d"
},
{
"ImportPath": "github.com/mesos/mesos-go/executor",
"Rev": "6440c09c9d8a1b365f3c3e9b2297dd856abd017c"
"Rev": "65cb9ffec50a76f4ed9fe4808405b66b3bb7010d"
},
{
"ImportPath": "github.com/mesos/mesos-go/mesosproto",
"Rev": "6440c09c9d8a1b365f3c3e9b2297dd856abd017c"
"Rev": "65cb9ffec50a76f4ed9fe4808405b66b3bb7010d"
},
{
"ImportPath": "github.com/mesos/mesos-go/mesosutil",
"Rev": "6440c09c9d8a1b365f3c3e9b2297dd856abd017c"
"Rev": "65cb9ffec50a76f4ed9fe4808405b66b3bb7010d"
},
{
"ImportPath": "github.com/mesos/mesos-go/messenger",
"Rev": "6440c09c9d8a1b365f3c3e9b2297dd856abd017c"
"Rev": "65cb9ffec50a76f4ed9fe4808405b66b3bb7010d"
},
{
"ImportPath": "github.com/mesos/mesos-go/scheduler",
"Rev": "6440c09c9d8a1b365f3c3e9b2297dd856abd017c"
"Rev": "65cb9ffec50a76f4ed9fe4808405b66b3bb7010d"
},
{
"ImportPath": "github.com/mesos/mesos-go/upid",
"Rev": "6440c09c9d8a1b365f3c3e9b2297dd856abd017c"
"Rev": "65cb9ffec50a76f4ed9fe4808405b66b3bb7010d"
},
{
"ImportPath": "github.com/miekg/dns",
@ -442,6 +422,10 @@
"Comment": "v1.0-28-g8adf9e1",
"Rev": "8adf9e1730c55cdc590de7d49766cb2acc88d8f2"
},
{
"ImportPath": "github.com/pborman/uuid",
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
},
{
"ImportPath": "github.com/prometheus/client_golang/extraction",
"Comment": "0.4.0-1-g692492e",
@ -563,6 +547,18 @@
"ImportPath": "golang.org/x/oauth2",
"Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9"
},
{
"ImportPath": "google.golang.org/api/compute/v1",
"Rev": "0c2979aeaa5b573e60d3ddffe5ce8dca8df309bd"
},
{
"ImportPath": "google.golang.org/api/container/v1beta1",
"Rev": "0c2979aeaa5b573e60d3ddffe5ce8dca8df309bd"
},
{
"ImportPath": "google.golang.org/api/googleapi",
"Rev": "0c2979aeaa5b573e60d3ddffe5ce8dca8df309bd"
},
{
"ImportPath": "google.golang.org/cloud/compute/metadata",
"Rev": "2e43671e4ad874a7bca65746ff3edb38e6e93762"

File diff suppressed because it is too large Load Diff

View File

@ -270,9 +270,6 @@ func (zkc *Client) monitorSession(sessionEvents <-chan zk.Event, connected chan
default: // message buf full, this becomes a non-blocking noop
}
case zk.StateSyncConnected:
log.Infoln("syncConnected to zookper server")
case zk.StateDisconnected:
log.Infoln("zookeeper client disconnected")

View File

@ -25,7 +25,6 @@ import (
"sync"
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/gogo/protobuf/proto"
log "github.com/golang/glog"
"github.com/mesos/mesos-go/mesosproto"
@ -33,6 +32,7 @@ import (
"github.com/mesos/mesos-go/mesosutil/process"
"github.com/mesos/mesos-go/messenger"
"github.com/mesos/mesos-go/upid"
"github.com/pborman/uuid"
"golang.org/x/net/context"
)

View File

@ -28,12 +28,12 @@ import (
"testing"
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/gogo/protobuf/proto"
log "github.com/golang/glog"
mesos "github.com/mesos/mesos-go/mesosproto"
util "github.com/mesos/mesos-go/mesosutil"
"github.com/mesos/mesos-go/testutil"
"github.com/pborman/uuid"
"github.com/stretchr/testify/assert"
)

View File

@ -28,7 +28,6 @@ import (
"sync"
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/gogo/protobuf/proto"
log "github.com/golang/glog"
"github.com/mesos/mesos-go/auth"
@ -38,6 +37,7 @@ import (
"github.com/mesos/mesos-go/mesosutil/process"
"github.com/mesos/mesos-go/messenger"
"github.com/mesos/mesos-go/upid"
"github.com/pborman/uuid"
"golang.org/x/net/context"
)

View File

@ -0,0 +1 @@
Paul Borman <borman@google.com>

View File

@ -1,4 +1,4 @@
Copyright (c) 2009 Google Inc. All rights reserved.
Copyright (c) 2009,2014 Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are

30
Godeps/_workspace/src/github.com/pborman/uuid/json.go generated vendored Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2014 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 uuid
import "errors"
func (u UUID) MarshalJSON() ([]byte, error) {
if len(u) == 0 {
return []byte(`""`), nil
}
return []byte(`"` + u.String() + `"`), nil
}
func (u *UUID) UnmarshalJSON(data []byte) error {
if len(data) == 0 || string(data) == `""` {
return nil
}
if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
return errors.New("invalid UUID format")
}
data = data[1 : len(data)-1]
uu := Parse(string(data))
if uu == nil {
return errors.New("invalid UUID format")
}
*u = uu
return nil
}

View File

@ -0,0 +1,32 @@
// Copyright 2014 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 uuid
import (
"encoding/json"
"reflect"
"testing"
)
var testUUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
func TestJSON(t *testing.T) {
type S struct {
ID1 UUID
ID2 UUID
}
s1 := S{ID1: testUUID}
data, err := json.Marshal(&s1)
if err != nil {
t.Fatal(err)
}
var s2 S
if err := json.Unmarshal(data, &s2); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(&s1, &s2) {
t.Errorf("got %#v, want %#v", s2, s1)
}
}

View File

@ -0,0 +1,66 @@
// Copyright 2014 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 uuid
import (
"flag"
"runtime"
"testing"
"time"
)
// This test is only run when --regressions is passed on the go test line.
var regressions = flag.Bool("regressions", false, "run uuid regression tests")
// TestClockSeqRace tests for a particular race condition of returning two
// identical Version1 UUIDs. The duration of 1 minute was chosen as the race
// condition, before being fixed, nearly always occured in under 30 seconds.
func TestClockSeqRace(t *testing.T) {
if !*regressions {
t.Skip("skipping regression tests")
}
duration := time.Minute
done := make(chan struct{})
defer close(done)
ch := make(chan UUID, 10000)
ncpu := runtime.NumCPU()
switch ncpu {
case 0, 1:
// We can't run the test effectively.
t.Skip("skipping race test, only one CPU detected")
return
default:
runtime.GOMAXPROCS(ncpu)
}
for i := 0; i < ncpu; i++ {
go func() {
for {
select {
case <-done:
return
case ch <- NewUUID():
}
}
}()
}
uuids := make(map[string]bool)
cnt := 0
start := time.Now()
for u := range ch {
s := u.String()
if uuids[s] {
t.Errorf("duplicate uuid after %d in %v: %s", cnt, time.Since(start), s)
return
}
uuids[s] = true
if time.Since(start) > duration {
return
}
cnt++
}
}

View File

@ -40,15 +40,15 @@ func (t Time) UnixTime() (sec, nsec int64) {
}
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
// adjusts the clock sequence as needed. An error is returned if the current
// time cannot be determined.
func GetTime() (Time, error) {
// clock sequence as well as adjusting the clock sequence as needed. An error
// is returned if the current time cannot be determined.
func GetTime() (Time, uint16, error) {
defer mu.Unlock()
mu.Lock()
return getTime()
}
func getTime() (Time, error) {
func getTime() (Time, uint16, error) {
t := timeNow()
// If we don't have a clock sequence already, set one.
@ -63,7 +63,7 @@ func getTime() (Time, error) {
clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
}
lasttime = now
return Time(now), nil
return Time(now), clock_seq, nil
}
// ClockSequence returns the current clock sequence, generating one if not

View File

@ -19,7 +19,7 @@ func NewUUID() UUID {
SetNodeInterface("")
}
now, err := GetTime()
now, seq, err := GetTime()
if err != nil {
return nil
}
@ -34,7 +34,7 @@ func NewUUID() UUID {
binary.BigEndian.PutUint32(uuid[0:], time_low)
binary.BigEndian.PutUint16(uuid[4:], time_mid)
binary.BigEndian.PutUint16(uuid[6:], time_hi)
binary.BigEndian.PutUint16(uuid[8:], clock_seq)
binary.BigEndian.PutUint16(uuid[8:], seq)
copy(uuid[10:], nodeID)
return uuid

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
{
"kind": "discovery#restDescription",
"etag": "\"l66ggWbucbkBw9Lpos72oziyefE/ZrZBeDfQYPqAxFURJt0IhCOLUHQ\"",
"etag": "\"ye6orv2F-1npMW3u9suM3a7C5Bo/H5vlYxYcjMuH0uPSZu-WXGARHps\"",
"discoveryVersion": "v1",
"id": "container:v1beta1",
"name": "container",
"version": "v1beta1",
"revision": "20141103",
"revision": "20150326",
"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",
@ -14,6 +14,7 @@
"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/docs/v1beta1/",
"protocol": "rest",
"baseUrl": "https://www.googleapis.com/container/v1beta1/projects/",
"basePath": "/container/v1beta1/projects/",
@ -78,15 +79,14 @@
"Cluster": {
"id": "Cluster",
"type": "object",
"externalTypeName": "container.v1beta1.Cluster",
"properties": {
"clusterApiVersion": {
"type": "string",
"description": "The API version of the Kubernetes master and kubelets running in this cluster. Allowed value is 0.4.2, or leave blank to pick up the latest stable release."
"description": "The API version of the Kubernetes master and kubelets running in this cluster. Leave blank to pick up the latest stable release, or specify a version of the form \"x.y.z\". The Google Container Engine release notes lists the currently supported versions. If an incorrect version is specified, the server returns an error listing the currently supported versions."
},
"containerIpv4Cidr": {
"type": "string",
"description": "[Output only] The IP addresses of the container pods in this cluster, in CIDR notation (e.g. 1.2.3.4/29)."
"description": "The IP address range of the container pods in this cluster, in CIDR 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 or 172.16.0.0/12."
},
"creationTimestamp": {
"type": "string",
@ -96,18 +96,37 @@
"type": "string",
"description": "An optional description of this cluster."
},
"enableCloudLogging": {
"type": "boolean",
"description": "Whether logs from the cluster should be made available via the Google Cloud Logging service. This includes both logs from your applications running in the cluster as well as logs from the Kubernetes components themselves."
},
"enableCloudMonitoring": {
"type": "boolean",
"description": "Whether metrics from the cluster should be made available via the Google Cloud Monitoring service."
},
"endpoint": {
"type": "string",
"description": "[Output only] The IP address of this cluster's Kubernetes master. The endpoint can be accessed from the internet at https://username:password@endpoint/.\n\nSee the masterAuth property of this resource for username and password information."
},
"instanceGroupUrls": {
"type": "array",
"description": "[Output only] The resource URLs of [instance groups](/compute/docs/instance-groups/) associated with this cluster.",
"items": {
"type": "string"
}
},
"masterAuth": {
"$ref": "MasterAuth",
"description": "The HTTP basic authentication information for accessing the master. Because the master endpoint is open to the internet, you should create a strong password."
"description": "The authentication information for accessing the master."
},
"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: \n- Lowercase letters, numbers, and hyphens only.\n- Must start with a letter.\n- Must end with a number or a letter."
},
"network": {
"type": "string",
"description": "The name of the Google Compute Engine network to which the cluster is connected."
},
"nodeConfig": {
"$ref": "NodeConfig",
"description": "The machine type and image to use for all nodes in this cluster. See the descriptions of the child properties of nodeConfig."
@ -122,9 +141,13 @@
"description": "The number of nodes to create in this cluster. You must ensure that your Compute Engine resource quota is sufficient for this number of instances plus one (to include the master). You must also have available firewall and routes quota.",
"format": "int32"
},
"selfLink": {
"type": "string",
"description": "[Output only] Server-defined URL for the resource."
},
"servicesIpv4Cidr": {
"type": "string",
"description": "[Output only] The IP addresses of the Kubernetes services in this cluster, in CIDR notation (e.g. 1.2.3.4/29). Service addresses are always in the 10.0.0.0/16 range."
"description": "[Output only] The IP address range of the Kubernetes services in this cluster, in CIDR notation (e.g. 1.2.3.4/29). Service addresses are typically put in the last /16 from the container CIDR."
},
"status": {
"type": "string",
@ -155,7 +178,6 @@
"CreateClusterRequest": {
"id": "CreateClusterRequest",
"type": "object",
"externalTypeName": "container.v1beta1.CreateClusterRequest",
"properties": {
"cluster": {
"$ref": "Cluster",
@ -166,7 +188,6 @@
"ListAggregatedClustersResponse": {
"id": "ListAggregatedClustersResponse",
"type": "object",
"externalTypeName": "container.v1beta1.ListAggregatedClustersResponse",
"properties": {
"clusters": {
"type": "array",
@ -180,7 +201,6 @@
"ListAggregatedOperationsResponse": {
"id": "ListAggregatedOperationsResponse",
"type": "object",
"externalTypeName": "container.v1beta1.ListAggregatedOperationsResponse",
"properties": {
"operations": {
"type": "array",
@ -194,7 +214,6 @@
"ListClustersResponse": {
"id": "ListClustersResponse",
"type": "object",
"externalTypeName": "container.v1beta1.ListClustersResponse",
"properties": {
"clusters": {
"type": "array",
@ -208,7 +227,6 @@
"ListOperationsResponse": {
"id": "ListOperationsResponse",
"type": "object",
"externalTypeName": "container.v1beta1.ListOperationsResponse",
"properties": {
"operations": {
"type": "array",
@ -222,27 +240,49 @@
"MasterAuth": {
"id": "MasterAuth",
"type": "object",
"externalTypeName": "container.v1beta1.MasterAuth",
"description": "The authentication information for accessing the master. Authentication is either done using HTTP basic authentication or using a bearer token.",
"properties": {
"bearerToken": {
"type": "string",
"description": "The token used to authenticate API requests to the master. The token is to be included in an HTTP Authorization Header in all requests to the master endpoint. The format of the header is: \"Authorization: Bearer \"."
},
"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."
},
"clusterCaCertificate": {
"type": "string",
"description": "[Output only] Base64 encoded public certificate that is the root of trust for the cluster."
},
"password": {
"type": "string",
"description": "The password to use when accessing the Kubernetes master endpoint."
"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."
},
"user": {
"type": "string",
"description": "The username to use when accessing the Kubernetes master endpoint."
"description": "The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint."
}
}
},
"NodeConfig": {
"id": "NodeConfig",
"type": "object",
"externalTypeName": "container.v1beta1.NodeConfig",
"properties": {
"machineType": {
"type": "string",
"description": "The name of a Google Compute Engine machine type (e.g. n1-standard-1).\n\nIf unspecified, the default machine type is n1-standard-1."
},
"serviceAccounts": {
"type": "array",
"description": "The optional list of ServiceAccounts, each with their specified scopes, to be made available on all of the node VMs. In addition to the service accounts and scopes specified, the \"default\" account will always be created with the following scopes to ensure the correct functioning of the cluster: \n- https://www.googleapis.com/auth/compute,\n- https://www.googleapis.com/auth/devstorage.read_only",
"items": {
"$ref": "ServiceAccount"
}
},
"sourceImage": {
"type": "string",
"description": "The fully-specified name of a Google Compute Engine image. For example: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/backports-debian-7-wheezy-vYYYYMMDD (where YYYMMDD is the version date).\n\nIf specifying an image, you are responsible for ensuring its compatibility with the Debian 7 backports image. We recommend leaving this field blank to accept the default backports-debian-7-wheezy value."
@ -253,7 +293,6 @@
"id": "Operation",
"type": "object",
"description": "Defines the operation resource. All fields are output only.",
"externalTypeName": "container.v1beta1.Operation",
"properties": {
"errorMessage": {
"type": "string",
@ -261,7 +300,7 @@
},
"name": {
"type": "string",
"description": "The server-assigned ID for this operation. If the operation is fulfilled upfront, it may not have a resource name."
"description": "The server-assigned ID for the operation."
},
"operationType": {
"type": "string",
@ -275,6 +314,10 @@
""
]
},
"selfLink": {
"type": "string",
"description": "Server-defined URL for the resource."
},
"status": {
"type": "string",
"description": "The current status of the operation.",
@ -293,11 +336,33 @@
"type": "string",
"description": "[Optional] The URL of the cluster resource that this operation is associated with."
},
"targetLink": {
"type": "string",
"description": "Server-defined URL for the target of the operation."
},
"zone": {
"type": "string",
"description": "The name of the Google Compute Engine zone in which the operation is taking place."
}
}
},
"ServiceAccount": {
"id": "ServiceAccount",
"type": "object",
"description": "A Compute Engine service account.",
"properties": {
"email": {
"type": "string",
"description": "Email address of the service account."
},
"scopes": {
"type": "array",
"description": "The list of scopes to be made available for this service account.",
"items": {
"type": "string"
}
}
}
}
},
"resources": {
@ -365,7 +430,7 @@
"id": "container.projects.zones.clusters.create",
"path": "{projectId}/zones/{zoneId}/clusters",
"httpMethod": "POST",
"description": "Creates a cluster, consisting of the specified number and type of Google Compute Engine instances, plus a Kubernetes master instance.\n\nThe cluster is created in the project's default network.\n\nA firewall is added that allows traffic into port 443 on the master, which enables HTTPS. A firewall and a route is added for each node to allow the containers on that node to communicate with all other instances in the cluster.\n\nFinally, a route named k8s-iproute-10-xx-0-0 is created to track that the cluster's 10.xx.0.0/16 CIDR has been assigned.",
"description": "Creates a cluster, consisting of the specified number and type of Google Compute Engine instances, plus a Kubernetes master instance.\n\nThe cluster is created in the project's default network.\n\nA firewall is added that allows traffic into port 443 on the master, which enables HTTPS. A firewall and a route is added for each node to allow the containers on that node to communicate with all other instances in the cluster.\n\nFinally, an entry is added to the project's global metadata indicating which CIDR range is being used by the cluster.",
"parameters": {
"projectId": {
"type": "string",

View File

@ -1,18 +1,21 @@
// Package container provides access to the Google Container Engine API.
//
// See https://cloud.google.com/container-engine/docs/v1beta1/
//
// Usage example:
//
// import "code.google.com/p/google-api-go-client/container/v1beta1"
// import "google.golang.org/api/container/v1beta1"
// ...
// containerService, err := container.New(oauthHttpClient)
package container
import (
"bytes"
"code.google.com/p/google-api-go-client/googleapi"
"encoding/json"
"errors"
"fmt"
"golang.org/x/net/context"
"google.golang.org/api/googleapi"
"io"
"net/http"
"net/url"
@ -31,6 +34,7 @@ var _ = url.Parse
var _ = googleapi.Version
var _ = errors.New
var _ = strings.Replace
var _ = context.Background
const apiId = "container:v1beta1"
const apiName = "container"
@ -53,12 +57,20 @@ func New(client *http.Client) (*Service, error) {
}
type Service struct {
client *http.Client
BasePath string // API endpoint base URL
client *http.Client
BasePath string // API endpoint base URL
UserAgent string // optional additional User-Agent fragment
Projects *ProjectsService
}
func (s *Service) userAgent() string {
if s.UserAgent == "" {
return googleapi.UserAgent
}
return googleapi.UserAgent + " " + s.UserAgent
}
func NewProjectsService(s *Service) *ProjectsService {
rs := &ProjectsService{s: s}
rs.Clusters = NewProjectsClustersService(s)
@ -130,12 +142,17 @@ type ProjectsZonesOperationsService struct {
type Cluster struct {
// ClusterApiVersion: The API version of the Kubernetes master and
// kubelets running in this cluster. Allowed value is 0.4.2, or leave
// blank to pick up the latest stable release.
// kubelets running in this cluster. Leave blank to pick up the latest
// stable release, or specify a version of the form "x.y.z". The Google
// Container Engine release notes lists the currently supported
// versions. If an incorrect version is specified, the server returns an
// error listing the currently supported versions.
ClusterApiVersion string `json:"clusterApiVersion,omitempty"`
// ContainerIpv4Cidr: [Output only] The IP addresses of the container
// pods in this cluster, in CIDR notation (e.g. 1.2.3.4/29).
// ContainerIpv4Cidr: The IP address range of the container pods in this
// cluster, in CIDR 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 or
// 172.16.0.0/12.
ContainerIpv4Cidr string `json:"containerIpv4Cidr,omitempty"`
// CreationTimestamp: [Output only] The time the cluster was created, in
@ -145,28 +162,43 @@ type Cluster struct {
// Description: An optional description of this cluster.
Description string `json:"description,omitempty"`
// EnableCloudLogging: Whether logs from the cluster should be made
// available via the Google Cloud Logging service. This includes both
// logs from your applications running in the cluster as well as logs
// from the Kubernetes components themselves.
EnableCloudLogging bool `json:"enableCloudLogging,omitempty"`
// EnableCloudMonitoring: Whether metrics from the cluster should be
// made available via the Google Cloud Monitoring service.
EnableCloudMonitoring bool `json:"enableCloudMonitoring,omitempty"`
// Endpoint: [Output only] The IP address of this cluster's Kubernetes
// master. 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.
// See the masterAuth property of this resource for username and
// password information.
Endpoint string `json:"endpoint,omitempty"`
// MasterAuth: The HTTP basic authentication information for accessing
// the master. Because the master endpoint is open to the internet, you
// should create a strong password.
// InstanceGroupUrls: [Output only] The resource URLs of [instance
// groups](/compute/docs/instance-groups/) associated with this cluster.
InstanceGroupUrls []string `json:"instanceGroupUrls,omitempty"`
// MasterAuth: The authentication information for accessing the master.
MasterAuth *MasterAuth `json:"masterAuth,omitempty"`
// Name: 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 start with a letter.
// - Must end with a number or a letter.
Name string `json:"name,omitempty"`
// Network: The name of the Google Compute Engine network to which the
// cluster is connected.
Network string `json:"network,omitempty"`
// NodeConfig: The machine type and image to use for all nodes in this
// cluster. See the descriptions of the child properties of nodeConfig.
NodeConfig *NodeConfig `json:"nodeConfig,omitempty"`
@ -181,12 +213,22 @@ type Cluster struct {
// have available firewall and routes quota.
NumNodes int64 `json:"numNodes,omitempty"`
// ServicesIpv4Cidr: [Output only] The IP addresses of the Kubernetes
// services in this cluster, in CIDR notation (e.g. 1.2.3.4/29).
// Service addresses are always in the 10.0.0.0/16 range.
// SelfLink: [Output only] Server-defined URL for the resource.
SelfLink string `json:"selfLink,omitempty"`
// ServicesIpv4Cidr: [Output only] The IP address range of the
// Kubernetes services in this cluster, in CIDR notation (e.g.
// 1.2.3.4/29). Service addresses are typically put in the last /16 from
// the container CIDR.
ServicesIpv4Cidr string `json:"servicesIpv4Cidr,omitempty"`
// Status: [Output only] The current status of this cluster.
//
// Possible values:
// "error"
// "provisioning"
// "running"
// "stopping"
Status string `json:"status,omitempty"`
// StatusMessage: [Output only] Additional information about the current
@ -224,13 +266,35 @@ type ListOperationsResponse struct {
Operations []*Operation `json:"operations,omitempty"`
}
// MasterAuth: The authentication information for accessing the master.
// Authentication is either done using HTTP basic authentication or
// using a bearer token.
type MasterAuth struct {
// Password: The password to use when accessing the Kubernetes master
// endpoint.
// BearerToken: The token used to authenticate API requests to the
// master. The token is to be included in an HTTP Authorization Header
// in all requests to the master endpoint. The format of the header is:
// "Authorization: Bearer ".
BearerToken string `json:"bearerToken,omitempty"`
// ClientCertificate: [Output only] Base64 encoded public certificate
// used by clients to authenticate to the cluster endpoint.
ClientCertificate string `json:"clientCertificate,omitempty"`
// ClientKey: [Output only] Base64 encoded private key used by clients
// to authenticate to the cluster endpoint.
ClientKey string `json:"clientKey,omitempty"`
// ClusterCaCertificate: [Output only] Base64 encoded public certificate
// that is the root of trust for the cluster.
ClusterCaCertificate string `json:"clusterCaCertificate,omitempty"`
// Password: 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.
Password string `json:"password,omitempty"`
// User: The username to use when accessing the Kubernetes master
// endpoint.
// User: The username to use for HTTP basic authentication when
// accessing the Kubernetes master endpoint.
User string `json:"user,omitempty"`
}
@ -238,15 +302,21 @@ type NodeConfig struct {
// MachineType: The name of a Google Compute Engine machine type (e.g.
// n1-standard-1).
//
// If unspecified, the default machine type is
// n1-standard-1.
// If unspecified, the default machine type is n1-standard-1.
MachineType string `json:"machineType,omitempty"`
// ServiceAccounts: The optional list of ServiceAccounts, each with
// their specified scopes, to be made available on all of the node VMs.
// In addition to the service accounts and scopes specified, the
// "default" account will always be created with the following scopes to
// ensure the correct functioning of the cluster:
// - https://www.googleapis.com/auth/compute,
// - https://www.googleapis.com/auth/devstorage.read_only
ServiceAccounts []*ServiceAccount `json:"serviceAccounts,omitempty"`
// SourceImage: The fully-specified name of a Google Compute Engine
// image. For example:
// https://www.googleapis.com/compute/v1/projects/debian-cloud/global/ima
// ges/backports-debian-7-wheezy-vYYYYMMDD (where YYYMMDD is the version
// date).
// https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/backports-debian-7-wheezy-vYYYYMMDD (where YYYMMDD is the version date).
//
// If specifying an image, you are responsible for ensuring its
// compatibility with the Debian 7 backports image. We recommend leaving
@ -255,30 +325,56 @@ type NodeConfig struct {
SourceImage string `json:"sourceImage,omitempty"`
}
// Operation: Defines the operation resource. All fields are output
// only.
type Operation struct {
// ErrorMessage: If an error has occurred, a textual description of the
// error.
ErrorMessage string `json:"errorMessage,omitempty"`
// Name: The server-assigned ID for this operation. If the operation is
// fulfilled upfront, it may not have a resource name.
// Name: The server-assigned ID for the operation.
Name string `json:"name,omitempty"`
// OperationType: The operation type.
//
// Possible values:
// "createCluster"
// "deleteCluster"
OperationType string `json:"operationType,omitempty"`
// SelfLink: Server-defined URL for the resource.
SelfLink string `json:"selfLink,omitempty"`
// Status: The current status of the operation.
//
// Possible values:
// "done"
// "pending"
// "running"
Status string `json:"status,omitempty"`
// Target: [Optional] The URL of the cluster resource that this
// operation is associated with.
Target string `json:"target,omitempty"`
// TargetLink: Server-defined URL for the target of the operation.
TargetLink string `json:"targetLink,omitempty"`
// Zone: The name of the Google Compute Engine zone in which the
// operation is taking place.
Zone string `json:"zone,omitempty"`
}
// ServiceAccount: A Compute Engine service account.
type ServiceAccount struct {
// Email: Email address of the service account.
Email string `json:"email,omitempty"`
// Scopes: The list of scopes to be made available for this service
// account.
Scopes []string `json:"scopes,omitempty"`
}
// method id "container.projects.clusters.list":
type ProjectsClustersListCall struct {
@ -315,7 +411,7 @@ func (c *ProjectsClustersListCall) Do() (*ListAggregatedClustersResponse, error)
googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId,
})
req.Header.Set("User-Agent", "google-api-go-client/0.5")
req.Header.Set("User-Agent", c.s.userAgent())
res, err := c.s.client.Do(req)
if err != nil {
return nil, err
@ -391,7 +487,7 @@ func (c *ProjectsOperationsListCall) Do() (*ListAggregatedOperationsResponse, er
googleapi.Expand(req.URL, map[string]string{
"projectId": c.projectId,
})
req.Header.Set("User-Agent", "google-api-go-client/0.5")
req.Header.Set("User-Agent", c.s.userAgent())
res, err := c.s.client.Do(req)
if err != nil {
return nil, err
@ -445,17 +541,15 @@ type ProjectsZonesClustersCreateCall struct {
// type of Google Compute Engine instances, plus a Kubernetes master
// instance.
//
// The cluster is created in the project's default
// network.
// The cluster is created in the project's default network.
//
// A firewall is added that allows traffic into port 443 on
// the master, which enables HTTPS. A firewall and a route is added for
// each node to allow the containers on that node to communicate with
// all other instances in the cluster.
// A firewall is added that allows traffic into port 443 on the master,
// which enables HTTPS. A firewall and a route is added for each node to
// allow the containers on that node to communicate with all other
// instances in the cluster.
//
// Finally, a route named
// k8s-iproute-10-xx-0-0 is created to track that the cluster's
// 10.xx.0.0/16 CIDR has been assigned.
// Finally, an entry is added to the project's global metadata
// indicating which CIDR range is being used by the cluster.
func (r *ProjectsZonesClustersService) Create(projectId string, zoneId string, createclusterrequest *CreateClusterRequest) *ProjectsZonesClustersCreateCall {
c := &ProjectsZonesClustersCreateCall{s: r.s, opt_: make(map[string]interface{})}
c.projectId = projectId
@ -492,7 +586,7 @@ func (c *ProjectsZonesClustersCreateCall) Do() (*Operation, error) {
"zoneId": c.zoneId,
})
req.Header.Set("Content-Type", ctype)
req.Header.Set("User-Agent", "google-api-go-client/0.5")
req.Header.Set("User-Agent", c.s.userAgent())
res, err := c.s.client.Do(req)
if err != nil {
return nil, err
@ -507,7 +601,7 @@ func (c *ProjectsZonesClustersCreateCall) Do() (*Operation, error) {
}
return ret, nil
// {
// "description": "Creates a cluster, consisting of the specified number and type of Google Compute Engine instances, plus a Kubernetes master instance.\n\nThe cluster is created in the project's default network.\n\nA firewall is added that allows traffic into port 443 on the master, which enables HTTPS. A firewall and a route is added for each node to allow the containers on that node to communicate with all other instances in the cluster.\n\nFinally, a route named k8s-iproute-10-xx-0-0 is created to track that the cluster's 10.xx.0.0/16 CIDR has been assigned.",
// "description": "Creates a cluster, consisting of the specified number and type of Google Compute Engine instances, plus a Kubernetes master instance.\n\nThe cluster is created in the project's default network.\n\nA firewall is added that allows traffic into port 443 on the master, which enables HTTPS. A firewall and a route is added for each node to allow the containers on that node to communicate with all other instances in the cluster.\n\nFinally, an entry is added to the project's global metadata indicating which CIDR range is being used by the cluster.",
// "httpMethod": "POST",
// "id": "container.projects.zones.clusters.create",
// "parameterOrder": [
@ -555,8 +649,8 @@ type ProjectsZonesClustersDeleteCall struct {
// Delete: Deletes the cluster, including the Kubernetes master and all
// worker nodes.
//
// Firewalls and routes that were configured at cluster
// creation are also deleted.
// Firewalls and routes that were configured at cluster creation are
// also deleted.
func (r *ProjectsZonesClustersService) Delete(projectId string, zoneId string, clusterId string) *ProjectsZonesClustersDeleteCall {
c := &ProjectsZonesClustersDeleteCall{s: r.s, opt_: make(map[string]interface{})}
c.projectId = projectId
@ -588,7 +682,7 @@ func (c *ProjectsZonesClustersDeleteCall) Do() (*Operation, error) {
"zoneId": c.zoneId,
"clusterId": c.clusterId,
})
req.Header.Set("User-Agent", "google-api-go-client/0.5")
req.Header.Set("User-Agent", c.s.userAgent())
res, err := c.s.client.Do(req)
if err != nil {
return nil, err
@ -684,7 +778,7 @@ func (c *ProjectsZonesClustersGetCall) Do() (*Cluster, error) {
"zoneId": c.zoneId,
"clusterId": c.clusterId,
})
req.Header.Set("User-Agent", "google-api-go-client/0.5")
req.Header.Set("User-Agent", c.s.userAgent())
res, err := c.s.client.Do(req)
if err != nil {
return nil, err
@ -777,7 +871,7 @@ func (c *ProjectsZonesClustersListCall) Do() (*ListClustersResponse, error) {
"projectId": c.projectId,
"zoneId": c.zoneId,
})
req.Header.Set("User-Agent", "google-api-go-client/0.5")
req.Header.Set("User-Agent", c.s.userAgent())
res, err := c.s.client.Do(req)
if err != nil {
return nil, err
@ -866,7 +960,7 @@ func (c *ProjectsZonesOperationsGetCall) Do() (*Operation, error) {
"zoneId": c.zoneId,
"operationId": c.operationId,
})
req.Header.Set("User-Agent", "google-api-go-client/0.5")
req.Header.Set("User-Agent", c.s.userAgent())
res, err := c.s.client.Do(req)
if err != nil {
return nil, err
@ -959,7 +1053,7 @@ func (c *ProjectsZonesOperationsListCall) Do() (*ListOperationsResponse, error)
"projectId": c.projectId,
"zoneId": c.zoneId,
})
req.Header.Set("User-Agent", "google-api-go-client/0.5")
req.Header.Set("User-Agent", c.s.userAgent())
res, err := c.s.client.Do(req)
if err != nil {
return nil, err

View File

@ -9,6 +9,7 @@ package googleapi
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -16,10 +17,14 @@ import (
"net/http"
"net/textproto"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"sync"
"time"
"code.google.com/p/google-api-go-client/googleapi/internal/uritemplates"
"golang.org/x/net/context"
"google.golang.org/api/googleapi/internal/uritemplates"
)
// ContentTyper is an interface for Readers which know (or would like
@ -30,7 +35,25 @@ type ContentTyper interface {
ContentType() string
}
const Version = "0.5"
// A SizeReaderAt is a ReaderAt with a Size method.
// An io.SectionReader implements SizeReaderAt.
type SizeReaderAt interface {
io.ReaderAt
Size() int64
}
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 {
@ -130,14 +153,41 @@ func getMediaType(media io.Reader) (io.Reader, string) {
return media, typer.ContentType()
}
pr, pw := io.Pipe()
typ := "application/octet-stream"
buf, err := ioutil.ReadAll(io.LimitReader(media, 512))
if err != nil {
pw.CloseWithError(fmt.Errorf("error reading media: %v", err))
return pr, typ
}
typ = http.DetectContentType(buf)
mr := io.MultiReader(bytes.NewReader(buf), media)
go func() {
_, err = io.Copy(pw, mr)
if err != nil {
pw.CloseWithError(fmt.Errorf("error reading media: %v", err))
return
}
pw.Close()
}()
return pr, 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.(ContentTyper); ok {
return typer.ContentType()
}
typ := "application/octet-stream"
buf := make([]byte, 1024)
n, err := media.Read(buf)
n, err := media.ReadAt(buf, 0)
buf = buf[:n]
if err == nil {
if err == nil || err == io.EOF {
typ = http.DetectContentType(buf)
}
return io.MultiReader(bytes.NewBuffer(buf), media), typ
return typ
}
type Lengther interface {
@ -145,7 +195,7 @@ type Lengther interface {
}
// endingWithErrorReader from r until it returns an error. If the
// final error from r is os.EOF and e is non-nil, e is used instead.
// final error from r is io.EOF and e is non-nil, e is used instead.
type endingWithErrorReader struct {
r io.Reader
e error
@ -159,38 +209,6 @@ func (er endingWithErrorReader) Read(p []byte) (n int, err error) {
return
}
func getReaderSize(r io.Reader) (io.Reader, int64) {
// Ideal case, the reader knows its own size.
if lr, ok := r.(Lengther); ok {
return r, int64(lr.Len())
}
// But maybe it's a seeker and we can seek to the end to find its size.
if s, ok := r.(io.Seeker); ok {
pos0, err := s.Seek(0, os.SEEK_CUR)
if err == nil {
posend, err := s.Seek(0, os.SEEK_END)
if err == nil {
_, err = s.Seek(pos0, os.SEEK_SET)
if err == nil {
return r, posend - pos0
} else {
// We moved it forward but can't restore it.
// Seems unlikely, but can't really restore now.
return endingWithErrorReader{strings.NewReader(""), err}, posend - pos0
}
}
}
}
// Otherwise we have to make a copy to calculate how big the reader is.
buf := new(bytes.Buffer)
// TODO(bradfitz): put a cap on this copy? spill to disk after
// a certain point?
_, err := io.Copy(buf, r)
return endingWithErrorReader{buf, err}, int64(buf.Len())
}
func typeHeader(contentType string) textproto.MIMEHeader {
h := make(textproto.MIMEHeader)
h.Set("Content-Type", contentType)
@ -219,60 +237,192 @@ func (w countingWriter) Write(p []byte) (int, error) {
// to the "multipart/related" content type, with random boundary.
//
// The return value is the content-length of the entire multpart body.
func ConditionallyIncludeMedia(media io.Reader, bodyp *io.Reader, ctypep *string) (totalContentLength int64, ok bool) {
func ConditionallyIncludeMedia(media io.Reader, bodyp *io.Reader, ctypep *string) (cancel func(), ok bool) {
if media == nil {
return
}
// Get the media type and size. The type check might return a
// different reader instance, so do the size check first,
// which looks at the specific type of the io.Reader.
// Get the media type, which might return a different reader instance.
var mediaType string
if typer, ok := media.(ContentTyper); ok {
mediaType = typer.ContentType()
}
media, mediaSize := getReaderSize(media)
if mediaType == "" {
media, mediaType = getMediaType(media)
}
body, bodyType := *bodyp, *ctypep
body, bodySize := getReaderSize(body)
media, mediaType = getMediaType(media)
// Calculate how big the the multipart will be.
{
totalContentLength = bodySize + mediaSize
mpw := multipart.NewWriter(countingWriter{&totalContentLength})
mpw.CreatePart(typeHeader(bodyType))
mpw.CreatePart(typeHeader(mediaType))
mpw.Close()
}
body, bodyType := *bodyp, *ctypep
pr, pw := io.Pipe()
mpw := multipart.NewWriter(pw)
*bodyp = pr
*ctypep = "multipart/related; boundary=" + mpw.Boundary()
go func() {
defer pw.Close()
defer mpw.Close()
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 totalContentLength, true
cancel = func() { pw.CloseWithError(errAborted) }
return cancel, true
}
var errAborted = errors.New("googleapi: upload aborted")
// 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
started bool // whether the upload has been started
// Callback is an optional function that will be called upon every progress update.
Callback ProgressUpdater
}
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() (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 := rx.Client.Do(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) {
var start int64
var err error
res := &http.Response{}
if rx.started {
start, res, err = rx.transferStatus()
if err != nil || res.StatusCode != statusResumeIncomplete {
return res, err
}
}
rx.started = true
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 = rx.Client.Do(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, rx.ContentLength)
}
}
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 {

View File

@ -11,9 +11,15 @@ import (
"io/ioutil"
"net/http"
"net/url"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"testing"
"time"
"golang.org/x/net/context"
)
type SetOpaqueTest struct {
@ -359,3 +365,234 @@ func TestConvertVariant(t *testing.T) {
}
}
}
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) {
for _, tr := range statusTests {
rx := &ResumableUpload{
Client: &http.Client{Transport: tr},
}
g, _, err := rx.transferStatus()
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
progressBuf string
}
func (tr *interruptedTransport) ProgressUpdate(current, total int64) {
tr.progressBuf += fmt.Sprintf("%v, %v\n", current, total)
}
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("transfered file corrupted:\ngot %s\nwant %s", tr.buf, slurp)
}
w := ""
for i := chunkSize; i <= st.Size(); i += chunkSize {
w += fmt.Sprintf("%v, %v\n", i, st.Size())
}
if st.Size()%chunkSize != 0 {
w += fmt.Sprintf("%v, %v\n", st.Size(), st.Size())
}
if tr.progressBuf != w {
t.Errorf("progress update error, got %v, want %v", tr.progressBuf, w)
}
}
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)
}
}
}

View File

@ -21,13 +21,13 @@ import (
"strings"
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/GoogleCloudPlatform/kubernetes/contrib/mesos/pkg/offers"
annotation "github.com/GoogleCloudPlatform/kubernetes/contrib/mesos/pkg/scheduler/meta"
"github.com/GoogleCloudPlatform/kubernetes/contrib/mesos/pkg/scheduler/metrics"
mresource "github.com/GoogleCloudPlatform/kubernetes/contrib/mesos/pkg/scheduler/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/gogo/protobuf/proto"
"github.com/pborman/uuid"
log "github.com/golang/glog"
mesos "github.com/mesos/mesos-go/mesosproto"

View File

@ -21,8 +21,8 @@ import (
"strconv"
"strings"
"code.google.com/p/go-uuid/uuid"
log "github.com/golang/glog"
"github.com/pborman/uuid"
)
type UID struct {

View File

@ -34,12 +34,12 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
"code.google.com/p/gcfg"
compute "code.google.com/p/google-api-go-client/compute/v1"
container "code.google.com/p/google-api-go-client/container/v1beta1"
"code.google.com/p/google-api-go-client/googleapi"
"github.com/golang/glog"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
compute "google.golang.org/api/compute/v1"
container "google.golang.org/api/container/v1beta1"
"google.golang.org/api/googleapi"
"google.golang.org/cloud/compute/metadata"
)

View File

@ -23,10 +23,10 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"code.google.com/p/google-api-go-client/googleapi"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/googleapi"
)
const (

View File

@ -20,8 +20,8 @@ import (
"sync"
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/pborman/uuid"
)
var uuidLock sync.Mutex