mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-10 12:32:03 +00:00
Merge branch 'master' into fix/golint
Conflicts: pkg/master/master.go pkg/master/pod_cache.go pkg/proxy/config/file.go pkg/proxy/proxier.go pkg/proxy/roundrobbin.go pkg/scheduler/randomfit.go pkg/scheduler/randomfit_test.go
This commit is contained in:
commit
41febcee5e
@ -2,6 +2,7 @@ language: go
|
||||
|
||||
go:
|
||||
- 1.3
|
||||
- 1.2
|
||||
- tip
|
||||
|
||||
install:
|
||||
@ -9,6 +10,7 @@ install:
|
||||
- go get github.com/coreos/etcd
|
||||
- ./hack/verify-gofmt.sh
|
||||
- ./hack/verify-boilerplate.sh
|
||||
- ./hack/install-std-race.sh
|
||||
- ./hack/build-go.sh
|
||||
|
||||
script:
|
||||
|
@ -128,7 +128,7 @@ If you are running both a remote kubernetes cluster and the local cluster, you c
|
||||
The folks at [CoreOS](http://coreos.com) have [instructions for running Kubernetes on CoreOS](http://coreos.com/blog/running-kubernetes-example-on-CoreOS-part-1/)
|
||||
|
||||
## Where to go next?
|
||||
[Detailed example application](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/examples/guestbook/guestbook.md)
|
||||
[Detailed example application](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/examples/guestbook/README.md)
|
||||
|
||||
[Example of dynamic updates](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/examples/update-demo/README.md)
|
||||
|
||||
|
@ -133,6 +133,25 @@ until $(curl --insecure --user ${user}:${passwd} --max-time 1 \
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Basic sanity checking
|
||||
for (( i=0; i<${#MINION_NAMES[@]}; i++)); do
|
||||
# Make sure docker is installed
|
||||
gcutil ssh ${MINION_NAMES[$i]} which docker > /dev/null
|
||||
if [ "$?" != "0" ]; then
|
||||
echo "Docker failed to install on ${MINION_NAMES[$i]} your cluster is unlikely to work correctly"
|
||||
echo "Please run ./cluster/kube-down.sh and re-create the cluster. (sorry!)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make sure the kubelet is running
|
||||
gcutil ssh ${MINION_NAMES[$i]} /etc/init.d/kubelet status
|
||||
if [ "$?" != "0" ]; then
|
||||
echo "Kubelet failed to install on ${MINION_NAMES[$i]} your cluster is unlikely to work correctly"
|
||||
echo "Please run ./cluster/kube-down.sh and re-create the cluster. (sorry!)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Kubernetes cluster is running. Access the master at:"
|
||||
echo
|
||||
|
@ -145,13 +145,19 @@ func runAtomicPutTest(c *client.Client) {
|
||||
Labels: map[string]string{
|
||||
"name": "atomicService",
|
||||
},
|
||||
// This is here because validation requires it.
|
||||
Selector: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
).Do().Into(&svc)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed creating atomicService: %v", err)
|
||||
}
|
||||
glog.Info("Created atomicService")
|
||||
testLabels := labels.Set{}
|
||||
testLabels := labels.Set{
|
||||
"foo": "bar",
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
// a: z, b: y, etc...
|
||||
testLabels[string([]byte{byte('a' + i)})] = string([]byte{byte('z' - i)})
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
kube_client "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
@ -50,6 +51,8 @@ var (
|
||||
verbose = flag.Bool("verbose", false, "If true, print extra information")
|
||||
proxy = flag.Bool("proxy", false, "If true, run a proxy to the api server")
|
||||
www = flag.String("www", "", "If -proxy is true, use this directory to serve static files")
|
||||
templateFile = flag.String("template_file", "", "If present load this file as a golang template and us it for output printing")
|
||||
templateStr = flag.String("template", "", "If present parse this string as a golang template and us it for output printing")
|
||||
)
|
||||
|
||||
func usage() {
|
||||
@ -184,11 +187,32 @@ func executeAPIRequest(method string, s *kube_client.Client) bool {
|
||||
}
|
||||
|
||||
var printer kubecfg.ResourcePrinter
|
||||
if *json {
|
||||
switch {
|
||||
case *json:
|
||||
printer = &kubecfg.IdentityPrinter{}
|
||||
} else if *yaml {
|
||||
case *yaml:
|
||||
printer = &kubecfg.YAMLPrinter{}
|
||||
} else {
|
||||
case len(*templateFile) > 0 || len(*templateStr) > 0:
|
||||
var data []byte
|
||||
if len(*templateFile) > 0 {
|
||||
var err error
|
||||
data, err = ioutil.ReadFile(*templateFile)
|
||||
if err != nil {
|
||||
glog.Fatalf("Error reading template %s, %v\n", *templateFile, err)
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
data = []byte(*templateStr)
|
||||
}
|
||||
tmpl, err := template.New("output").Parse(string(data))
|
||||
if err != nil {
|
||||
glog.Fatalf("Error parsing template %s, %v\n", string(data), err)
|
||||
return false
|
||||
}
|
||||
printer = &kubecfg.TemplatePrinter{
|
||||
Template: tmpl,
|
||||
}
|
||||
default:
|
||||
printer = &kubecfg.HumanReadablePrinter{}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,12 @@ This example also assumes that you have [Docker](http://docker.io) installed on
|
||||
|
||||
It also assumes that ```$DOCKER_USER``` is set to your docker user id.
|
||||
|
||||
You may need to open the firewall for port 8080 using the [console][cloud-console] or the `gcutil` tool. The following command will allow traffic from any source to instances tagged `kubernetes-minion`:
|
||||
|
||||
```shell
|
||||
$ gcutil addfirewall --allowed=tcp:8080 --target_tags=kubernetes-minion kubernetes-minion-8080
|
||||
```
|
||||
|
||||
### Step One: Build the image
|
||||
|
||||
$ cd kubernetes/examples/update-demo/image
|
||||
@ -83,3 +89,5 @@ We will now update the servers that are running out in your cluster.
|
||||
$ cluster/kubecfg.sh -u=30s rollingupdate dataController
|
||||
|
||||
Watch the UX, it will update one pod every 30 seconds until all of the pods have the new color.
|
||||
|
||||
[cloud-console]: https://console.developer.google.com
|
||||
|
@ -17,7 +17,7 @@
|
||||
# This script sets up a go workspace locally and builds all go components.
|
||||
# You can 'source' this file if you want to set up GOPATH in your local shell.
|
||||
|
||||
if [ "$(which go)" == "" ]; then
|
||||
if [[ "$(which go)" == "" ]]; then
|
||||
echo "Can't find 'go' in PATH, please fix and retry."
|
||||
echo "See http://golang.org/doc/install for installation instructions."
|
||||
exit 1
|
||||
@ -29,7 +29,7 @@ fi
|
||||
if [ "${TRAVIS}" != "true" ]; then
|
||||
GO_VERSION=($(go version))
|
||||
|
||||
if [ ${GO_VERSION[2]} \< "go1.2" ]; then
|
||||
if [[ ${GO_VERSION[2]} < "go1.2" ]]; then
|
||||
echo "Detected go version: ${GO_VERSION}."
|
||||
echo "Kubernetes requires go version 1.2 or greater."
|
||||
echo "Please install Go version 1.2 or later"
|
||||
@ -37,10 +37,14 @@ if [ "${TRAVIS}" != "true" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
pushd $(dirname "${BASH_SOURCE}")/.. >/dev/null
|
||||
KUBE_REPO_ROOT="${PWD}"
|
||||
if [[ "$OSTYPE" == *darwin* ]]; then
|
||||
KUBE_REPO_ROOT="${PWD}/$(dirname ${BASH_SOURCE:-$0})/.."
|
||||
else
|
||||
KUBE_REPO_REL_ROOT="$(dirname ${BASH_SOURCE:-$0})/.."
|
||||
KUBE_REPO_ROOT="$(readlink -f ${KUBE_REPO_REL_ROOT})"
|
||||
fi
|
||||
|
||||
KUBE_TARGET="${KUBE_REPO_ROOT}/output/go"
|
||||
popd >/dev/null
|
||||
|
||||
mkdir -p "${KUBE_TARGET}"
|
||||
|
||||
|
28
hack/install-std-race.sh
Executable file
28
hack/install-std-race.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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.
|
||||
|
||||
# This script installs std -race on Travis (see https://code.google.com/p/go/issues/detail?id=6479)
|
||||
|
||||
set -e
|
||||
|
||||
if [ "${TRAVIS}" == "true" ]; then
|
||||
GO_VERSION=($(go version))
|
||||
|
||||
if [ ${GO_VERSION[2]} \< "go1.3" ]; then
|
||||
echo "Installing the -race compatible version of the std go library"
|
||||
go install -a -race std
|
||||
fi
|
||||
fi
|
@ -35,14 +35,16 @@ find_test_dirs() {
|
||||
)
|
||||
}
|
||||
|
||||
# -covermode=atomic becomes default with -race in Go >=1.3
|
||||
KUBE_COVER="-cover -covermode=atomic -coverprofile=\"tmp.out\""
|
||||
|
||||
cd "${KUBE_TARGET}"
|
||||
|
||||
if [ "$1" != "" ]; then
|
||||
go test -race -cover -coverprofile="tmp.out" "$KUBE_GO_PACKAGE/$1" "${@:2}"
|
||||
go test -race $KUBE_COVER "$KUBE_GO_PACKAGE/$1" "${@:2}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for package in $(find_test_dirs); do
|
||||
go test -race -cover -coverprofile="tmp.out" "${KUBE_GO_PACKAGE}/${package}" "${@:2}"
|
||||
go test -race $KUBE_COVER "${KUBE_GO_PACKAGE}/${package}" "${@:2}"
|
||||
done
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
@ -50,6 +51,7 @@ type ContainerManifest struct {
|
||||
// Required: This must be a supported version string, such as "v1beta1".
|
||||
Version string `yaml:"version" json:"version"`
|
||||
// Required: This must be a DNS_SUBDOMAIN.
|
||||
// TODO: ID on Manifest is deprecated and will be removed in the future.
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Volumes []Volume `yaml:"volumes" json:"volumes"`
|
||||
Containers []Container `yaml:"containers" json:"containers"`
|
||||
@ -116,11 +118,10 @@ type HTTPGetProbe struct {
|
||||
|
||||
// LivenessProbe describes a liveness probe to be examined to the container.
|
||||
type LivenessProbe struct {
|
||||
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
|
||||
// Type of liveness probe. Current legal values "http"
|
||||
Type string `yaml:"type,omitempty" json:"type,omitempty"`
|
||||
// HTTPGetProbe parameters, required if Type == 'http'
|
||||
HTTPGet HTTPGetProbe `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
|
||||
HTTPGet *HTTPGetProbe `yaml:"httpGet,omitempty" json:"httpGet,omitempty"`
|
||||
// Length of time before health checking is activated. In seconds.
|
||||
InitialDelaySeconds int64 `yaml:"initialDelaySeconds,omitempty" json:"initialDelaySeconds,omitempty"`
|
||||
}
|
||||
@ -141,9 +142,9 @@ type Container struct {
|
||||
// Optional: Defaults to unlimited.
|
||||
Memory int `yaml:"memory,omitempty" json:"memory,omitempty"`
|
||||
// Optional: Defaults to unlimited.
|
||||
CPU int `yaml:"cpu,omitempty" json:"cpu,omitempty"`
|
||||
VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty" json:"volumeMounts,omitempty"`
|
||||
LivenessProbe LivenessProbe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"`
|
||||
CPU int `yaml:"cpu,omitempty" json:"cpu,omitempty"`
|
||||
VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty" json:"volumeMounts,omitempty"`
|
||||
LivenessProbe *LivenessProbe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"`
|
||||
}
|
||||
|
||||
// Percentile represents a pair which contains a percentage from 0 to 100 and
|
||||
@ -267,6 +268,10 @@ type Service struct {
|
||||
// This service will route traffic to pods having labels matching this selector.
|
||||
Selector map[string]string `json:"selector,omitempty" yaml:"selector,omitempty"`
|
||||
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" yaml:"createExternalLoadBalancer,omitempty"`
|
||||
|
||||
// ContainerPort is the name of the port on the container to direct traffic to.
|
||||
// Optional, if unspecified use the first port on the container.
|
||||
ContainerPort util.IntOrString `json:"containerPort,omitempty" yaml:"containerPort,omitempty"`
|
||||
}
|
||||
|
||||
// Endpoints is a collection of endpoints that implement the actual service, for example:
|
||||
|
@ -246,3 +246,14 @@ func ValidateManifest(manifest *ContainerManifest) []error {
|
||||
allErrs.Append(validateContainers(manifest.Containers, allVolumes)...)
|
||||
return []error(allErrs)
|
||||
}
|
||||
|
||||
func ValidateService(service *Service) []error {
|
||||
allErrs := errorList{}
|
||||
if service.ID == "" {
|
||||
allErrs.Append(makeInvalidError("Service.ID", service.ID))
|
||||
}
|
||||
if len(service.Selector) == 0 {
|
||||
allErrs.Append(makeInvalidError("Service.Selector", service.Selector))
|
||||
}
|
||||
return []error(allErrs)
|
||||
}
|
||||
|
@ -262,3 +262,36 @@ func TestValidateManifest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateService(t *testing.T) {
|
||||
errs := ValidateService(&Service{
|
||||
JSONBase: JSONBase{ID: "foo"},
|
||||
Selector: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
})
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Unexpected non-zero error list: %#v", errs)
|
||||
}
|
||||
|
||||
errs = ValidateService(&Service{
|
||||
Selector: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
})
|
||||
if len(errs) != 1 {
|
||||
t.Errorf("Unexpected error list: %#v", errs)
|
||||
}
|
||||
|
||||
errs = ValidateService(&Service{
|
||||
JSONBase: JSONBase{ID: "foo"},
|
||||
})
|
||||
if len(errs) != 1 {
|
||||
t.Errorf("Unexpected error list: %#v", errs)
|
||||
}
|
||||
|
||||
errs = ValidateService(&Service{})
|
||||
if len(errs) != 2 {
|
||||
t.Errorf("Unexpected error list: %#v", errs)
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ package controller
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
@ -64,9 +63,6 @@ func (r RealPodControl) createReplica(controllerSpec api.ReplicationController)
|
||||
labels["replicationController"] = controllerSpec.ID
|
||||
}
|
||||
pod := api.Pod{
|
||||
JSONBase: api.JSONBase{
|
||||
ID: fmt.Sprintf("%08x", rand.Uint32()),
|
||||
},
|
||||
DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState,
|
||||
Labels: controllerSpec.DesiredState.PodTemplate.Labels,
|
||||
}
|
||||
@ -104,10 +100,9 @@ func (rm *ReplicationManager) Run(period time.Duration) {
|
||||
func (rm *ReplicationManager) watchControllers() {
|
||||
watchChannel := make(chan *etcd.Response)
|
||||
stop := make(chan bool)
|
||||
defer func() {
|
||||
// Ensure that the call to watch ends.
|
||||
close(stop)
|
||||
}()
|
||||
// Ensure that the call to watch ends.
|
||||
defer close(stop)
|
||||
|
||||
go func() {
|
||||
defer util.HandleCrash()
|
||||
_, err := rm.etcdClient.Watch("/registry/controllers", 0, true, watchChannel, stop)
|
||||
@ -141,30 +136,29 @@ func (rm *ReplicationManager) watchControllers() {
|
||||
}
|
||||
|
||||
func (rm *ReplicationManager) handleWatchResponse(response *etcd.Response) (*api.ReplicationController, error) {
|
||||
if response.Action == "set" {
|
||||
if response.Node != nil {
|
||||
var controllerSpec api.ReplicationController
|
||||
err := json.Unmarshal([]byte(response.Node.Value), &controllerSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controllerSpec, nil
|
||||
switch response.Action {
|
||||
case "set":
|
||||
if response.Node == nil {
|
||||
return nil, fmt.Errorf("response node is null %#v", response)
|
||||
}
|
||||
return nil, fmt.Errorf("response node is null %#v", response)
|
||||
} else if response.Action == "delete" {
|
||||
var controllerSpec api.ReplicationController
|
||||
if err := json.Unmarshal([]byte(response.Node.Value), &controllerSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controllerSpec, nil
|
||||
case "delete":
|
||||
// Ensure that the final state of a replication controller is applied before it is deleted.
|
||||
// Otherwise, a replication controller could be modified and then deleted (for example, from 3 to 0
|
||||
// replicas), and it would be non-deterministic which of its pods continued to exist.
|
||||
if response.PrevNode != nil {
|
||||
var controllerSpec api.ReplicationController
|
||||
if err := json.Unmarshal([]byte(response.PrevNode.Value), &controllerSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controllerSpec, nil
|
||||
if response.PrevNode == nil {
|
||||
return nil, fmt.Errorf("previous node is null %#v", response)
|
||||
}
|
||||
return nil, fmt.Errorf("previous node is null %#v", response)
|
||||
var controllerSpec api.ReplicationController
|
||||
if err := json.Unmarshal([]byte(response.PrevNode.Value), &controllerSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controllerSpec, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -194,7 +188,7 @@ func (rm *ReplicationManager) syncReplicationController(controllerSpec api.Repli
|
||||
rm.podControl.createReplica(controllerSpec)
|
||||
}
|
||||
} else if diff > 0 {
|
||||
glog.Info("Too many replicas, deleting")
|
||||
glog.Infof("Too many replicas, deleting %d\n", diff)
|
||||
for i := 0; i < diff; i++ {
|
||||
rm.podControl.deletePod(filteredList[i].ID)
|
||||
}
|
||||
|
@ -200,12 +200,22 @@ func TestCreateReplica(t *testing.T) {
|
||||
|
||||
podControl.createReplica(controllerSpec)
|
||||
|
||||
//expectedPod := Pod{
|
||||
// Labels: controllerSpec.DesiredState.PodTemplate.Labels,
|
||||
// DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState,
|
||||
//}
|
||||
// TODO: fix this so that it validates the body.
|
||||
expectedPod := api.Pod{
|
||||
JSONBase: api.JSONBase{
|
||||
Kind: "Pod",
|
||||
},
|
||||
Labels: controllerSpec.DesiredState.PodTemplate.Labels,
|
||||
DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState,
|
||||
}
|
||||
fakeHandler.ValidateRequest(t, makeURL("/pods"), "POST", nil)
|
||||
actualPod := api.Pod{}
|
||||
if err := json.Unmarshal([]byte(fakeHandler.RequestBody), &actualPod); err != nil {
|
||||
t.Errorf("Unexpected error: %#v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expectedPod, actualPod) {
|
||||
t.Logf("Body: %s", fakeHandler.RequestBody)
|
||||
t.Errorf("Unexpected mismatch. Expected %#v, Got: %#v", expectedPod, actualPod)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleWatchResponseNotSet(t *testing.T) {
|
||||
|
20
pkg/healthz/doc.go
Normal file
20
pkg/healthz/doc.go
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
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 healthz implements basic http server health checking.
|
||||
// Usage:
|
||||
// import _ "healthz" registers a handler on the path '/healthz', that serves 200s
|
||||
package healthz
|
31
pkg/healthz/healthz.go
Normal file
31
pkg/healthz/healthz.go
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
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 healthz
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/healthz", handleHealthz)
|
||||
}
|
||||
|
||||
func handleHealthz(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO Support user supplied health functions too.
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("ok"))
|
||||
}
|
@ -22,6 +22,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
@ -235,3 +236,19 @@ func (h *HumanReadablePrinter) PrintObj(obj interface{}, output io.Writer) error
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
type TemplatePrinter struct {
|
||||
Template *template.Template
|
||||
}
|
||||
|
||||
func (t *TemplatePrinter) Print(data []byte, w io.Writer) error {
|
||||
obj, err := api.Decode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.PrintObj(obj, w)
|
||||
}
|
||||
|
||||
func (t *TemplatePrinter) PrintObj(obj interface{}, w io.Writer) error {
|
||||
return t.Template.Execute(w, obj)
|
||||
}
|
||||
|
@ -25,10 +25,16 @@ import (
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// HealthChecker is an abstract interface for container health checker.
|
||||
type HealthCheckStatus int
|
||||
|
||||
const (
|
||||
CheckHealthy HealthCheckStatus = 0
|
||||
CheckUnhealthy HealthCheckStatus = 1
|
||||
CheckUnknown HealthCheckStatus = 2
|
||||
)
|
||||
|
||||
type HealthChecker interface {
|
||||
// IsHealthy checks if the container is healthy.
|
||||
IsHealthy(container api.Container) (bool, error)
|
||||
HealthCheck(container api.Container) (HealthCheckStatus, error)
|
||||
}
|
||||
|
||||
type httpDoInterface interface {
|
||||
@ -51,14 +57,13 @@ type MuxHealthChecker struct {
|
||||
checkers map[string]HealthChecker
|
||||
}
|
||||
|
||||
// IsHealthy checks the health of the container by delegating to an appropriate HealthChecker according to container.LivenessProbe.Type.
|
||||
func (m *MuxHealthChecker) IsHealthy(container api.Container) (bool, error) {
|
||||
func (m *MuxHealthChecker) HealthCheck(container api.Container) (HealthCheckStatus, error) {
|
||||
checker, ok := m.checkers[container.LivenessProbe.Type]
|
||||
if !ok || checker == nil {
|
||||
glog.Warningf("Failed to find health checker for %s %s", container.Name, container.LivenessProbe.Type)
|
||||
return true, nil
|
||||
return CheckUnknown, nil
|
||||
}
|
||||
return checker.IsHealthy(container)
|
||||
return checker.HealthCheck(container)
|
||||
}
|
||||
|
||||
// HTTPHealthChecker is an implementation of HealthChecker which checks container health by sending HTTP Get requests.
|
||||
@ -76,15 +81,17 @@ func (h *HTTPHealthChecker) findPort(container api.Container, portName string) i
|
||||
return -1
|
||||
}
|
||||
|
||||
// IsHealthy checks if the container is healthy by trying sending HTTP Get requests to the container.
|
||||
func (h *HTTPHealthChecker) IsHealthy(container api.Container) (bool, error) {
|
||||
func (h *HTTPHealthChecker) HealthCheck(container api.Container) (HealthCheckStatus, error) {
|
||||
params := container.LivenessProbe.HTTPGet
|
||||
if params == nil {
|
||||
return CheckUnknown, fmt.Errorf("Error, no HTTP parameters specified: %v", container)
|
||||
}
|
||||
port := h.findPort(container, params.Port)
|
||||
if port == -1 {
|
||||
var err error
|
||||
port, err = strconv.ParseInt(params.Port, 10, 0)
|
||||
if err != nil {
|
||||
return true, err
|
||||
return CheckUnknown, err
|
||||
}
|
||||
}
|
||||
var host string
|
||||
@ -100,7 +107,11 @@ func (h *HTTPHealthChecker) IsHealthy(container api.Container) (bool, error) {
|
||||
}
|
||||
if err != nil {
|
||||
// At this point, if it fails, its either a policy (unlikely) or HTTP protocol (likely) error.
|
||||
return false, nil
|
||||
return CheckUnhealthy, nil
|
||||
}
|
||||
return res.StatusCode == http.StatusOK, nil
|
||||
if res.StatusCode == http.StatusOK {
|
||||
return CheckHealthy, nil
|
||||
}
|
||||
glog.V(1).Infof("Health check failed for %v, Response: %v", container, *res)
|
||||
return CheckUnhealthy, nil
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ func TestHttpHealth(t *testing.T) {
|
||||
}
|
||||
|
||||
container := api.Container{
|
||||
LivenessProbe: api.LivenessProbe{
|
||||
HTTPGet: api.HTTPGetProbe{
|
||||
LivenessProbe: &api.LivenessProbe{
|
||||
HTTPGet: &api.HTTPGetProbe{
|
||||
Port: "8080",
|
||||
Path: "/foo/bar",
|
||||
},
|
||||
@ -55,8 +55,8 @@ func TestHttpHealth(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
ok, err := check.IsHealthy(container)
|
||||
if !ok {
|
||||
ok, err := check.HealthCheck(container)
|
||||
if ok != CheckHealthy {
|
||||
t.Error("Unexpected unhealthy")
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/coreos/go-etcd/etcd"
|
||||
@ -44,6 +45,8 @@ import (
|
||||
"gopkg.in/v1/yaml"
|
||||
)
|
||||
|
||||
const defaultChanSize = 1024
|
||||
|
||||
// DockerContainerData is the structured representation of the JSON object returned by Docker inspect
|
||||
type DockerContainerData struct {
|
||||
state struct {
|
||||
@ -143,8 +146,9 @@ func (kl *Kubelet) RunKubelet(dockerEndpoint, configPath, manifestURL, etcdServe
|
||||
if address != "" {
|
||||
glog.Infof("Starting to listen on %s:%d", address, port)
|
||||
handler := KubeletServer{
|
||||
Kubelet: kl,
|
||||
UpdateChannel: updateChannel,
|
||||
Kubelet: kl,
|
||||
UpdateChannel: updateChannel,
|
||||
DelegateHandler: http.DefaultServeMux,
|
||||
}
|
||||
s := &http.Server{
|
||||
Addr: net.JoinHostPort(address, strconv.FormatUint(uint64(port), 10)),
|
||||
@ -192,7 +196,7 @@ func (kl *Kubelet) getDockerContainers() (map[DockerID]docker.APIContainers, err
|
||||
result := map[DockerID]docker.APIContainers{}
|
||||
containerList, err := kl.DockerClient.ListContainers(docker.ListContainersOptions{})
|
||||
if err != nil {
|
||||
return result, err
|
||||
return nil, err
|
||||
}
|
||||
for _, value := range containerList {
|
||||
// Skip containers that we didn't create to allow users to manually
|
||||
@ -202,7 +206,7 @@ func (kl *Kubelet) getDockerContainers() (map[DockerID]docker.APIContainers, err
|
||||
}
|
||||
result[DockerID(value.ID)] = value
|
||||
}
|
||||
return result, err
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Return Docker's container ID for a manifest's container. Returns an empty string if it doesn't exist.
|
||||
@ -732,7 +736,7 @@ func (kl *Kubelet) syncManifest(manifest *api.ContainerManifest, keepChannel cha
|
||||
glog.V(1).Infof("health check errored: %v", err)
|
||||
continue
|
||||
}
|
||||
if !healthy {
|
||||
if healthy != CheckHealthy {
|
||||
glog.V(1).Infof("manifest %s container %s is unhealthy.", manifest.ID, container.Name)
|
||||
if err != nil {
|
||||
glog.V(1).Infof("Failed to get container info %v, for %s", err, containerID)
|
||||
@ -758,7 +762,7 @@ func (kl *Kubelet) SyncManifests(config []api.ContainerManifest) error {
|
||||
glog.Infof("Desired: %+v", config)
|
||||
var err error
|
||||
dockerIdsToKeep := map[DockerID]empty{}
|
||||
keepChannel := make(chan DockerID)
|
||||
keepChannel := make(chan DockerID, defaultChanSize)
|
||||
waitGroup := sync.WaitGroup{}
|
||||
|
||||
// Check for any containers that need starting
|
||||
@ -989,16 +993,16 @@ func (kl *Kubelet) GetMachineStats() (*api.ContainerStats, error) {
|
||||
return kl.statsFromContainerPath("/")
|
||||
}
|
||||
|
||||
func (kl *Kubelet) healthy(container api.Container, dockerContainer *docker.APIContainers) (bool, error) {
|
||||
func (kl *Kubelet) healthy(container api.Container, dockerContainer *docker.APIContainers) (HealthCheckStatus, error) {
|
||||
// Give the container 60 seconds to start up.
|
||||
if !container.LivenessProbe.Enabled {
|
||||
return true, nil
|
||||
if container.LivenessProbe == nil {
|
||||
return CheckHealthy, nil
|
||||
}
|
||||
if time.Now().Unix()-dockerContainer.Created < container.LivenessProbe.InitialDelaySeconds {
|
||||
return true, nil
|
||||
return CheckHealthy, nil
|
||||
}
|
||||
if kl.HealthChecker == nil {
|
||||
return true, nil
|
||||
return CheckHealthy, nil
|
||||
}
|
||||
return kl.HealthChecker.IsHealthy(container)
|
||||
return kl.HealthChecker.HealthCheck(container)
|
||||
}
|
||||
|
@ -33,8 +33,9 @@ import (
|
||||
|
||||
// KubeletServer is a http.Handler which exposes kubelet functionality over HTTP.
|
||||
type KubeletServer struct {
|
||||
Kubelet kubeletInterface
|
||||
UpdateChannel chan<- manifestUpdate
|
||||
Kubelet kubeletInterface
|
||||
UpdateChannel chan<- manifestUpdate
|
||||
DelegateHandler http.Handler
|
||||
}
|
||||
|
||||
// kubeletInterface contains all the kubelet methods required by the server.
|
||||
@ -46,8 +47,7 @@ type kubeletInterface interface {
|
||||
}
|
||||
|
||||
func (s *KubeletServer) error(w http.ResponseWriter, err error) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Internal Error: %v", err)
|
||||
http.Error(w, fmt.Sprintf("Internal Error: %v", err), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func (s *KubeletServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
@ -87,20 +87,17 @@ func (s *KubeletServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
case u.Path == "/podInfo":
|
||||
podID := u.Query().Get("podID")
|
||||
if len(podID) == 0 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprint(w, "Missing 'podID=' query entry.")
|
||||
http.Error(w, "Missing 'podID=' query entry.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
info, err := s.Kubelet.GetPodInfo(podID)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Internal Error: %v", err)
|
||||
s.error(w, err)
|
||||
return
|
||||
}
|
||||
data, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Internal Error: %v", err)
|
||||
s.error(w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
@ -109,8 +106,7 @@ func (s *KubeletServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
case strings.HasPrefix(u.Path, "/stats"):
|
||||
s.serveStats(w, req)
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Not found.")
|
||||
s.DelegateHandler.ServeHTTP(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,13 +126,11 @@ func (s *KubeletServer) serveStats(w http.ResponseWriter, req *http.Request) {
|
||||
case 3:
|
||||
stats, err = s.Kubelet.GetContainerStats(components[1], components[2])
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "unknown resource.")
|
||||
http.Error(w, "unknown resource.", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Internal Error: %v", err)
|
||||
s.error(w, err)
|
||||
return
|
||||
}
|
||||
if stats == nil {
|
||||
@ -146,8 +140,7 @@ func (s *KubeletServer) serveStats(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
data, err := json.Marshal(stats)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Internal Error: %v", err)
|
||||
s.error(w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
@ -424,8 +424,8 @@ func TestSyncManifestsDeletes(t *testing.T) {
|
||||
|
||||
type FalseHealthChecker struct{}
|
||||
|
||||
func (f *FalseHealthChecker) IsHealthy(container api.Container) (bool, error) {
|
||||
return false, nil
|
||||
func (f *FalseHealthChecker) HealthCheck(container api.Container) (HealthCheckStatus, error) {
|
||||
return CheckUnhealthy, nil
|
||||
}
|
||||
|
||||
func TestSyncManifestsUnhealthy(t *testing.T) {
|
||||
@ -448,8 +448,7 @@ func TestSyncManifestsUnhealthy(t *testing.T) {
|
||||
ID: "foo",
|
||||
Containers: []api.Container{
|
||||
{Name: "bar",
|
||||
LivenessProbe: api.LivenessProbe{
|
||||
Enabled: true,
|
||||
LivenessProbe: &api.LivenessProbe{
|
||||
// Always returns healthy == false
|
||||
Type: "false",
|
||||
},
|
||||
|
@ -89,7 +89,7 @@ func try(selectorPiece, op string) (lhs, rhs string, ok bool) {
|
||||
|
||||
// SelectorFromSet returns a Selector which will match exactly the given Set.
|
||||
func SelectorFromSet(ls Set) Selector {
|
||||
var items []Selector
|
||||
items := make([]Selector, 0, len(ls))
|
||||
for label, value := range ls {
|
||||
items = append(items, &hasTerm{label: label, value: value})
|
||||
}
|
||||
|
@ -84,10 +84,10 @@ func (m *Master) init(cloud cloudprovider.Interface, podInfoGetter client.PodInf
|
||||
m.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
||||
podCache := NewPodCache(podInfoGetter, m.podRegistry, time.Second*30)
|
||||
go podCache.Loop()
|
||||
s := scheduler.NewFirstFitScheduler(m.podRegistry, m.random)
|
||||
s := scheduler.NewRandomFitScheduler(m.podRegistry, m.random)
|
||||
m.storage = map[string]apiserver.RESTStorage{
|
||||
"pods": registry.MakePodRegistryStorage(m.podRegistry, podInfoGetter, s, m.minionRegistry, cloud, podCache),
|
||||
"replicationControllers": registry.MakeControllerRegistryStorage(m.controllerRegistry, m.podRegistry),
|
||||
"replicationControllers": registry.NewControllerRegistryStorage(m.controllerRegistry, m.podRegistry),
|
||||
"services": registry.MakeServiceRegistryStorage(m.serviceRegistry, cloud, m.minionRegistry),
|
||||
"minions": registry.MakeMinionRegistryStorage(m.minionRegistry),
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ func (p *PodCache) GetPodInfo(host, podID string) (api.PodInfo, error) {
|
||||
defer p.podLock.Unlock()
|
||||
value, ok := p.podInfo[podID]
|
||||
if !ok {
|
||||
return nil, errors.New("No cached pod info")
|
||||
return nil, errors.New("no cached pod info")
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
@ -81,35 +81,38 @@ func (impl ConfigSourceFile) Run() {
|
||||
data, err := ioutil.ReadFile(impl.filename)
|
||||
if err != nil {
|
||||
glog.Errorf("Couldn't read file: %s : %v", impl.filename, err)
|
||||
} else {
|
||||
var config serviceConfig
|
||||
err = json.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
glog.Errorf("Couldn't unmarshal configuration from file : %s %v", data, err)
|
||||
} else {
|
||||
if !bytes.Equal(lastData, data) {
|
||||
lastData = data
|
||||
// Ok, we have a valid configuration, send to channel for
|
||||
// rejiggering.
|
||||
newServices := make([]api.Service, len(config.Services))
|
||||
newEndpoints := make([]api.Endpoints, len(config.Services))
|
||||
for i, service := range config.Services {
|
||||
newServices[i] = api.Service{JSONBase: api.JSONBase{ID: service.Name}, Port: service.Port}
|
||||
newEndpoints[i] = api.Endpoints{Name: service.Name, Endpoints: service.Endpoints}
|
||||
}
|
||||
if !reflect.DeepEqual(lastServices, newServices) {
|
||||
serviceUpdate := ServiceUpdate{Op: SET, Services: newServices}
|
||||
impl.serviceChannel <- serviceUpdate
|
||||
lastServices = newServices
|
||||
}
|
||||
if !reflect.DeepEqual(lastEndpoints, newEndpoints) {
|
||||
endpointsUpdate := EndpointsUpdate{Op: SET, Endpoints: newEndpoints}
|
||||
impl.endpointsChannel <- endpointsUpdate
|
||||
lastEndpoints = newEndpoints
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if bytes.Equal(lastData, data) {
|
||||
continue
|
||||
}
|
||||
lastData = data
|
||||
|
||||
config := &serviceConfig{}
|
||||
if err = json.Unmarshal(data, config); err != nil {
|
||||
glog.Errorf("Couldn't unmarshal configuration from file : %s %v", data, err)
|
||||
continue
|
||||
}
|
||||
// Ok, we have a valid configuration, send to channel for
|
||||
// rejiggering.
|
||||
newServices := make([]api.Service, len(config.Services))
|
||||
newEndpoints := make([]api.Endpoints, len(config.Services))
|
||||
for i, service := range config.Services {
|
||||
newServices[i] = api.Service{JSONBase: api.JSONBase{ID: service.Name}, Port: service.Port}
|
||||
newEndpoints[i] = api.Endpoints{Name: service.Name, Endpoints: service.Endpoints}
|
||||
}
|
||||
if !reflect.DeepEqual(lastServices, newServices) {
|
||||
serviceUpdate := ServiceUpdate{Op: SET, Services: newServices}
|
||||
impl.serviceChannel <- serviceUpdate
|
||||
lastServices = newServices
|
||||
}
|
||||
if !reflect.DeepEqual(lastEndpoints, newEndpoints) {
|
||||
endpointsUpdate := EndpointsUpdate{Op: SET, Endpoints: newEndpoints}
|
||||
impl.endpointsChannel <- endpointsUpdate
|
||||
lastEndpoints = newEndpoints
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ func copyBytes(in, out *net.TCPConn) {
|
||||
glog.Infof("Copying from %v <-> %v <-> %v <-> %v",
|
||||
in.RemoteAddr(), in.LocalAddr(), out.LocalAddr(), out.RemoteAddr())
|
||||
_, err := io.Copy(in, out)
|
||||
if err != nil && err != io.EOF {
|
||||
if err != nil {
|
||||
glog.Errorf("I/O error: %v", err)
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ func (proxier Proxier) AcceptHandler(service string, listener net.Listener) {
|
||||
inConn.Close()
|
||||
continue
|
||||
}
|
||||
go proxyConnection(inConn.(*net.TCPConn), outConn.(*net.TCPConn))
|
||||
proxyConnection(inConn.(*net.TCPConn), outConn.(*net.TCPConn))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
@ -59,13 +58,12 @@ func (impl LoadBalancerRR) LoadBalance(service string, srcAddr net.Addr) (string
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
// isValid returns true if spec is valid.
|
||||
func (impl LoadBalancerRR) isValid(spec string) bool {
|
||||
index := strings.Index(spec, ":")
|
||||
if index == -1 {
|
||||
_, port, err := net.SplitHostPort(spec)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
value, err := strconv.Atoi(spec[index+1:])
|
||||
value, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@ -93,9 +91,10 @@ func (impl LoadBalancerRR) OnUpdate(endpoints []api.Endpoints) {
|
||||
// First update / add all new endpoints for services.
|
||||
for _, value := range endpoints {
|
||||
existingEndpoints, exists := impl.endpointsMap[value.Name]
|
||||
if !exists || !reflect.DeepEqual(value.Endpoints, existingEndpoints) {
|
||||
validEndpoints := impl.filterValidEndpoints(value.Endpoints)
|
||||
if !exists || !reflect.DeepEqual(existingEndpoints, validEndpoints) {
|
||||
glog.Infof("LoadBalancerRR: Setting endpoints for %s to %+v", value.Name, value.Endpoints)
|
||||
impl.endpointsMap[value.Name] = impl.filterValidEndpoints(value.Endpoints)
|
||||
impl.endpointsMap[value.Name] = validEndpoints
|
||||
// Start RR from the beginning if added or updated.
|
||||
impl.rrIndex[value.Name] = 0
|
||||
}
|
||||
|
@ -20,12 +20,13 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
// Implementation of RESTStorage for the api server.
|
||||
// ControllerRegistryStorage is an implementation of RESTStorage for the api server.
|
||||
type ControllerRegistryStorage struct {
|
||||
registry ControllerRegistry
|
||||
podRegistry PodRegistry
|
||||
@ -33,7 +34,7 @@ type ControllerRegistryStorage struct {
|
||||
pollPeriod time.Duration
|
||||
}
|
||||
|
||||
func MakeControllerRegistryStorage(registry ControllerRegistry, podRegistry PodRegistry) apiserver.RESTStorage {
|
||||
func NewControllerRegistryStorage(registry ControllerRegistry, podRegistry PodRegistry) apiserver.RESTStorage {
|
||||
return &ControllerRegistryStorage{
|
||||
registry: registry,
|
||||
podRegistry: podRegistry,
|
||||
@ -41,6 +42,7 @@ func MakeControllerRegistryStorage(registry ControllerRegistry, podRegistry PodR
|
||||
}
|
||||
}
|
||||
|
||||
// List obtains a list of ReplicationControllers that match selector.
|
||||
func (storage *ControllerRegistryStorage) List(selector labels.Selector) (interface{}, error) {
|
||||
result := api.ReplicationControllerList{}
|
||||
controllers, err := storage.registry.ListControllers()
|
||||
@ -54,6 +56,7 @@ func (storage *ControllerRegistryStorage) List(selector labels.Selector) (interf
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Get obtains the ReplicationController specified by its id.
|
||||
func (storage *ControllerRegistryStorage) Get(id string) (interface{}, error) {
|
||||
controller, err := storage.registry.GetController(id)
|
||||
if err != nil {
|
||||
@ -62,26 +65,32 @@ func (storage *ControllerRegistryStorage) Get(id string) (interface{}, error) {
|
||||
return controller, err
|
||||
}
|
||||
|
||||
// Delete asynchronously deletes the ReplicationController specified by its id.
|
||||
func (storage *ControllerRegistryStorage) Delete(id string) (<-chan interface{}, error) {
|
||||
return apiserver.MakeAsync(func() (interface{}, error) {
|
||||
return api.Status{Status: api.StatusSuccess}, storage.registry.DeleteController(id)
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Extract deserializes user provided data into an api.ReplicationController.
|
||||
func (storage *ControllerRegistryStorage) Extract(body []byte) (interface{}, error) {
|
||||
result := api.ReplicationController{}
|
||||
err := api.DecodeInto(body, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Create registers a given new ReplicationController instance to storage.registry.
|
||||
func (storage *ControllerRegistryStorage) Create(obj interface{}) (<-chan interface{}, error) {
|
||||
controller, ok := obj.(api.ReplicationController)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("not a replication controller: %#v", obj)
|
||||
}
|
||||
if controller.ID == "" {
|
||||
return nil, fmt.Errorf("ID should not be empty: %#v", controller)
|
||||
if len(controller.ID) == 0 {
|
||||
controller.ID = uuid.NewUUID().String()
|
||||
}
|
||||
// Pod Manifest ID should be assigned by the pod API
|
||||
controller.DesiredState.PodTemplate.DesiredState.Manifest.ID = ""
|
||||
|
||||
return apiserver.MakeAsync(func() (interface{}, error) {
|
||||
err := storage.registry.CreateController(controller)
|
||||
if err != nil {
|
||||
@ -91,12 +100,13 @@ func (storage *ControllerRegistryStorage) Create(obj interface{}) (<-chan interf
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Update replaces a given ReplicationController instance with an existing instance in storage.registry.
|
||||
func (storage *ControllerRegistryStorage) Update(obj interface{}) (<-chan interface{}, error) {
|
||||
controller, ok := obj.(api.ReplicationController)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("not a replication controller: %#v", obj)
|
||||
}
|
||||
if controller.ID == "" {
|
||||
if len(controller.ID) == 0 {
|
||||
return nil, fmt.Errorf("ID should not be empty: %#v", controller)
|
||||
}
|
||||
return apiserver.MakeAsync(func() (interface{}, error) {
|
||||
|
@ -17,11 +17,13 @@ limitations under the License.
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
@ -37,6 +39,26 @@ type EndpointController struct {
|
||||
podRegistry PodRegistry
|
||||
}
|
||||
|
||||
func findPort(manifest *api.ContainerManifest, portName util.IntOrString) (int, error) {
|
||||
if ((portName.Kind == util.IntstrString && len(portName.StrVal) == 0) ||
|
||||
(portName.Kind == util.IntstrInt && portName.IntVal == 0)) &&
|
||||
len(manifest.Containers[0].Ports) > 0 {
|
||||
return manifest.Containers[0].Ports[0].ContainerPort, nil
|
||||
}
|
||||
if portName.Kind == util.IntstrInt {
|
||||
return portName.IntVal, nil
|
||||
}
|
||||
name := portName.StrVal
|
||||
for _, container := range manifest.Containers {
|
||||
for _, port := range container.Ports {
|
||||
if port.Name == name {
|
||||
return port.ContainerPort, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1, fmt.Errorf("no suitable port for manifest: %s", manifest.ID)
|
||||
}
|
||||
|
||||
func (e *EndpointController) SyncServiceEndpoints() error {
|
||||
services, err := e.serviceRegistry.ListServices()
|
||||
if err != nil {
|
||||
@ -52,11 +74,12 @@ func (e *EndpointController) SyncServiceEndpoints() error {
|
||||
}
|
||||
endpoints := make([]string, len(pods))
|
||||
for ix, pod := range pods {
|
||||
// TODO: Use port names in the service object, don't just use port #0
|
||||
endpoints[ix] = net.JoinHostPort(
|
||||
pod.CurrentState.Host,
|
||||
strconv.Itoa(pod.DesiredState.Manifest.Containers[0].Ports[0].HostPort),
|
||||
)
|
||||
port, err := findPort(&pod.DesiredState.Manifest, service.ContainerPort)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to find port for service: %v, %v", service, err)
|
||||
continue
|
||||
}
|
||||
endpoints[ix] = net.JoinHostPort(pod.CurrentState.PodIP, strconv.Itoa(port))
|
||||
}
|
||||
err = e.serviceRegistry.UpdateEndpoints(api.Endpoints{
|
||||
Name: service.ID,
|
||||
|
@ -21,8 +21,62 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func TestFindPort(t *testing.T) {
|
||||
manifest := api.ContainerManifest{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Ports: []api.Port{
|
||||
{
|
||||
Name: "foo",
|
||||
ContainerPort: 8080,
|
||||
HostPort: 9090,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
ContainerPort: 8000,
|
||||
HostPort: 9000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
port, err := findPort(&manifest, util.IntOrString{Kind: util.IntstrString, StrVal: "foo"})
|
||||
expectNoError(t, err)
|
||||
if port != 8080 {
|
||||
t.Errorf("Expected 8080, Got %d", port)
|
||||
}
|
||||
port, err = findPort(&manifest, util.IntOrString{Kind: util.IntstrString, StrVal: "bar"})
|
||||
expectNoError(t, err)
|
||||
if port != 8000 {
|
||||
t.Errorf("Expected 8000, Got %d", port)
|
||||
}
|
||||
port, err = findPort(&manifest, util.IntOrString{Kind: util.IntstrInt, IntVal: 8000})
|
||||
if port != 8000 {
|
||||
t.Errorf("Expected 8000, Got %d", port)
|
||||
}
|
||||
port, err = findPort(&manifest, util.IntOrString{Kind: util.IntstrInt, IntVal: 7000})
|
||||
if port != 7000 {
|
||||
t.Errorf("Expected 7000, Got %d", port)
|
||||
}
|
||||
port, err = findPort(&manifest, util.IntOrString{Kind: util.IntstrString, StrVal: "baz"})
|
||||
if err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
}
|
||||
port, err = findPort(&manifest, util.IntOrString{Kind: util.IntstrString, StrVal: ""})
|
||||
expectNoError(t, err)
|
||||
if port != 8080 {
|
||||
t.Errorf("Expected 8080, Got %d", port)
|
||||
}
|
||||
port, err = findPort(&manifest, util.IntOrString{})
|
||||
expectNoError(t, err)
|
||||
if port != 8080 {
|
||||
t.Errorf("Expected 8080, Got %d", port)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncEndpointsEmpty(t *testing.T) {
|
||||
serviceRegistry := MockServiceRegistry{}
|
||||
podRegistry := MockPodRegistry{}
|
||||
|
@ -58,6 +58,7 @@ func (registry *EtcdRegistry) helper() *tools.EtcdHelper {
|
||||
return &tools.EtcdHelper{registry.etcdClient}
|
||||
}
|
||||
|
||||
// ListPods obtains a list of pods that match selector.
|
||||
func (registry *EtcdRegistry) ListPods(selector labels.Selector) ([]api.Pod, error) {
|
||||
pods := []api.Pod{}
|
||||
machines, err := registry.machines.List()
|
||||
@ -80,6 +81,7 @@ func (registry *EtcdRegistry) ListPods(selector labels.Selector) ([]api.Pod, err
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
// GetPod gets a specific pod specified by its ID.
|
||||
func (registry *EtcdRegistry) GetPod(podID string) (*api.Pod, error) {
|
||||
pod, _, err := registry.findPod(podID)
|
||||
return &pod, err
|
||||
@ -89,6 +91,7 @@ func makeContainerKey(machine string) string {
|
||||
return "/registry/hosts/" + machine + "/kubelet"
|
||||
}
|
||||
|
||||
// CreatePod creates a pod based on a specification, schedule it onto a specific machine.
|
||||
func (registry *EtcdRegistry) CreatePod(machineIn string, pod api.Pod) error {
|
||||
podOut, machine, err := registry.findPod(pod.ID)
|
||||
if err == nil {
|
||||
@ -126,6 +129,7 @@ func (registry *EtcdRegistry) UpdatePod(pod api.Pod) error {
|
||||
return fmt.Errorf("unimplemented!")
|
||||
}
|
||||
|
||||
// DeletePod deletes an existing pod specified by its ID.
|
||||
func (registry *EtcdRegistry) DeletePod(podID string) error {
|
||||
_, machine, err := registry.findPod(podID)
|
||||
if err != nil {
|
||||
@ -190,6 +194,7 @@ func (registry *EtcdRegistry) findPod(podID string) (api.Pod, string, error) {
|
||||
return api.Pod{}, "", fmt.Errorf("pod not found %s", podID)
|
||||
}
|
||||
|
||||
// ListControllers obtains a list of ReplicationControllers.
|
||||
func (registry *EtcdRegistry) ListControllers() ([]api.ReplicationController, error) {
|
||||
var controllers []api.ReplicationController
|
||||
err := registry.helper().ExtractList("/registry/controllers", &controllers)
|
||||
@ -200,6 +205,7 @@ func makeControllerKey(id string) string {
|
||||
return "/registry/controllers/" + id
|
||||
}
|
||||
|
||||
// GetController gets a specific ReplicationController specified by its ID.
|
||||
func (registry *EtcdRegistry) GetController(controllerID string) (*api.ReplicationController, error) {
|
||||
var controller api.ReplicationController
|
||||
key := makeControllerKey(controllerID)
|
||||
@ -210,15 +216,18 @@ func (registry *EtcdRegistry) GetController(controllerID string) (*api.Replicati
|
||||
return &controller, nil
|
||||
}
|
||||
|
||||
// CreateController creates a new ReplicationController.
|
||||
func (registry *EtcdRegistry) CreateController(controller api.ReplicationController) error {
|
||||
// TODO : check for existence here and error.
|
||||
return registry.UpdateController(controller)
|
||||
}
|
||||
|
||||
// UpdateController replaces an existing ReplicationController.
|
||||
func (registry *EtcdRegistry) UpdateController(controller api.ReplicationController) error {
|
||||
return registry.helper().SetObj(makeControllerKey(controller.ID), controller)
|
||||
}
|
||||
|
||||
// DeleteController deletes a ReplicationController specified by its ID.
|
||||
func (registry *EtcdRegistry) DeleteController(controllerID string) error {
|
||||
key := makeControllerKey(controllerID)
|
||||
_, err := registry.etcdClient.Delete(key, false)
|
||||
@ -229,16 +238,19 @@ func makeServiceKey(name string) string {
|
||||
return "/registry/services/specs/" + name
|
||||
}
|
||||
|
||||
// ListServices obtains a list of Services.
|
||||
func (registry *EtcdRegistry) ListServices() (api.ServiceList, error) {
|
||||
var list api.ServiceList
|
||||
err := registry.helper().ExtractList("/registry/services/specs", &list.Items)
|
||||
return list, err
|
||||
}
|
||||
|
||||
// CreateService creates a new Service.
|
||||
func (registry *EtcdRegistry) CreateService(svc api.Service) error {
|
||||
return registry.helper().SetObj(makeServiceKey(svc.ID), svc)
|
||||
}
|
||||
|
||||
// GetService obtains a Service specified by its name.
|
||||
func (registry *EtcdRegistry) GetService(name string) (*api.Service, error) {
|
||||
key := makeServiceKey(name)
|
||||
var svc api.Service
|
||||
@ -249,6 +261,7 @@ func (registry *EtcdRegistry) GetService(name string) (*api.Service, error) {
|
||||
return &svc, nil
|
||||
}
|
||||
|
||||
// DeleteService deletes a Service specified by its name.
|
||||
func (registry *EtcdRegistry) DeleteService(name string) error {
|
||||
key := makeServiceKey(name)
|
||||
_, err := registry.etcdClient.Delete(key, true)
|
||||
@ -260,10 +273,13 @@ func (registry *EtcdRegistry) DeleteService(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateService replaces an existing Service.
|
||||
func (registry *EtcdRegistry) UpdateService(svc api.Service) error {
|
||||
// TODO : check for existence here and error.
|
||||
return registry.CreateService(svc)
|
||||
}
|
||||
|
||||
// UpdateEndpoints update Endpoints of a Service.
|
||||
func (registry *EtcdRegistry) UpdateEndpoints(e api.Endpoints) error {
|
||||
return registry.helper().SetObj("/registry/services/endpoints/"+e.Name, e)
|
||||
}
|
||||
|
@ -35,13 +35,13 @@ type PodRegistry interface {
|
||||
DeletePod(podID string) error
|
||||
}
|
||||
|
||||
// ControllerRegistry is an interface for things that know how to store Controllers.
|
||||
// ControllerRegistry is an interface for things that know how to store ReplicationControllers.
|
||||
type ControllerRegistry interface {
|
||||
ListControllers() ([]api.ReplicationController, error)
|
||||
GetController(controllerId string) (*api.ReplicationController, error)
|
||||
GetController(controllerID string) (*api.ReplicationController, error)
|
||||
CreateController(controller api.ReplicationController) error
|
||||
UpdateController(controller api.ReplicationController) error
|
||||
DeleteController(controllerId string) error
|
||||
DeleteController(controllerID string) error
|
||||
}
|
||||
|
||||
// ServiceRegistry is an interface for things that know how to store services.
|
||||
|
@ -17,9 +17,11 @@ limitations under the License.
|
||||
package registry
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func TestMakeManifestNoServices(t *testing.T) {
|
||||
@ -59,6 +61,10 @@ func TestMakeManifestServices(t *testing.T) {
|
||||
{
|
||||
JSONBase: api.JSONBase{ID: "test"},
|
||||
Port: 8080,
|
||||
ContainerPort: util.IntOrString{
|
||||
Kind: util.IntstrInt,
|
||||
IntVal: 900,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -80,12 +86,44 @@ func TestMakeManifestServices(t *testing.T) {
|
||||
})
|
||||
expectNoError(t, err)
|
||||
container := manifest.Containers[0]
|
||||
if len(container.Env) != 2 ||
|
||||
container.Env[0].Name != "TEST_SERVICE_PORT" ||
|
||||
container.Env[0].Value != "8080" ||
|
||||
container.Env[1].Name != "SERVICE_HOST" ||
|
||||
container.Env[1].Value != "machine" {
|
||||
t.Errorf("Expected 2 env vars, got: %#v", manifest)
|
||||
envs := []api.EnvVar{
|
||||
{
|
||||
Name: "TEST_SERVICE_PORT",
|
||||
Value: "8080",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT",
|
||||
Value: "tcp://machine:8080",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_900_TCP",
|
||||
Value: "tcp://machine:8080",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_900_TCP_PROTO",
|
||||
Value: "tcp",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_900_TCP_PORT",
|
||||
Value: "8080",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_900_TCP_ADDR",
|
||||
Value: "machine",
|
||||
},
|
||||
{
|
||||
Name: "SERVICE_HOST",
|
||||
Value: "machine",
|
||||
},
|
||||
}
|
||||
if len(container.Env) != 7 {
|
||||
t.Errorf("Expected 7 env vars, got %d: %#v", len(container.Env), manifest)
|
||||
return
|
||||
}
|
||||
for ix := range container.Env {
|
||||
if !reflect.DeepEqual(envs[ix], container.Env[ix]) {
|
||||
t.Errorf("expected %#v, got %#v", envs[ix], container.Env[ix])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,6 +134,10 @@ func TestMakeManifestServicesExistingEnvVar(t *testing.T) {
|
||||
{
|
||||
JSONBase: api.JSONBase{ID: "test"},
|
||||
Port: 8080,
|
||||
ContainerPort: util.IntOrString{
|
||||
Kind: util.IntstrInt,
|
||||
IntVal: 900,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -122,13 +164,48 @@ func TestMakeManifestServicesExistingEnvVar(t *testing.T) {
|
||||
})
|
||||
expectNoError(t, err)
|
||||
container := manifest.Containers[0]
|
||||
if len(container.Env) != 3 ||
|
||||
container.Env[0].Name != "foo" ||
|
||||
container.Env[0].Value != "bar" ||
|
||||
container.Env[1].Name != "TEST_SERVICE_PORT" ||
|
||||
container.Env[1].Value != "8080" ||
|
||||
container.Env[2].Name != "SERVICE_HOST" ||
|
||||
container.Env[2].Value != "machine" {
|
||||
t.Errorf("Expected no env vars, got: %#v", manifest)
|
||||
|
||||
envs := []api.EnvVar{
|
||||
{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
},
|
||||
{
|
||||
Name: "TEST_SERVICE_PORT",
|
||||
Value: "8080",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT",
|
||||
Value: "tcp://machine:8080",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_900_TCP",
|
||||
Value: "tcp://machine:8080",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_900_TCP_PROTO",
|
||||
Value: "tcp",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_900_TCP_PORT",
|
||||
Value: "8080",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_900_TCP_ADDR",
|
||||
Value: "machine",
|
||||
},
|
||||
{
|
||||
Name: "SERVICE_HOST",
|
||||
Value: "machine",
|
||||
},
|
||||
}
|
||||
if len(container.Env) != 8 {
|
||||
t.Errorf("Expected 8 env vars, got: %#v", manifest)
|
||||
return
|
||||
}
|
||||
for ix := range container.Env {
|
||||
if !reflect.DeepEqual(envs[ix], container.Env[ix]) {
|
||||
t.Errorf("expected %#v, got %#v", envs[ix], container.Env[ix])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,8 +93,8 @@ func (registry *MemoryRegistry) CreateController(controller api.ReplicationContr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (registry *MemoryRegistry) DeleteController(controllerId string) error {
|
||||
delete(registry.controllerData, controllerId)
|
||||
func (registry *MemoryRegistry) DeleteController(controllerID string) error {
|
||||
delete(registry.controllerData, controllerID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
@ -85,8 +86,14 @@ func (storage *PodRegistryStorage) fillPodInfo(pod *api.Pod) {
|
||||
if storage.podCache != nil {
|
||||
info, err := storage.podCache.GetPodInfo(pod.CurrentState.Host, pod.ID)
|
||||
if err != nil {
|
||||
glog.Errorf("Error getting container info: %#v", err)
|
||||
return
|
||||
glog.Errorf("Error getting container info from cache: %#v", err)
|
||||
if storage.podInfoGetter != nil {
|
||||
info, err = storage.podInfoGetter.GetPodInfo(pod.CurrentState.Host, pod.ID)
|
||||
}
|
||||
if err != nil {
|
||||
glog.Errorf("Error getting fresh container info: %#v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
pod.CurrentState.Info = info
|
||||
netContainerInfo, ok := info["net"]
|
||||
@ -185,8 +192,9 @@ func (storage *PodRegistryStorage) Extract(body []byte) (interface{}, error) {
|
||||
func (storage *PodRegistryStorage) Create(obj interface{}) (<-chan interface{}, error) {
|
||||
pod := obj.(api.Pod)
|
||||
if len(pod.ID) == 0 {
|
||||
return nil, fmt.Errorf("id is unspecified: %#v", pod)
|
||||
pod.ID = uuid.NewUUID().String()
|
||||
}
|
||||
pod.DesiredState.Manifest.ID = pod.ID
|
||||
|
||||
return apiserver.MakeAsync(func() (interface{}, error) {
|
||||
// TODO(lavalamp): Separate scheduler more cleanly.
|
||||
@ -205,7 +213,7 @@ func (storage *PodRegistryStorage) Create(obj interface{}) (<-chan interface{},
|
||||
func (storage *PodRegistryStorage) Update(obj interface{}) (<-chan interface{}, error) {
|
||||
pod := obj.(api.Pod)
|
||||
if len(pod.ID) == 0 {
|
||||
return nil, fmt.Errorf("id is unspecified: %#v", pod)
|
||||
return nil, fmt.Errorf("ID should not be empty: %#v", pod)
|
||||
}
|
||||
|
||||
return apiserver.MakeAsync(func() (interface{}, error) {
|
||||
|
@ -35,6 +35,104 @@ func expectNoError(t *testing.T, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func expectApiStatusError(t *testing.T, ch <-chan interface{}, msg string) {
|
||||
out := <-ch
|
||||
status, ok := out.(*api.Status)
|
||||
if !ok {
|
||||
t.Errorf("Expected an api.Status object, was %#v", out)
|
||||
return
|
||||
}
|
||||
if msg != status.Details {
|
||||
t.Errorf("Expected %#v, was %s", msg, status.Details)
|
||||
}
|
||||
}
|
||||
|
||||
func expectPod(t *testing.T, ch <-chan interface{}) (*api.Pod, bool) {
|
||||
out := <-ch
|
||||
pod, ok := out.(*api.Pod)
|
||||
if !ok || pod == nil {
|
||||
t.Errorf("Expected an api.Pod object, was %#v", out)
|
||||
return nil, false
|
||||
}
|
||||
return pod, true
|
||||
}
|
||||
|
||||
func TestCreatePodRegistryError(t *testing.T) {
|
||||
mockRegistry := &MockPodRegistry{
|
||||
err: fmt.Errorf("test error"),
|
||||
}
|
||||
storage := PodRegistryStorage{
|
||||
scheduler: &MockScheduler{},
|
||||
registry: mockRegistry,
|
||||
}
|
||||
pod := api.Pod{}
|
||||
ch, err := storage.Create(pod)
|
||||
if err != nil {
|
||||
t.Errorf("Expected %#v, Got %#v", nil, err)
|
||||
}
|
||||
expectApiStatusError(t, ch, mockRegistry.err.Error())
|
||||
}
|
||||
|
||||
type MockScheduler struct {
|
||||
err error
|
||||
pod api.Pod
|
||||
machine string
|
||||
}
|
||||
|
||||
func (m *MockScheduler) Schedule(pod api.Pod, lister scheduler.MinionLister) (string, error) {
|
||||
m.pod = pod
|
||||
return m.machine, m.err
|
||||
}
|
||||
|
||||
func TestCreatePodSchedulerError(t *testing.T) {
|
||||
mockScheduler := MockScheduler{
|
||||
err: fmt.Errorf("test error"),
|
||||
}
|
||||
storage := PodRegistryStorage{
|
||||
scheduler: &mockScheduler,
|
||||
}
|
||||
pod := api.Pod{}
|
||||
ch, err := storage.Create(pod)
|
||||
if err != nil {
|
||||
t.Errorf("Expected %#v, Got %#v", nil, err)
|
||||
}
|
||||
expectApiStatusError(t, ch, mockScheduler.err.Error())
|
||||
}
|
||||
|
||||
type MockPodStorageRegistry struct {
|
||||
MockPodRegistry
|
||||
machine string
|
||||
}
|
||||
|
||||
func (r *MockPodStorageRegistry) CreatePod(machine string, pod api.Pod) error {
|
||||
r.MockPodRegistry.pod = &pod
|
||||
r.machine = machine
|
||||
return r.MockPodRegistry.err
|
||||
}
|
||||
|
||||
func TestCreatePodSetsIds(t *testing.T) {
|
||||
mockRegistry := &MockPodStorageRegistry{
|
||||
MockPodRegistry: MockPodRegistry{err: fmt.Errorf("test error")},
|
||||
}
|
||||
storage := PodRegistryStorage{
|
||||
scheduler: &MockScheduler{machine: "test"},
|
||||
registry: mockRegistry,
|
||||
}
|
||||
pod := api.Pod{}
|
||||
ch, err := storage.Create(pod)
|
||||
if err != nil {
|
||||
t.Errorf("Expected %#v, Got %#v", nil, err)
|
||||
}
|
||||
expectApiStatusError(t, ch, mockRegistry.err.Error())
|
||||
|
||||
if len(mockRegistry.MockPodRegistry.pod.ID) == 0 {
|
||||
t.Errorf("Expected pod ID to be set, Got %#v", pod)
|
||||
}
|
||||
if mockRegistry.MockPodRegistry.pod.DesiredState.Manifest.ID != mockRegistry.MockPodRegistry.pod.ID {
|
||||
t.Errorf("Expected manifest ID to be equal to pod ID, Got %#v", pod)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPodsError(t *testing.T) {
|
||||
mockRegistry := MockPodRegistry{
|
||||
err: fmt.Errorf("test error"),
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
type ServiceRegistryStorage struct {
|
||||
@ -41,6 +42,39 @@ func MakeServiceRegistryStorage(registry ServiceRegistry, cloud cloudprovider.In
|
||||
}
|
||||
}
|
||||
|
||||
func makeLinkVariables(service api.Service, machine string) []api.EnvVar {
|
||||
prefix := strings.ToUpper(service.ID)
|
||||
var port string
|
||||
if service.ContainerPort.Kind == util.IntstrString {
|
||||
port = service.ContainerPort.StrVal
|
||||
} else {
|
||||
port = strconv.Itoa(service.ContainerPort.IntVal)
|
||||
}
|
||||
portPrefix := prefix + "_PORT_" + port + "_TCP"
|
||||
return []api.EnvVar{
|
||||
{
|
||||
Name: prefix + "_PORT",
|
||||
Value: fmt.Sprintf("tcp://%s:%d", machine, service.Port),
|
||||
},
|
||||
{
|
||||
Name: portPrefix,
|
||||
Value: fmt.Sprintf("tcp://%s:%d", machine, service.Port),
|
||||
},
|
||||
{
|
||||
Name: portPrefix + "_PROTO",
|
||||
Value: "tcp",
|
||||
},
|
||||
{
|
||||
Name: portPrefix + "_PORT",
|
||||
Value: strconv.Itoa(service.Port),
|
||||
},
|
||||
{
|
||||
Name: portPrefix + "_ADDR",
|
||||
Value: machine,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetServiceEnvironmentVariables populates a list of environment variables that are use
|
||||
// in the container environment to get access to services.
|
||||
func GetServiceEnvironmentVariables(registry ServiceRegistry, machine string) ([]api.EnvVar, error) {
|
||||
@ -53,6 +87,7 @@ func GetServiceEnvironmentVariables(registry ServiceRegistry, machine string) ([
|
||||
name := strings.ToUpper(service.ID) + "_SERVICE_PORT"
|
||||
value := strconv.Itoa(service.Port)
|
||||
result = append(result, api.EnvVar{Name: name, Value: value})
|
||||
result = append(result, makeLinkVariables(service, machine)...)
|
||||
}
|
||||
result = append(result, api.EnvVar{Name: "SERVICE_HOST", Value: machine})
|
||||
return result, nil
|
||||
@ -112,8 +147,9 @@ func (sr *ServiceRegistryStorage) Extract(body []byte) (interface{}, error) {
|
||||
|
||||
func (sr *ServiceRegistryStorage) Create(obj interface{}) (<-chan interface{}, error) {
|
||||
srv := obj.(api.Service)
|
||||
if srv.ID == "" {
|
||||
return nil, fmt.Errorf("ID should not be empty: %#v", srv)
|
||||
errs := api.ValidateService(&srv)
|
||||
if len(errs) > 0 {
|
||||
return nil, fmt.Errorf("Validation errors: %v", errs)
|
||||
}
|
||||
return apiserver.MakeAsync(func() (interface{}, error) {
|
||||
// TODO: Consider moving this to a rectification loop, so that we make/remove external load balancers
|
||||
|
@ -33,6 +33,7 @@ func TestServiceRegistry(t *testing.T) {
|
||||
|
||||
svc := api.Service{
|
||||
JSONBase: api.JSONBase{ID: "foo"},
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
}
|
||||
c, _ := storage.Create(svc)
|
||||
<-c
|
||||
@ -56,6 +57,7 @@ func TestServiceRegistryExternalService(t *testing.T) {
|
||||
|
||||
svc := api.Service{
|
||||
JSONBase: api.JSONBase{ID: "foo"},
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
CreateExternalLoadBalancer: true,
|
||||
}
|
||||
c, _ := storage.Create(svc)
|
||||
@ -82,6 +84,7 @@ func TestServiceRegistryExternalServiceError(t *testing.T) {
|
||||
|
||||
svc := api.Service{
|
||||
JSONBase: api.JSONBase{ID: "foo"},
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
CreateExternalLoadBalancer: true,
|
||||
}
|
||||
c, _ := storage.Create(svc)
|
||||
@ -106,6 +109,7 @@ func TestServiceRegistryDelete(t *testing.T) {
|
||||
|
||||
svc := api.Service{
|
||||
JSONBase: api.JSONBase{ID: "foo"},
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
}
|
||||
memory.CreateService(svc)
|
||||
|
||||
@ -131,6 +135,7 @@ func TestServiceRegistryDeleteExternal(t *testing.T) {
|
||||
|
||||
svc := api.Service{
|
||||
JSONBase: api.JSONBase{ID: "foo"},
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
CreateExternalLoadBalancer: true,
|
||||
}
|
||||
memory.CreateService(svc)
|
||||
|
@ -21,27 +21,28 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
// Anything that can list minions for a scheduler.
|
||||
// MinionLister interface represents anything that can list minions for a scheduler.
|
||||
type MinionLister interface {
|
||||
List() (machines []string, err error)
|
||||
}
|
||||
|
||||
// Make a MinionLister from a []string
|
||||
// FakeMinionLister implements MinionLister on a []string for test purposes.
|
||||
type FakeMinionLister []string
|
||||
|
||||
// Returns minions as a []string
|
||||
// List returns minions as a []string
|
||||
func (f FakeMinionLister) List() ([]string, error) {
|
||||
return []string(f), nil
|
||||
}
|
||||
|
||||
// Anything that can list pods for a scheduler
|
||||
// PodLister interface represents anything that can list pods for a scheduler
|
||||
type PodLister interface {
|
||||
ListPods(labels.Selector) ([]api.Pod, error)
|
||||
}
|
||||
|
||||
// Make a MinionLister from an []api.Pods
|
||||
// FakePodLister implements PodLister on an []api.Pods for test purposes.
|
||||
type FakePodLister []api.Pod
|
||||
|
||||
// ListPods returns []api.Pod matching a query.
|
||||
func (f FakePodLister) ListPods(s labels.Selector) (selected []api.Pod, err error) {
|
||||
for _, pod := range f {
|
||||
if s.Matches(labels.Set(pod.Labels)) {
|
||||
|
@ -18,14 +18,15 @@ package scheduler
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
// RandomScheduler choses machines uniformly at random.
|
||||
// RandomScheduler chooses machines uniformly at random.
|
||||
type RandomScheduler struct {
|
||||
// TODO: rand.Rand is *NOT* thread safe.
|
||||
random *rand.Rand
|
||||
random *rand.Rand
|
||||
randomLock sync.Mutex
|
||||
}
|
||||
|
||||
func MakeRandomScheduler(random *rand.Rand) Scheduler {
|
||||
@ -34,10 +35,14 @@ func MakeRandomScheduler(random *rand.Rand) Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule schedules a given pod to a random machine.
|
||||
func (s *RandomScheduler) Schedule(pod api.Pod, minionLister MinionLister) (string, error) {
|
||||
machines, err := minionLister.List()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
s.randomLock.Lock()
|
||||
defer s.randomLock.Unlock()
|
||||
return machines[s.random.Int()%len(machines)], nil
|
||||
}
|
||||
|
@ -19,25 +19,27 @@ package scheduler
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
type FirstFitScheduler struct {
|
||||
podLister PodLister
|
||||
// TODO: *rand.Rand is *not* threadsafe
|
||||
random *rand.Rand
|
||||
// RandomFitScheduler is a Scheduler which schedules a Pod on a random machine which matches its requirement.
|
||||
type RandomFitScheduler struct {
|
||||
podLister PodLister
|
||||
random *rand.Rand
|
||||
randomLock sync.Mutex
|
||||
}
|
||||
|
||||
func NewFirstFitScheduler(podLister PodLister, random *rand.Rand) Scheduler {
|
||||
return &FirstFitScheduler{
|
||||
func NewRandomFitScheduler(podLister PodLister, random *rand.Rand) Scheduler {
|
||||
return &RandomFitScheduler{
|
||||
podLister: podLister,
|
||||
random: random,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FirstFitScheduler) containsPort(pod api.Pod, port api.Port) bool {
|
||||
func (s *RandomFitScheduler) containsPort(pod api.Pod, port api.Port) bool {
|
||||
for _, container := range pod.DesiredState.Manifest.Containers {
|
||||
for _, podPort := range container.Ports {
|
||||
if podPort.HostPort == port.HostPort {
|
||||
@ -48,7 +50,8 @@ func (s *FirstFitScheduler) containsPort(pod api.Pod, port api.Port) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *FirstFitScheduler) Schedule(pod api.Pod, minionLister MinionLister) (string, error) {
|
||||
// Schedule schedules a pod on a random machine which matches its requirement.
|
||||
func (s *RandomFitScheduler) Schedule(pod api.Pod, minionLister MinionLister) (string, error) {
|
||||
machines, err := minionLister.List()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -80,7 +83,8 @@ func (s *FirstFitScheduler) Schedule(pod api.Pod, minionLister MinionLister) (st
|
||||
}
|
||||
if len(machineOptions) == 0 {
|
||||
return "", fmt.Errorf("failed to find fit for %#v", pod)
|
||||
} else {
|
||||
return machineOptions[s.random.Int()%len(machineOptions)], nil
|
||||
}
|
||||
s.randomLock.Lock()
|
||||
defer s.randomLock.Unlock()
|
||||
return machineOptions[s.random.Int()%len(machineOptions)], nil
|
||||
}
|
@ -23,31 +23,31 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
func TestFirstFitSchedulerNothingScheduled(t *testing.T) {
|
||||
func TestRandomFitSchedulerNothingScheduled(t *testing.T) {
|
||||
fakeRegistry := FakePodLister{}
|
||||
r := rand.New(rand.NewSource(0))
|
||||
st := schedulerTester{
|
||||
t: t,
|
||||
scheduler: NewFirstFitScheduler(&fakeRegistry, r),
|
||||
scheduler: NewRandomFitScheduler(&fakeRegistry, r),
|
||||
minionLister: FakeMinionLister{"m1", "m2", "m3"},
|
||||
}
|
||||
st.expectSchedule(api.Pod{}, "m3")
|
||||
}
|
||||
|
||||
func TestFirstFitSchedulerFirstScheduled(t *testing.T) {
|
||||
func TestRandomFitSchedulerFirstScheduled(t *testing.T) {
|
||||
fakeRegistry := FakePodLister{
|
||||
makePod("m1", 8080),
|
||||
}
|
||||
r := rand.New(rand.NewSource(0))
|
||||
st := schedulerTester{
|
||||
t: t,
|
||||
scheduler: NewFirstFitScheduler(fakeRegistry, r),
|
||||
scheduler: NewRandomFitScheduler(fakeRegistry, r),
|
||||
minionLister: FakeMinionLister{"m1", "m2", "m3"},
|
||||
}
|
||||
st.expectSchedule(makePod("", 8080), "m3")
|
||||
}
|
||||
|
||||
func TestFirstFitSchedulerFirstScheduledComplicated(t *testing.T) {
|
||||
func TestRandomFitSchedulerFirstScheduledComplicated(t *testing.T) {
|
||||
fakeRegistry := FakePodLister{
|
||||
makePod("m1", 80, 8080),
|
||||
makePod("m2", 8081, 8082, 8083),
|
||||
@ -56,13 +56,13 @@ func TestFirstFitSchedulerFirstScheduledComplicated(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(0))
|
||||
st := schedulerTester{
|
||||
t: t,
|
||||
scheduler: NewFirstFitScheduler(fakeRegistry, r),
|
||||
scheduler: NewRandomFitScheduler(fakeRegistry, r),
|
||||
minionLister: FakeMinionLister{"m1", "m2", "m3"},
|
||||
}
|
||||
st.expectSchedule(makePod("", 8080, 8081), "m3")
|
||||
}
|
||||
|
||||
func TestFirstFitSchedulerFirstScheduledImpossible(t *testing.T) {
|
||||
func TestRandomFitSchedulerFirstScheduledImpossible(t *testing.T) {
|
||||
fakeRegistry := FakePodLister{
|
||||
makePod("m1", 8080),
|
||||
makePod("m2", 8081),
|
||||
@ -71,7 +71,7 @@ func TestFirstFitSchedulerFirstScheduledImpossible(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(0))
|
||||
st := schedulerTester{
|
||||
t: t,
|
||||
scheduler: NewFirstFitScheduler(fakeRegistry, r),
|
||||
scheduler: NewRandomFitScheduler(fakeRegistry, r),
|
||||
minionLister: FakeMinionLister{"m1", "m2", "m3"},
|
||||
}
|
||||
st.expectFailure(makePod("", 8080, 8081))
|
@ -31,6 +31,7 @@ func MakeRoundRobinScheduler() Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule schedules a pod on the machine next to the last scheduled machine.
|
||||
func (s *RoundRobinScheduler) Schedule(pod api.Pod, minionLister MinionLister) (string, error) {
|
||||
machines, err := minionLister.List()
|
||||
if err != nil {
|
||||
|
@ -26,6 +26,8 @@ import (
|
||||
type TestInterface interface {
|
||||
Errorf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// LogInterface is a simple interface to allow injection of Logf to report serving errors.
|
||||
type LogInterface interface {
|
||||
Logf(format string, args ...interface{})
|
||||
}
|
||||
@ -53,6 +55,7 @@ func (f *FakeHandler) ServeHTTP(response http.ResponseWriter, request *http.Requ
|
||||
f.RequestBody = string(bodyReceived)
|
||||
}
|
||||
|
||||
// ValidateRequest verifies that FakeHandler received a request with expected path, method, and body.
|
||||
func (f FakeHandler) ValidateRequest(t TestInterface, expectedPath, expectedMethod string, body *string) {
|
||||
if f.RequestReceived.URL.Path != expectedPath {
|
||||
t.Errorf("Unexpected request path for request %#v, received: %q, expected: %q", f.RequestReceived, f.RequestReceived.URL.Path, expectedPath)
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
|
||||
type empty struct{}
|
||||
|
||||
// A set of strings, implemented via map[string]struct{} for minimal memory consumption.
|
||||
// StringSet is a set of strings, implemented via map[string]struct{} for minimal memory consumption.
|
||||
type StringSet map[string]empty
|
||||
|
||||
// NewStringSet creates a StringSet from a list of values.
|
||||
@ -50,7 +50,7 @@ func (s StringSet) Has(item string) bool {
|
||||
return contained
|
||||
}
|
||||
|
||||
// Return the contents as a sorted string slice.
|
||||
// List returns the contents as a sorted string slice.
|
||||
func (s StringSet) List() []string {
|
||||
res := make([]string, 0, len(s))
|
||||
for key := range s {
|
||||
|
@ -85,14 +85,14 @@ const (
|
||||
|
||||
// SetYAML implements the yaml.Setter interface.
|
||||
func (intstr *IntOrString) SetYAML(tag string, value interface{}) bool {
|
||||
if intVal, ok := value.(int); ok {
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
intstr.Kind = IntstrInt
|
||||
intstr.IntVal = intVal
|
||||
intstr.IntVal = v
|
||||
return true
|
||||
}
|
||||
if strVal, ok := value.(string); ok {
|
||||
case string:
|
||||
intstr.Kind = IntstrString
|
||||
intstr.StrVal = strVal
|
||||
intstr.StrVal = v
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -129,6 +129,6 @@ func (intstr IntOrString) MarshalJSON() ([]byte, error) {
|
||||
case IntstrString:
|
||||
return json.Marshal(intstr.StrVal)
|
||||
default:
|
||||
panic("impossible IntOrString.Kind")
|
||||
return []byte{}, fmt.Errorf("impossible IntOrString.Kind")
|
||||
}
|
||||
}
|
||||
|
@ -73,121 +73,83 @@ type IntOrStringHolder struct {
|
||||
}
|
||||
|
||||
func TestIntOrStringUnmarshalYAML(t *testing.T) {
|
||||
{
|
||||
yaml_code_int := "val: 123\n"
|
||||
|
||||
var result IntOrStringHolder
|
||||
if err := yaml.Unmarshal([]byte(yaml_code_int), &result); err != nil {
|
||||
t.Errorf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
if result.IOrS.Kind != IntstrInt || result.IOrS.IntVal != 123 {
|
||||
t.Errorf("Failed to unmarshal int-typed IntOrString: %v", result)
|
||||
}
|
||||
cases := []struct {
|
||||
input string
|
||||
result IntOrString
|
||||
}{
|
||||
{"val: 123\n", IntOrString{Kind: IntstrInt, IntVal: 123}},
|
||||
{"val: \"123\"\n", IntOrString{Kind: IntstrString, StrVal: "123"}},
|
||||
}
|
||||
|
||||
{
|
||||
yaml_code_str := "val: \"123\"\n"
|
||||
|
||||
for _, c := range cases {
|
||||
var result IntOrStringHolder
|
||||
if err := yaml.Unmarshal([]byte(yaml_code_str), &result); err != nil {
|
||||
if err := yaml.Unmarshal([]byte(c.input), &result); err != nil {
|
||||
t.Errorf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
if result.IOrS.Kind != IntstrString || result.IOrS.StrVal != "123" {
|
||||
t.Errorf("Failed to unmarshal string-typed IntOrString: %v", result)
|
||||
if result.IOrS != c.result {
|
||||
t.Errorf("Failed to unmarshal IntOrString: got %+v", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntOrStringMarshalYAML(t *testing.T) {
|
||||
{
|
||||
input := IntOrStringHolder{
|
||||
IOrS: IntOrString{
|
||||
Kind: IntstrInt,
|
||||
IntVal: 123,
|
||||
},
|
||||
}
|
||||
result, err := yaml.Marshal(&input)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to marshal: %v", err)
|
||||
}
|
||||
if string(result) != "val: 123\n" {
|
||||
t.Errorf("Failed to marshal int-typed IntOrString: %q", string(result))
|
||||
}
|
||||
cases := []struct {
|
||||
input IntOrString
|
||||
result string
|
||||
}{
|
||||
{IntOrString{Kind: IntstrInt, IntVal: 123}, "val: 123\n"},
|
||||
{IntOrString{Kind: IntstrString, StrVal: "123"}, "val: \"123\"\n"},
|
||||
}
|
||||
|
||||
{
|
||||
input := IntOrStringHolder{
|
||||
IOrS: IntOrString{
|
||||
Kind: IntstrString,
|
||||
StrVal: "123",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
input := IntOrStringHolder{c.input}
|
||||
result, err := yaml.Marshal(&input)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to marshal: %v", err)
|
||||
}
|
||||
if string(result) != "val: \"123\"\n" {
|
||||
t.Errorf("Failed to marshal string-typed IntOrString: %q", string(result))
|
||||
if string(result) != c.result {
|
||||
t.Errorf("Failed to marshal IntOrString: got %q", string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntOrStringUnmarshalJSON(t *testing.T) {
|
||||
{
|
||||
json_code_int := "{\"val\": 123}"
|
||||
|
||||
var result IntOrStringHolder
|
||||
if err := json.Unmarshal([]byte(json_code_int), &result); err != nil {
|
||||
t.Errorf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
if result.IOrS.Kind != IntstrInt || result.IOrS.IntVal != 123 {
|
||||
t.Errorf("Failed to unmarshal int-typed IntOrString: %v", result)
|
||||
}
|
||||
cases := []struct {
|
||||
input string
|
||||
result IntOrString
|
||||
}{
|
||||
{"{\"val\": 123}", IntOrString{Kind: IntstrInt, IntVal: 123}},
|
||||
{"{\"val\": \"123\"}", IntOrString{Kind: IntstrString, StrVal: "123"}},
|
||||
}
|
||||
|
||||
{
|
||||
json_code_str := "{\"val\": \"123\"}"
|
||||
|
||||
for _, c := range cases {
|
||||
var result IntOrStringHolder
|
||||
if err := json.Unmarshal([]byte(json_code_str), &result); err != nil {
|
||||
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
|
||||
t.Errorf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
if result.IOrS.Kind != IntstrString || result.IOrS.StrVal != "123" {
|
||||
t.Errorf("Failed to unmarshal string-typed IntOrString: %v", result)
|
||||
if result.IOrS != c.result {
|
||||
t.Errorf("Failed to unmarshal IntOrString: got %+v", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntOrStringMarshalJSON(t *testing.T) {
|
||||
{
|
||||
input := IntOrStringHolder{
|
||||
IOrS: IntOrString{
|
||||
Kind: IntstrInt,
|
||||
IntVal: 123,
|
||||
},
|
||||
}
|
||||
result, err := json.Marshal(&input)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to marshal: %v", err)
|
||||
}
|
||||
if string(result) != "{\"val\":123}" {
|
||||
t.Errorf("Failed to marshal int-typed IntOrString: %q", string(result))
|
||||
}
|
||||
cases := []struct {
|
||||
input IntOrString
|
||||
result string
|
||||
}{
|
||||
{IntOrString{Kind: IntstrInt, IntVal: 123}, "{\"val\":123}"},
|
||||
{IntOrString{Kind: IntstrString, StrVal: "123"}, "{\"val\":\"123\"}"},
|
||||
}
|
||||
|
||||
{
|
||||
input := IntOrStringHolder{
|
||||
IOrS: IntOrString{
|
||||
Kind: IntstrString,
|
||||
StrVal: "123",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
input := IntOrStringHolder{c.input}
|
||||
result, err := json.Marshal(&input)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to marshal: %v", err)
|
||||
}
|
||||
if string(result) != "{\"val\":\"123\"}" {
|
||||
t.Errorf("Failed to marshal string-typed IntOrString: %q", string(result))
|
||||
if string(result) != c.result {
|
||||
t.Errorf("Failed to marshal IntOrString: got %q", string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
|
||||
const dnsLabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
|
||||
|
||||
var dnsLabelRegexp *regexp.Regexp = regexp.MustCompile("^" + dnsLabelFmt + "$")
|
||||
var dnsLabelRegexp = regexp.MustCompile("^" + dnsLabelFmt + "$")
|
||||
|
||||
const dnsLabelMaxLength int = 63
|
||||
|
||||
@ -34,7 +34,7 @@ func IsDNSLabel(value string) bool {
|
||||
|
||||
const dnsSubdomainFmt string = dnsLabelFmt + "(\\." + dnsLabelFmt + ")*"
|
||||
|
||||
var dnsSubdomainRegexp *regexp.Regexp = regexp.MustCompile("^" + dnsSubdomainFmt + "$")
|
||||
var dnsSubdomainRegexp = regexp.MustCompile("^" + dnsSubdomainFmt + "$")
|
||||
|
||||
const dnsSubdomainMaxLength int = 253
|
||||
|
||||
@ -46,7 +46,7 @@ func IsDNSSubdomain(value string) bool {
|
||||
|
||||
const cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*"
|
||||
|
||||
var cIdentifierRegexp *regexp.Regexp = regexp.MustCompile("^" + cIdentifierFmt + "$")
|
||||
var cIdentifierRegexp = regexp.MustCompile("^" + cIdentifierFmt + "$")
|
||||
|
||||
// IsCIdentifier tests for a string that conforms the definition of an identifier
|
||||
// in C. This checks the format, but not the length.
|
||||
|
@ -21,7 +21,7 @@ set -eu
|
||||
set -o pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
SCRIPT_DIR=$(cd $(dirname $0); pwd)
|
||||
SCRIPT_DIR=$(CDPATH="" cd $(dirname $0); pwd)
|
||||
|
||||
INSTANCE_PREFIX=$1
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
# exit on any error
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=$(cd $(dirname $0); pwd)
|
||||
SCRIPT_DIR=$(CDPATH="" cd $(dirname $0); pwd)
|
||||
|
||||
source $SCRIPT_DIR/config.sh
|
||||
|
||||
|
27
third_party/src/code.google.com/p/go-uuid/uuid/LICENSE
vendored
Normal file
27
third_party/src/code.google.com/p/go-uuid/uuid/LICENSE
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 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
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
84
third_party/src/code.google.com/p/go-uuid/uuid/dce.go
vendored
Executable file
84
third_party/src/code.google.com/p/go-uuid/uuid/dce.go
vendored
Executable file
@ -0,0 +1,84 @@
|
||||
// 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 uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A Domain represents a Version 2 domain
|
||||
type Domain byte
|
||||
|
||||
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||
const (
|
||||
Person = Domain(0)
|
||||
Group = Domain(1)
|
||||
Org = Domain(2)
|
||||
)
|
||||
|
||||
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||
//
|
||||
// The domain should be one of Person, Group or Org.
|
||||
// On a POSIX system the id should be the users UID for the Person
|
||||
// domain and the users GID for the Group. The meaning of id for
|
||||
// the domain Org or on non-POSIX systems is site defined.
|
||||
//
|
||||
// For a given domain/id pair the same token may be returned for up to
|
||||
// 7 minutes and 10 seconds.
|
||||
func NewDCESecurity(domain Domain, id uint32) UUID {
|
||||
uuid := NewUUID()
|
||||
if uuid != nil {
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||
uuid[9] = byte(domain)
|
||||
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||
// domain with the id returned by os.Getuid.
|
||||
//
|
||||
// NewDCEPerson(Person, uint32(os.Getuid()))
|
||||
func NewDCEPerson() UUID {
|
||||
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
}
|
||||
|
||||
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||
// domain with the id returned by os.Getgid.
|
||||
//
|
||||
// NewDCEGroup(Group, uint32(os.Getgid()))
|
||||
func NewDCEGroup() UUID {
|
||||
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
// Domain returns the domain for a Version 2 UUID or false.
|
||||
func (uuid UUID) Domain() (Domain, bool) {
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
return 0, false
|
||||
}
|
||||
return Domain(uuid[9]), true
|
||||
}
|
||||
|
||||
// Id returns the id for a Version 2 UUID or false.
|
||||
func (uuid UUID) Id() (uint32, bool) {
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
return 0, false
|
||||
}
|
||||
return binary.BigEndian.Uint32(uuid[0:4]), true
|
||||
}
|
||||
|
||||
func (d Domain) String() string {
|
||||
switch d {
|
||||
case Person:
|
||||
return "Person"
|
||||
case Group:
|
||||
return "Group"
|
||||
case Org:
|
||||
return "Org"
|
||||
}
|
||||
return fmt.Sprintf("Domain%d", int(d))
|
||||
}
|
8
third_party/src/code.google.com/p/go-uuid/uuid/doc.go
vendored
Executable file
8
third_party/src/code.google.com/p/go-uuid/uuid/doc.go
vendored
Executable file
@ -0,0 +1,8 @@
|
||||
// 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.
|
||||
|
||||
// The uuid package generates and inspects UUIDs.
|
||||
//
|
||||
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
|
||||
package uuid
|
53
third_party/src/code.google.com/p/go-uuid/uuid/hash.go
vendored
Normal file
53
third_party/src/code.google.com/p/go-uuid/uuid/hash.go
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// 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 uuid
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Well known Name Space IDs and UUIDs
|
||||
var (
|
||||
NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||||
NIL = Parse("00000000-0000-0000-0000-000000000000")
|
||||
)
|
||||
|
||||
// NewHash returns a new UUID dervied from the hash of space concatenated with
|
||||
// data generated by h. The hash should be at least 16 byte in length. The
|
||||
// first 16 bytes of the hash are used to form the UUID. The version of the
|
||||
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
||||
// NewMD5 and NewSHA1.
|
||||
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||
h.Reset()
|
||||
h.Write(space)
|
||||
h.Write([]byte(data))
|
||||
s := h.Sum(nil)
|
||||
uuid := make([]byte, 16)
|
||||
copy(uuid, s)
|
||||
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
||||
// supplied name space and data.
|
||||
//
|
||||
// NewHash(md5.New(), space, data, 3)
|
||||
func NewMD5(space UUID, data []byte) UUID {
|
||||
return NewHash(md5.New(), space, data, 3)
|
||||
}
|
||||
|
||||
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
||||
// supplied name space and data.
|
||||
//
|
||||
// NewHash(sha1.New(), space, data, 5)
|
||||
func NewSHA1(space UUID, data []byte) UUID {
|
||||
return NewHash(sha1.New(), space, data, 5)
|
||||
}
|
101
third_party/src/code.google.com/p/go-uuid/uuid/node.go
vendored
Executable file
101
third_party/src/code.google.com/p/go-uuid/uuid/node.go
vendored
Executable file
@ -0,0 +1,101 @@
|
||||
// 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 uuid
|
||||
|
||||
import "net"
|
||||
|
||||
var (
|
||||
interfaces []net.Interface // cached list of interfaces
|
||||
ifname string // name of interface being used
|
||||
nodeID []byte // hardware for version 1 UUIDs
|
||||
)
|
||||
|
||||
// NodeInterface returns the name of the interface from which the NodeID was
|
||||
// derived. The interface "user" is returned if the NodeID was set by
|
||||
// SetNodeID.
|
||||
func NodeInterface() string {
|
||||
return ifname
|
||||
}
|
||||
|
||||
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
||||
// If name is "" then the first usable interface found will be used or a random
|
||||
// Node ID will be generated. If a named interface cannot be found then false
|
||||
// is returned.
|
||||
//
|
||||
// SetNodeInterface never fails when name is "".
|
||||
func SetNodeInterface(name string) bool {
|
||||
if interfaces == nil {
|
||||
var err error
|
||||
interfaces, err = net.Interfaces()
|
||||
if err != nil && name != "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, ifs := range interfaces {
|
||||
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
||||
if setNodeID(ifs.HardwareAddr) {
|
||||
ifname = ifs.Name
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We found no interfaces with a valid hardware address. If name
|
||||
// does not specify a specific interface generate a random Node ID
|
||||
// (section 4.1.6)
|
||||
if name == "" {
|
||||
if nodeID == nil {
|
||||
nodeID = make([]byte, 6)
|
||||
}
|
||||
randomBits(nodeID)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
||||
// if not already set.
|
||||
func NodeID() []byte {
|
||||
if nodeID == nil {
|
||||
SetNodeInterface("")
|
||||
}
|
||||
nid := make([]byte, 6)
|
||||
copy(nid, nodeID)
|
||||
return nid
|
||||
}
|
||||
|
||||
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
||||
// of id are used. If id is less than 6 bytes then false is returned and the
|
||||
// Node ID is not set.
|
||||
func SetNodeID(id []byte) bool {
|
||||
if setNodeID(id) {
|
||||
ifname = "user"
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func setNodeID(id []byte) bool {
|
||||
if len(id) < 6 {
|
||||
return false
|
||||
}
|
||||
if nodeID == nil {
|
||||
nodeID = make([]byte, 6)
|
||||
}
|
||||
copy(nodeID, id)
|
||||
return true
|
||||
}
|
||||
|
||||
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
||||
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) NodeID() []byte {
|
||||
if len(uuid) != 16 {
|
||||
return nil
|
||||
}
|
||||
node := make([]byte, 6)
|
||||
copy(node, uuid[10:])
|
||||
return node
|
||||
}
|
132
third_party/src/code.google.com/p/go-uuid/uuid/time.go
vendored
Executable file
132
third_party/src/code.google.com/p/go-uuid/uuid/time.go
vendored
Executable file
@ -0,0 +1,132 @@
|
||||
// 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/binary"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
||||
// 1582.
|
||||
type Time int64
|
||||
|
||||
const (
|
||||
lillian = 2299160 // Julian day of 15 Oct 1582
|
||||
unix = 2440587 // Julian day of 1 Jan 1970
|
||||
epoch = unix - lillian // Days between epochs
|
||||
g1582 = epoch * 86400 // seconds between epochs
|
||||
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
lasttime uint64 // last time we returned
|
||||
clock_seq uint16 // clock sequence for this run
|
||||
|
||||
timeNow = time.Now // for testing
|
||||
)
|
||||
|
||||
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
||||
// epoch of 1 Jan 1970.
|
||||
func (t Time) UnixTime() (sec, nsec int64) {
|
||||
sec = int64(t - g1582ns100)
|
||||
nsec = (sec % 10000000) * 100
|
||||
sec /= 10000000
|
||||
return sec, nsec
|
||||
}
|
||||
|
||||
// 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) {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
return getTime()
|
||||
}
|
||||
|
||||
func getTime() (Time, error) {
|
||||
t := timeNow()
|
||||
|
||||
// If we don't have a clock sequence already, set one.
|
||||
if clock_seq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
now := uint64(t.UnixNano()/100) + g1582ns100
|
||||
|
||||
// If time has gone backwards with this clock sequence then we
|
||||
// increment the clock sequence
|
||||
if now <= lasttime {
|
||||
clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
|
||||
}
|
||||
lasttime = now
|
||||
return Time(now), nil
|
||||
}
|
||||
|
||||
// ClockSequence returns the current clock sequence, generating one if not
|
||||
// already set. The clock sequence is only used for Version 1 UUIDs.
|
||||
//
|
||||
// The uuid package does not use global static storage for the clock sequence or
|
||||
// the last time a UUID was generated. Unless SetClockSequence a new random
|
||||
// clock sequence is generated the first time a clock sequence is requested by
|
||||
// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated
|
||||
// for
|
||||
func ClockSequence() int {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
return clockSequence()
|
||||
}
|
||||
|
||||
func clockSequence() int {
|
||||
if clock_seq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
return int(clock_seq & 0x3fff)
|
||||
}
|
||||
|
||||
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
|
||||
// -1 causes a new sequence to be generated.
|
||||
func SetClockSequence(seq int) {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
setClockSequence(seq)
|
||||
}
|
||||
|
||||
func setClockSequence(seq int) {
|
||||
if seq == -1 {
|
||||
var b [2]byte
|
||||
randomBits(b[:]) // clock sequence
|
||||
seq = int(b[0])<<8 | int(b[1])
|
||||
}
|
||||
old_seq := clock_seq
|
||||
clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
||||
if old_seq != clock_seq {
|
||||
lasttime = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||
// uuid. It returns false if uuid is not valid. The time is only well defined
|
||||
// for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) Time() (Time, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
return Time(time), true
|
||||
}
|
||||
|
||||
// ClockSequence returns the clock sequence encoded in uuid. It returns false
|
||||
// if uuid is not valid. The clock sequence is only well defined for version 1
|
||||
// and 2 UUIDs.
|
||||
func (uuid UUID) ClockSequence() (int, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true
|
||||
}
|
43
third_party/src/code.google.com/p/go-uuid/uuid/util.go
vendored
Normal file
43
third_party/src/code.google.com/p/go-uuid/uuid/util.go
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// 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 uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// randomBits completely fills slice b with random data.
|
||||
func randomBits(b []byte) {
|
||||
if _, err := io.ReadFull(rander, b); err != nil {
|
||||
panic(err.Error()) // rand should never fail
|
||||
}
|
||||
}
|
||||
|
||||
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
||||
var xvalues = []byte{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
}
|
||||
|
||||
// xtob converts the the first two hex bytes of x into a byte.
|
||||
func xtob(x string) (byte, bool) {
|
||||
b1 := xvalues[x[0]]
|
||||
b2 := xvalues[x[1]]
|
||||
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
||||
}
|
163
third_party/src/code.google.com/p/go-uuid/uuid/uuid.go
vendored
Executable file
163
third_party/src/code.google.com/p/go-uuid/uuid/uuid.go
vendored
Executable file
@ -0,0 +1,163 @@
|
||||
// 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 uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
||||
// 4122.
|
||||
type UUID []byte
|
||||
|
||||
// A Version represents a UUIDs version.
|
||||
type Version byte
|
||||
|
||||
// A Variant represents a UUIDs variant.
|
||||
type Variant byte
|
||||
|
||||
// Constants returned by Variant.
|
||||
const (
|
||||
Invalid = Variant(iota) // Invalid UUID
|
||||
RFC4122 // The variant specified in RFC4122
|
||||
Reserved // Reserved, NCS backward compatibility.
|
||||
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
||||
Future // Reserved for future definition.
|
||||
)
|
||||
|
||||
var rander = rand.Reader // random function
|
||||
|
||||
// New returns a new random (version 4) UUID as a string. It is a convenience
|
||||
// function for NewRandom().String().
|
||||
func New() string {
|
||||
return NewRandom().String()
|
||||
}
|
||||
|
||||
// Parse decodes s into a UUID or returns nil. Both the UUID form of
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
|
||||
func Parse(s string) UUID {
|
||||
if len(s) == 36+9 {
|
||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||
return nil
|
||||
}
|
||||
s = s[9:]
|
||||
} else if len(s) != 36 {
|
||||
return nil
|
||||
}
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return nil
|
||||
}
|
||||
uuid := make([]byte, 16)
|
||||
for i, x := range []int{
|
||||
0, 2, 4, 6,
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
if v, ok := xtob(s[x:]); !ok {
|
||||
return nil
|
||||
} else {
|
||||
uuid[i] = v
|
||||
}
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// Equal returns true if uuid1 and uuid2 are equal.
|
||||
func Equal(uuid1, uuid2 UUID) bool {
|
||||
return bytes.Equal(uuid1, uuid2)
|
||||
}
|
||||
|
||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// , or "" if uuid is invalid.
|
||||
func (uuid UUID) String() string {
|
||||
if uuid == nil || len(uuid) != 16 {
|
||||
return ""
|
||||
}
|
||||
b := []byte(uuid)
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
|
||||
b[:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
}
|
||||
|
||||
// URN returns the RFC 2141 URN form of uuid,
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
||||
func (uuid UUID) URN() string {
|
||||
if uuid == nil || len(uuid) != 16 {
|
||||
return ""
|
||||
}
|
||||
b := []byte(uuid)
|
||||
return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x",
|
||||
b[:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
}
|
||||
|
||||
// Variant returns the variant encoded in uuid. It returns Invalid if
|
||||
// uuid is invalid.
|
||||
func (uuid UUID) Variant() Variant {
|
||||
if len(uuid) != 16 {
|
||||
return Invalid
|
||||
}
|
||||
switch {
|
||||
case (uuid[8] & 0xc0) == 0x80:
|
||||
return RFC4122
|
||||
case (uuid[8] & 0xe0) == 0xc0:
|
||||
return Microsoft
|
||||
case (uuid[8] & 0xe0) == 0xe0:
|
||||
return Future
|
||||
default:
|
||||
return Reserved
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Version returns the verison of uuid. It returns false if uuid is not
|
||||
// valid.
|
||||
func (uuid UUID) Version() (Version, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
return Version(uuid[6] >> 4), true
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
if v > 15 {
|
||||
return fmt.Sprintf("BAD_VERSION_%d", v)
|
||||
}
|
||||
return fmt.Sprintf("VERSION_%d", v)
|
||||
}
|
||||
|
||||
func (v Variant) String() string {
|
||||
switch v {
|
||||
case RFC4122:
|
||||
return "RFC4122"
|
||||
case Reserved:
|
||||
return "Reserved"
|
||||
case Microsoft:
|
||||
return "Microsoft"
|
||||
case Future:
|
||||
return "Future"
|
||||
case Invalid:
|
||||
return "Invalid"
|
||||
}
|
||||
return fmt.Sprintf("BadVariant%d", int(v))
|
||||
}
|
||||
|
||||
// SetRand sets the random number generator to r, which implents io.Reader.
|
||||
// If r.Read returns an error when the package requests random data then
|
||||
// a panic will be issued.
|
||||
//
|
||||
// Calling SetRand with nil sets the random number generator to the default
|
||||
// generator.
|
||||
func SetRand(r io.Reader) {
|
||||
if r == nil {
|
||||
rander = rand.Reader
|
||||
return
|
||||
}
|
||||
rander = r
|
||||
}
|
390
third_party/src/code.google.com/p/go-uuid/uuid/uuid_test.go
vendored
Executable file
390
third_party/src/code.google.com/p/go-uuid/uuid/uuid_test.go
vendored
Executable file
@ -0,0 +1,390 @@
|
||||
// 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 uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
in string
|
||||
version Version
|
||||
variant Variant
|
||||
isuuid bool
|
||||
}
|
||||
|
||||
var tests = []test{
|
||||
{"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true},
|
||||
{"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true},
|
||||
{"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true},
|
||||
{"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true},
|
||||
{"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true},
|
||||
{"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true},
|
||||
{"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true},
|
||||
{"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true},
|
||||
{"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true},
|
||||
{"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true},
|
||||
{"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true},
|
||||
{"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true},
|
||||
{"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true},
|
||||
{"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true},
|
||||
|
||||
{"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true},
|
||||
{"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true},
|
||||
{"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true},
|
||||
{"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true},
|
||||
|
||||
{"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false},
|
||||
{"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
}
|
||||
|
||||
var constants = []struct {
|
||||
c interface{}
|
||||
name string
|
||||
}{
|
||||
{Person, "Person"},
|
||||
{Group, "Group"},
|
||||
{Org, "Org"},
|
||||
{Invalid, "Invalid"},
|
||||
{RFC4122, "RFC4122"},
|
||||
{Reserved, "Reserved"},
|
||||
{Microsoft, "Microsoft"},
|
||||
{Future, "Future"},
|
||||
{Domain(17), "Domain17"},
|
||||
{Variant(42), "BadVariant42"},
|
||||
}
|
||||
|
||||
func testTest(t *testing.T, in string, tt test) {
|
||||
uuid := Parse(in)
|
||||
if ok := (uuid != nil); ok != tt.isuuid {
|
||||
t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid)
|
||||
}
|
||||
if uuid == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v := uuid.Variant(); v != tt.variant {
|
||||
t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant)
|
||||
}
|
||||
if v, _ := uuid.Version(); v != tt.version {
|
||||
t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUUID(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
testTest(t, tt.in, tt)
|
||||
testTest(t, strings.ToUpper(tt.in), tt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConstants(t *testing.T) {
|
||||
for x, tt := range constants {
|
||||
v, ok := tt.c.(fmt.Stringer)
|
||||
if !ok {
|
||||
t.Errorf("%x: %v: not a stringer", x, v)
|
||||
} else if s := v.String(); s != tt.name {
|
||||
v, _ := tt.c.(int)
|
||||
t.Errorf("%x: Constant %T:%d gives %q, expected %q\n", x, tt.c, v, s, tt.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomUUID(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
for x := 1; x < 32; x++ {
|
||||
uuid := NewRandom()
|
||||
s := uuid.String()
|
||||
if m[s] {
|
||||
t.Errorf("NewRandom returned duplicated UUID %s\n", s)
|
||||
}
|
||||
m[s] = true
|
||||
if v, _ := uuid.Version(); v != 4 {
|
||||
t.Errorf("Random UUID of version %s\n", v)
|
||||
}
|
||||
if uuid.Variant() != RFC4122 {
|
||||
t.Errorf("Random UUID is variant %d\n", uuid.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
for x := 1; x < 32; x++ {
|
||||
s := New()
|
||||
if m[s] {
|
||||
t.Errorf("New returned duplicated UUID %s\n", s)
|
||||
}
|
||||
m[s] = true
|
||||
uuid := Parse(s)
|
||||
if uuid == nil {
|
||||
t.Errorf("New returned %q which does not decode\n", s)
|
||||
continue
|
||||
}
|
||||
if v, _ := uuid.Version(); v != 4 {
|
||||
t.Errorf("Random UUID of version %s\n", v)
|
||||
}
|
||||
if uuid.Variant() != RFC4122 {
|
||||
t.Errorf("Random UUID is variant %d\n", uuid.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clockSeq(t *testing.T, uuid UUID) int {
|
||||
seq, ok := uuid.ClockSequence()
|
||||
if !ok {
|
||||
t.Fatalf("%s: invalid clock sequence\n", uuid)
|
||||
}
|
||||
return seq
|
||||
}
|
||||
|
||||
func TestClockSeq(t *testing.T) {
|
||||
// Fake time.Now for this test to return a monotonically advancing time; restore it at end.
|
||||
defer func(orig func() time.Time) { timeNow = orig }(timeNow)
|
||||
monTime := time.Now()
|
||||
timeNow = func() time.Time {
|
||||
monTime = monTime.Add(1 * time.Second)
|
||||
return monTime
|
||||
}
|
||||
|
||||
SetClockSequence(-1)
|
||||
uuid1 := NewUUID()
|
||||
uuid2 := NewUUID()
|
||||
|
||||
if clockSeq(t, uuid1) != clockSeq(t, uuid2) {
|
||||
t.Errorf("clock sequence %d != %d\n", clockSeq(t, uuid1), clockSeq(t, uuid2))
|
||||
}
|
||||
|
||||
SetClockSequence(-1)
|
||||
uuid2 = NewUUID()
|
||||
|
||||
// Just on the very off chance we generated the same sequence
|
||||
// two times we try again.
|
||||
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||
SetClockSequence(-1)
|
||||
uuid2 = NewUUID()
|
||||
}
|
||||
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||
t.Errorf("Duplicate clock sequence %d\n", clockSeq(t, uuid1))
|
||||
}
|
||||
|
||||
SetClockSequence(0x1234)
|
||||
uuid1 = NewUUID()
|
||||
if seq := clockSeq(t, uuid1); seq != 0x1234 {
|
||||
t.Errorf("%s: expected seq 0x1234 got 0x%04x\n", uuid1, seq)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoding(t *testing.T) {
|
||||
text := "7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||
urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||
data := UUID{
|
||||
0x7d, 0x44, 0x48, 0x40,
|
||||
0x9d, 0xc0,
|
||||
0x11, 0xd1,
|
||||
0xb2, 0x45,
|
||||
0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2,
|
||||
}
|
||||
if v := data.String(); v != text {
|
||||
t.Errorf("%x: encoded to %s, expected %s\n", data, v, text)
|
||||
}
|
||||
if v := data.URN(); v != urn {
|
||||
t.Errorf("%x: urn is %s, expected %s\n", data, v, urn)
|
||||
}
|
||||
|
||||
uuid := Parse(text)
|
||||
if !Equal(uuid, data) {
|
||||
t.Errorf("%s: decoded to %s, expected %s\n", text, uuid, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion1(t *testing.T) {
|
||||
uuid1 := NewUUID()
|
||||
uuid2 := NewUUID()
|
||||
|
||||
if Equal(uuid1, uuid2) {
|
||||
t.Errorf("%s:duplicate uuid\n", uuid1)
|
||||
}
|
||||
if v, _ := uuid1.Version(); v != 1 {
|
||||
t.Errorf("%s: version %s expected 1\n", uuid1, v)
|
||||
}
|
||||
if v, _ := uuid2.Version(); v != 1 {
|
||||
t.Errorf("%s: version %s expected 1\n", uuid2, v)
|
||||
}
|
||||
n1 := uuid1.NodeID()
|
||||
n2 := uuid2.NodeID()
|
||||
if !bytes.Equal(n1, n2) {
|
||||
t.Errorf("Different nodes %x != %x\n", n1, n2)
|
||||
}
|
||||
t1, ok := uuid1.Time()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid time\n", uuid1)
|
||||
}
|
||||
t2, ok := uuid2.Time()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid time\n", uuid2)
|
||||
}
|
||||
q1, ok := uuid1.ClockSequence()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid clock sequence\n", uuid1)
|
||||
}
|
||||
q2, ok := uuid2.ClockSequence()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid clock sequence", uuid2)
|
||||
}
|
||||
|
||||
switch {
|
||||
case t1 == t2 && q1 == q2:
|
||||
t.Errorf("time stopped\n")
|
||||
case t1 > t2 && q1 == q2:
|
||||
t.Errorf("time reversed\n")
|
||||
case t1 < t2 && q1 != q2:
|
||||
t.Errorf("clock sequence chaned unexpectedly\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeAndTime(t *testing.T) {
|
||||
// Time is February 5, 1998 12:30:23.136364800 AM GMT
|
||||
|
||||
uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2")
|
||||
node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2}
|
||||
|
||||
ts, ok := uuid.Time()
|
||||
if ok {
|
||||
c := time.Unix(ts.UnixTime())
|
||||
want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC)
|
||||
if !c.Equal(want) {
|
||||
t.Errorf("Got time %v, want %v", c, want)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("%s: bad time\n", uuid)
|
||||
}
|
||||
if !bytes.Equal(node, uuid.NodeID()) {
|
||||
t.Errorf("Expected node %v got %v\n", node, uuid.NodeID())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMD5(t *testing.T) {
|
||||
uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String()
|
||||
want := "6fa459ea-ee8a-3ca4-894e-db77e160355e"
|
||||
if uuid != want {
|
||||
t.Errorf("MD5: got %q expected %q\n", uuid, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSHA1(t *testing.T) {
|
||||
uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String()
|
||||
want := "886313e1-3b8a-5372-9b90-0c9aee199e5d"
|
||||
if uuid != want {
|
||||
t.Errorf("SHA1: got %q expected %q\n", uuid, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID(t *testing.T) {
|
||||
nid := []byte{1, 2, 3, 4, 5, 6}
|
||||
SetNodeInterface("")
|
||||
s := NodeInterface()
|
||||
if s == "" || s == "user" {
|
||||
t.Errorf("NodeInterface %q after SetInteface\n", s)
|
||||
}
|
||||
node1 := NodeID()
|
||||
if node1 == nil {
|
||||
t.Errorf("NodeID nil after SetNodeInterface\n", s)
|
||||
}
|
||||
SetNodeID(nid)
|
||||
s = NodeInterface()
|
||||
if s != "user" {
|
||||
t.Errorf("Expected NodeInterface %q got %q\n", "user", s)
|
||||
}
|
||||
node2 := NodeID()
|
||||
if node2 == nil {
|
||||
t.Errorf("NodeID nil after SetNodeID\n", s)
|
||||
}
|
||||
if bytes.Equal(node1, node2) {
|
||||
t.Errorf("NodeID not changed after SetNodeID\n", s)
|
||||
} else if !bytes.Equal(nid, node2) {
|
||||
t.Errorf("NodeID is %x, expected %x\n", node2, nid)
|
||||
}
|
||||
}
|
||||
|
||||
func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) {
|
||||
if uuid == nil {
|
||||
t.Errorf("%s failed\n", name)
|
||||
return
|
||||
}
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
t.Errorf("%s: %s: expected version 2, got %s\n", name, uuid, v)
|
||||
return
|
||||
}
|
||||
if v, ok := uuid.Domain(); !ok || v != domain {
|
||||
if !ok {
|
||||
t.Errorf("%s: %d: Domain failed\n", name, uuid)
|
||||
} else {
|
||||
t.Errorf("%s: %s: expected domain %d, got %d\n", name, uuid, domain, v)
|
||||
}
|
||||
}
|
||||
if v, ok := uuid.Id(); !ok || v != id {
|
||||
if !ok {
|
||||
t.Errorf("%s: %d: Id failed\n", name, uuid)
|
||||
} else {
|
||||
t.Errorf("%s: %s: expected id %d, got %d\n", name, uuid, id, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDCE(t *testing.T) {
|
||||
testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678)
|
||||
testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid()))
|
||||
testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
type badRand struct{}
|
||||
|
||||
func (r badRand) Read(buf []byte) (int, error) {
|
||||
for i, _ := range buf {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func TestBadRand(t *testing.T) {
|
||||
SetRand(badRand{})
|
||||
uuid1 := New()
|
||||
uuid2 := New()
|
||||
if uuid1 != uuid2 {
|
||||
t.Errorf("execpted duplicates, got %q and %q\n", uuid1, uuid2)
|
||||
}
|
||||
SetRand(nil)
|
||||
uuid1 = New()
|
||||
uuid2 = New()
|
||||
if uuid1 == uuid2 {
|
||||
t.Errorf("unexecpted duplicates, got %q\n", uuid1)
|
||||
}
|
||||
}
|
41
third_party/src/code.google.com/p/go-uuid/uuid/version1.go
vendored
Normal file
41
third_party/src/code.google.com/p/go-uuid/uuid/version1.go
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// 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 uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||
// be set NewUUID returns nil. If clock sequence has not been set by
|
||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||
// return the current NewUUID returns nil.
|
||||
func NewUUID() UUID {
|
||||
if nodeID == nil {
|
||||
SetNodeInterface("")
|
||||
}
|
||||
|
||||
now, err := GetTime()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uuid := make([]byte, 16)
|
||||
|
||||
time_low := uint32(now & 0xffffffff)
|
||||
time_mid := uint16((now >> 32) & 0xffff)
|
||||
time_hi := uint16((now >> 48) & 0x0fff)
|
||||
time_hi |= 0x1000 // Version 1
|
||||
|
||||
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)
|
||||
copy(uuid[10:], nodeID)
|
||||
|
||||
return uuid
|
||||
}
|
25
third_party/src/code.google.com/p/go-uuid/uuid/version4.go
vendored
Normal file
25
third_party/src/code.google.com/p/go-uuid/uuid/version4.go
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// 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 uuid
|
||||
|
||||
// Random returns a Random (Version 4) UUID or panics.
|
||||
//
|
||||
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||
// package.
|
||||
//
|
||||
// A note about uniqueness derived from from the UUID Wikipedia entry:
|
||||
//
|
||||
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
||||
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
||||
// means the probability is about 0.00000000006 (6 × 10−11),
|
||||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||
// year and having one duplicate.
|
||||
func NewRandom() UUID {
|
||||
uuid := make([]byte, 16)
|
||||
randomBits([]byte(uuid))
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||
return uuid
|
||||
}
|
Loading…
Reference in New Issue
Block a user