Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Kennan
2015-02-06 08:16:28 +08:00
136 changed files with 14323 additions and 978 deletions

27
api/swagger-spec/api.json Normal file
View File

@@ -0,0 +1,27 @@
{
"swaggerVersion": "1.2",
"apiVersion": "",
"basePath": "127.0.0.1:8050",
"resourcePath": "/api",
"apis": [
{
"path": "/api",
"description": "get available api versions",
"operations": [
{
"type": "void",
"method": "GET",
"summary": "get available api versions",
"nickname": "getApiVersions",
"parameters": [],
"produces": [
"application/json"
],
"consumes": [
"application/json"
]
}
]
}
]
}

View File

@@ -0,0 +1,30 @@
{
"swaggerVersion": "1.2",
"apis": [
{
"path": "/api/v1beta1",
"description": "API at /api/v1beta1 version v1beta1"
},
{
"path": "/api/v1beta2",
"description": "API at /api/v1beta2 version v1beta2"
},
{
"path": "/api/v1beta3",
"description": "API at /api/v1beta3 version v1beta3"
},
{
"path": "/api",
"description": "get available api versions"
},
{
"path": "/version",
"description": "git code version from which this is built"
}
],
"apiVersion": "",
"info": {
"title": "",
"description": ""
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
{
"swaggerVersion": "1.2",
"apiVersion": "",
"basePath": "127.0.0.1:8050",
"resourcePath": "/version",
"apis": [
{
"path": "/version",
"description": "git code version from which this is built",
"operations": [
{
"type": "void",
"method": "GET",
"summary": "get the code version",
"nickname": "getCodeVersion",
"parameters": [],
"produces": [
"application/json"
],
"consumes": [
"application/json"
]
}
]
}
]
}

View File

@@ -34,7 +34,7 @@ The `release.sh` script will build a release. It will build binaries, run tests
The main output is a tar file: `kubernetes.tar.gz`. This includes:
* Cross compiled client utilities.
* Script (`cluster/kubecfg.sh`) for picking and running the right client binary based on platform.
* Script (`cluster/kubectl.sh`) for picking and running the right client binary based on platform.
* Examples
* Cluster deployment scripts for various clouds
* Tar file containing all server binaries

View File

@@ -46,3 +46,5 @@ echo "... calling setup-logging-firewall" >&2
setup-logging-firewall
echo "Done" >&2
exit 0

View File

@@ -123,7 +123,7 @@ elif [[ "${KUBERNETES_PROVIDER}" == "gke" ]]; then
)
fi
detect-master &> /dev/null
detect-master > /dev/null
if [[ -n "${KUBE_MASTER_IP-}" && -z "${KUBERNETES_MASTER-}" ]]; then
export KUBERNETES_MASTER=https://${KUBE_MASTER_IP}
fi

View File

@@ -96,7 +96,7 @@ func (fakeKubeletClient) GetPodStatus(host, podNamespace, podID string) (api.Pod
return r, nil
}
func (fakeKubeletClient) HealthCheck(host string) (probe.Status, error) {
func (fakeKubeletClient) HealthCheck(host string) (probe.Result, error) {
return probe.Success, nil
}

View File

@@ -33,6 +33,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
nodeControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller"
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
kubeletServer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/server"
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
@@ -139,7 +140,7 @@ func startComponents(etcdClient tools.EtcdClient, cl *client.Client, addr net.IP
runScheduler(cl)
runControllerManager(machineList, cl, *nodeMilliCPU, *nodeMemory)
dockerClient := util.ConnectToDockerOrDie(*dockerEndpoint)
dockerClient := dockertools.ConnectToDockerOrDie(*dockerEndpoint)
kubeletServer.SimpleRunKubelet(cl, nil, dockerClient, machineList[0], "/tmp/kubernetes", "", "127.0.0.1", 10250, *masterServiceNamespace, kubeletServer.ProbeVolumePlugins())
}

View File

@@ -0,0 +1,4 @@
FROM golang:1.4-onbuild
VOLUME ["/git"]
ENV GIT_SYNC_DEST /git
ENTRYPOINT ["/go/bin/git-sync"]

View File

@@ -0,0 +1,16 @@
# git-sync
git-sync is a command that pull a git repository to a local directory.
It can be used to source a container volume with the content of a git repo.
## Usage
```
# build the container
docker build -t git-sync .
# run the git-sync container
docker run -d GIT_SYNC_REPO=https://github.com/GoogleCloudPlatform/kubernetes -e GIT_SYNC_BRANCH=gh-pages -r HEAD -v /git-data:/git git-sync
# run a nginx container to serve sync'ed content
docker run -d -p 8080:80 -v /git-data:/usr/share/nginx/html nginx
```

View File

@@ -0,0 +1,30 @@
# git-blog-demo
This demo shows how to use the `git-sync` sidekick container along side `volumes` and `volumeMounts` to create a markdown powered blog.
## How it works
The pod is composed of 3 containers that share directories using 2 volumes:
- The `git-sync` container clones a git repo into the `markdown` volume
- The `hugo` container read from the `markdown` volume and render it into the `html` volume.
- The `nginx` container serve the content from the `html` volume.
## Usage
Build the demo containers, and push them to a registry
```
docker build -t <some-registry>/git-sync ..
docker build -t <some-registry>/hugo hugo/
docker push <some-registry>/hugo <some-registry>/git-sync
```
Create the pod and the service for the blog
```
kubectl pods create config/pod.html
kubectl services create config/pod.html
```
Open the service external ip in your browser

View File

@@ -0,0 +1,3 @@
baseurl = "http://example.com"
languageCode = "en-us"
title = "example blog"

View File

@@ -0,0 +1,9 @@
+++
date = "2014-12-19T15:29:48-08:00"
draft = true
title = "about"
+++
## A headline
Some content about the blog.

View File

@@ -0,0 +1,9 @@
+++
date = "2014-12-19T15:30:18-08:00"
draft = true
title = "first"
+++
## first port
This is the first post.

View File

View File

View File

@@ -0,0 +1,50 @@
id: blog-pod
kind: Pod
apiVersion: v1beta1
desiredState:
manifest:
version: v1beta1
containers:
- name: git-sync
image: proppy/git-sync
imagePullPolicy: PullAlways
env::
- name: GIT_SYNC_REPO
value: https://github.com/proppy/blog.git
- name: GIT_SYNC_DEST
value: /git
volumeMounts:
- name: markdown
mountPath: /git
- name: hugo
image: proppy/hugo
imagePullPolicy: PullAlways
env:
- name: SRC
value: /src
- name: BUILD_DRAFT
value: 'true'
- name: BASE_URL
value: kube.proppy.sh
volumeMounts:
- name: markdown
mountPath: /src
- name: html
mountPath: /dest
- name: nginx
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
ports:
- name: http-server
containerPort: 80
volumes:
- name: markdown
source:
emptyDir: {}
- name: html
source:
emptyDir: {}
labels:
name: blog

View File

@@ -0,0 +1,8 @@
id: blog-service
kind: Service
apiVersion: v1beta1
port: 80
containerPort: http-server
selector:
name: blog
createExternalLoadBalancer: true

View File

@@ -0,0 +1,13 @@
FROM golang
RUN go get -v github.com/spf13/hugo
RUN git clone --recursive https://github.com/spf13/hugoThemes.git /themes
VOLUME ["/src", "/dest"]
EXPOSE 1313
ENV SRC /src
ENV DEST /dest
ENV THEME hyde
ENV BUILD_DRAFT false
ENV BASE_URL ""
ADD run-hugo /run-hugo
ENTRYPOINT ["/run-hugo"]
CMD ["server", "--source=${SRC}", "--theme=${THEME}", "--buildDrafts=${BUILD_DRAFT}", "--baseUrl=${BASE_URL}", "--watch", "--destination=${DEST}", "--appendPort=false"]

View File

@@ -14,12 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
${KUBE_ROOT}/hack/e2e-suite/goe2e.sh -tTestNetwork
set -ex
if [ ! -d ${SRC}/themes ]; then
ln -s /themes ${SRC}/themes
fi
hugo $(eval echo $*) # force default CMD env expansion

112
contrib/git-sync/main.go Normal file
View File

@@ -0,0 +1,112 @@
/*
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.
*/
// git-sync is a command that pull a git repository to a local directory.
package main // import "github.com/GoogleCloudPlatform/kubernetes/contrib/git-sync"
import (
"flag"
"fmt"
"log"
"os"
"os/exec"
"path"
"strconv"
"strings"
"time"
)
var flRepo = flag.String("repo", envString("GIT_SYNC_REPO", ""), "git repo url")
var flBranch = flag.String("branch", envString("GIT_SYNC_BRANCH", "master"), "git branch")
var flRev = flag.String("rev", envString("GIT_SYNC_BRANCH", "HEAD"), "git rev")
var flDest = flag.String("dest", envString("GIT_SYNC_DEST", ""), "destination path")
var flWait = flag.Int("wait", envInt("GIT_SYNC_WAIT", 0), "number of seconds to wait before exit")
func envString(key, def string) string {
if env := os.Getenv(key); env != "" {
return env
}
return def
}
func envInt(key string, def int) int {
if env := os.Getenv(key); env != "" {
val, err := strconv.Atoi(env)
if err != nil {
log.Println("invalid value for %q: using default: %q", key, def)
return def
}
return val
}
return def
}
const usage = "usage: GIT_SYNC_REPO= GIT_SYNC_DEST= [GIT_SYNC_BRANCH= GIT_SYNC_WAIT=] git-sync -repo GIT_REPO_URL -dest PATH [-branch -wait]"
func main() {
flag.Parse()
if *flRepo == "" || *flDest == "" {
flag.Usage()
log.Fatal(usage)
}
if _, err := exec.LookPath("git"); err != nil {
log.Fatalf("required git executable not found: %v", err)
}
if err := syncRepo(*flRepo, *flDest, *flBranch, *flRev); err != nil {
log.Fatalf("error syncing repo: %v", err)
}
log.Printf("wait %d seconds", *flWait)
time.Sleep(time.Duration(*flWait) * time.Second)
log.Println("done")
}
// syncRepo syncs the branch of a given repository to the destination at the given rev.
func syncRepo(repo, dest, branch, rev string) error {
gitRepoPath := path.Join(dest, ".git")
_, err := os.Stat(gitRepoPath)
switch {
case os.IsNotExist(err):
// clone repo
cmd := exec.Command("git", "clone", "--no-checkout", "-b", branch, repo, dest)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error cloning repo %q: %v: %s", strings.Join(cmd.Args, " "), err, string(output))
}
log.Printf("clone %q: %s", repo, string(output))
case err != nil:
return fmt.Errorf("error checking if repo exist %q: %v", gitRepoPath, err)
}
// fetch branch
cmd := exec.Command("git", "fetch", "origin", branch)
cmd.Dir = dest
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error running command %q: %v: %s", strings.Join(cmd.Args, " "), err, string(output))
}
log.Printf("fetch %q: %s", branch, string(output))
// reset working copy
cmd = exec.Command("git", "reset", "--hard", rev)
cmd.Dir = dest
output, err = cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error running command %q : %v: %s", strings.Join(cmd.Args, " "), err, string(output))
}
log.Printf("reset %q: %v", rev, string(output))
return nil
}

View File

@@ -7,15 +7,15 @@ EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/apiserver
User=kube
ExecStart=/usr/bin/kube-apiserver \
${KUBE_LOGTOSTDERR} \
${KUBE_LOG_LEVEL} \
${KUBE_ETCD_SERVERS} \
${KUBE_API_ADDRESS} \
${KUBE_API_PORT} \
${KUBELET_PORT} \
${KUBE_ALLOW_PRIV} \
${KUBE_SERVICE_ADDRESSES} \
${KUBE_API_ARGS}
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBE_ETCD_SERVERS \
$KUBE_API_ADDRESS \
$KUBE_API_PORT \
$KUBELET_PORT \
$KUBE_ALLOW_PRIV \
$KUBE_SERVICE_ADDRESSES \
$KUBE_API_ARGS
Restart=on-failure
[Install]

View File

@@ -8,11 +8,11 @@ EnvironmentFile=-/etc/kubernetes/apiserver
EnvironmentFile=-/etc/kubernetes/controller-manager
User=kube
ExecStart=/usr/bin/kube-controller-manager \
${KUBE_LOGTOSTDERR} \
${KUBE_LOG_LEVEL} \
${KUBELET_ADDRESSES} \
${KUBE_MASTER} \
${KUBE_CONTROLLER_MANAGER_ARGS}
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBELET_ADDRESSES \
$KUBE_MASTER \
$KUBE_CONTROLLER_MANAGER_ARGS
Restart=on-failure
[Install]

View File

@@ -6,10 +6,10 @@ Documentation=https://github.com/GoogleCloudPlatform/kubernetes
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/proxy
ExecStart=/usr/bin/kube-proxy \
${KUBE_LOGTOSTDERR} \
${KUBE_LOG_LEVEL} \
${KUBE_ETCD_SERVERS} \
${KUBE_PROXY_ARGS}
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBE_ETCD_SERVERS \
$KUBE_PROXY_ARGS
Restart=on-failure
[Install]

View File

@@ -8,10 +8,10 @@ EnvironmentFile=-/etc/kubernetes/apiserver
EnvironmentFile=-/etc/kubernetes/scheduler
User=kube
ExecStart=/usr/bin/kube-scheduler \
${KUBE_LOGTOSTDERR} \
${KUBE_LOG_LEVEL} \
${KUBE_MASTER} \
${KUBE_SCHEDULER_ARGS}
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBE_MASTER \
$KUBE_SCHEDULER_ARGS
Restart=on-failure
[Install]

View File

@@ -9,14 +9,14 @@ WorkingDirectory=/var/lib/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/bin/kubelet \
${KUBE_LOGTOSTDERR} \
${KUBE_LOG_LEVEL} \
${KUBE_ETCD_SERVERS} \
${KUBELET_ADDRESS} \
${KUBELET_PORT} \
${KUBELET_HOSTNAME} \
${KUBE_ALLOW_PRIV} \
${KUBELET_ARGS}
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBE_ETCD_SERVERS \
$KUBELET_ADDRESS \
$KUBELET_PORT \
$KUBELET_HOSTNAME \
$KUBE_ALLOW_PRIV \
$KUBELET_ARGS
Restart=on-failure
[Install]

View File

@@ -14,7 +14,7 @@ The example below creates an elastic Kubernetes cluster with 3 worker nodes and
## Prerequisites
* [kubecfg CLI](aws/kubecfg.md)
* [kubectl CLI](aws/kubectl.md)
* [aws CLI](http://aws.amazon.com/cli)
* [CoreOS image for AWS](https://coreos.com/docs/running-coreos/cloud-providers/ec2/#choosing-a-channel)
@@ -42,7 +42,7 @@ aws cloudformation describe-stack-events --stack-name kubernetes
aws cloudformation describe-stacks --stack-name kubernetes
```
[Skip to kubecfg client configuration](#configure-the-kubecfg-ssh-tunnel)
[Skip to kubectl client configuration](#configure-the-kubectl-ssh-tunnel)
### Manually
@@ -121,9 +121,9 @@ aws ec2 run-instances --count 1 --image-id <ami_image_id> --key-name <keypair> \
--user-data file://node.yaml
```
### Configure the kubecfg SSH tunnel
### Configure the kubectl SSH tunnel
This command enables secure communication between the kubecfg client and the Kubernetes API.
This command enables secure communication between the kubectl client and the Kubernetes API.
```
ssh -f -nNT -L 8080:127.0.0.1:8080 core@<master-public-ip>
@@ -134,7 +134,7 @@ ssh -f -nNT -L 8080:127.0.0.1:8080 core@<master-public-ip>
Once the worker instances have fully booted, they will be automatically registered with the Kubernetes API server by the kube-register service running on the master node. It may take a few mins.
```
kubecfg list minions
kubectl get nodes
```
## Starting a simple pod
@@ -167,16 +167,16 @@ Create a pod manifest: `pod.json`
}
```
### Create the pod using the kubecfg command line tool
### Create the pod using the kubectl command line tool
```
kubecfg -c pod.json create pods
kubectl create -f pod.json
```
### Testing
```
kubecfg list pods
kubectl get pods
```
> Record the **Host** of the pod, which should be the private IP address.
@@ -208,5 +208,5 @@ Visit the public IP address in your browser to view the running pod.
### Delete the pod
```
kubecfg delete pods/hello
kubectl delete pods hello
```

View File

@@ -247,7 +247,7 @@
" ExecStart=/opt/bin/kube-apiserver \\\n",
" --address=0.0.0.0 \\\n",
" --port=8080 \\\n",
" --portal_net 10.244.0.0/16 \\\n",
" --portal_net=10.244.0.0/16 \\\n",
" --etcd_servers=http://127.0.0.1:4001 \\\n",
" --public_address_override=$private_ipv4 \\\n",
" --logtostderr=true\n",

View File

@@ -1,28 +0,0 @@
# Install and configure kubecfg
## Download the kubecfg CLI tool
### Darwin
```
wget http://storage.googleapis.com/k8s/darwin/kubecfg
```
### Linux
```
wget http://storage.googleapis.com/k8s/linux/kubecfg
```
### Copy kubecfg to your path
```
chmod +x kubecfg
mv kubecfg /usr/local/bin/
```
### Create a secure tunnel for API communication
```
ssh -f -nNT -L 8080:127.0.0.1:8080 core@<master-public-ip>
```

View File

@@ -0,0 +1,28 @@
# Install and configure kubecfg
## Download the kubecfg CLI tool
### Darwin
```
wget https://storage.googleapis.com/kubernetes-release/release/v0.9.2/bin/darwin/amd64/kubectl
```
### Linux
```
wget https://storage.googleapis.com/kubernetes-release/release/v0.9.2/bin/linux/amd64/kubectl
```
### Copy kubectl to your path
```
chmod +x kubectl
mv kubectl /usr/local/bin/
```
### Create a secure tunnel for API communication
```
ssh -f -nNT -L 8080:127.0.0.1:8080 core@<master-public-ip>
```

View File

@@ -34,21 +34,26 @@ can tweak some of these parameters by editing `cluster/azure/config-default.sh`.
### Running a container (simple version)
The `cluster/kubecfg.sh` command below spins up two containers, running [Nginx](http://nginx.org/en/) and with port 80 mapped to 8080:
Once you have your instances up and running, the `hack/build-go.sh` script sets up
your Go workspace and builds the Go components.
```
cd kubernetes
cluster/kubecfg.sh -p 8080:80 run dockerfile/nginx 2 myNginx
The `kubectl.sh` line below spins up two containers running
[Nginx](http://nginx.org/en/) running on port 80:
```bash
cluster/kubectl.sh run-container my-nginx --image=dockerfile/nginx --replicas=2 --port=80
```
To stop the containers:
```
cluster/kubecfg.sh stop myNginx
```bash
cluster/kubectl.sh stop rc my-nginx
```
To delete the containers:
```
cluster/kubecfg.sh rm myNginx
```bash
cluster/kubectl.sh delete rc my-nginx
```
### Running a container (more complete version)

View File

@@ -50,23 +50,23 @@ field values:
Once you have your instances up and running, the `hack/build-go.sh` script sets up
your Go workspace and builds the Go components.
The `kubecfg.sh` line below spins up two containers running
[Nginx](http://nginx.org/en/) with port 80 mapped to 8080:
The `kubectl.sh` line below spins up two containers running
[Nginx](http://nginx.org/en/) running on port 80:
```bash
cluster/kubecfg.sh -p 8080:80 run dockerfile/nginx 2 myNginx
cluster/kubectl.sh run-container my-nginx --image=dockerfile/nginx --replicas=2 --port=80
```
To stop the containers:
```bash
cluster/kubecfg.sh stop myNginx
cluster/kubectl.sh stop rc my-nginx
```
To delete the containers:
```bash
cluster/kubecfg.sh rm myNginx
cluster/kubectl.sh delete rc my-nginx
```
### Running a container (more complete version)

View File

@@ -30,7 +30,7 @@ hack/local-up-cluster.sh
This will build and start a lightweight local cluster, consisting of a master
and a single minion. Type Control-C to shut it down.
You can use the cluster/kubecfg.sh script to interact with the local cluster.
You can use the cluster/kubectl.sh script to interact with the local cluster.
You must set the KUBERNETES_PROVIDER and KUBERNETES_MASTER environment variables to let other programs
know how to reach your master.
@@ -43,13 +43,13 @@ export KUBERNETES_MASTER=http://localhost:8080
Your cluster is running, and you want to start running containers!
You can now use any of the cluster/kubecfg.sh commands to interact with your local setup.
You can now use any of the cluster/kubectl.sh commands to interact with your local setup.
```
cluster/kubectl.sh get pods
cluster/kubectl.sh get services
cluster/kubectl.sh get replicationControllers
cluster/kubecfg.sh -p 8081:80 run dockerfile/nginx 1 myNginx
cluster/kubectl.sh run-container my-nginx --image=dockerfile/nginx --replicas=2 --port=80
## begin wait for provision to complete, you can monitor the docker pull by opening a new terminal

View File

@@ -25,7 +25,7 @@ $ sudo ./util.sh
After this the kubernetes and `etcd` services would be up and running. You can use `service start/stop/restart/force-reload` on the services.
Launching and scheduling containers using kubecfg can also be used at this point, as explained mentioned in the [examples](https://github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/guestbook)
Launching and scheduling containers using kubectl can also be used at this point, as explained mentioned in the [examples](https://github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/guestbook)
### 3. Customizing the ubuntu launch
To customize the defaults you will need to tweak `/etc/default/kube*` files and restart the appropriate services. This is needed if the binaries are copied in a place other than `/opt/bin`. A run could look like

View File

@@ -178,10 +178,9 @@ NAME IMAGE(S SELECTOR REPLICAS
```
Start a container running nginx with a replication controller and three replicas
(note that this step uses the `kubecfg.sh` command instead of `kubectl.sh`):
```
$ cluster/kubecfg.sh -p 8080:80 run dockerfile/nginx 3 myNginx
$ cluster/kubectl.sh run-container my-nginx --image=dockerfile/nginx --replicas=3 --port=80
```
When listing the pods, you will see that three containers have been started and are in Waiting state:
@@ -231,7 +230,7 @@ NAME LABELS SELECTOR IP PORT
$ cluster/kubectl.sh get replicationControllers
NAME IMAGE(S SELECTOR REPLICAS
myNginx dockerfile/nginx name=myNginx 3
myNginx dockerfile/nginx name=my-nginx 3
```
We did not start any services, hence there are none listed. But we see three replicas displayed properly.
@@ -239,7 +238,7 @@ Check the [guestbook](../../examples/guestbook/README.md) application to learn h
You can already play with resizing the replicas with:
```
$ cluster/kubecfg.sh resize myNginx 2
$ cluster/kubectl.sh resize rc my-nginx --replicas=2
$ cluster/kubectl.sh get pods
NAME IMAGE(S) HOST LABELS STATUS
7813c8bd-3ffe-11e4-9036-0800279696e1 dockerfile/nginx 10.245.2.2/10.245.2.2 name=myNginx Running

View File

@@ -73,7 +73,8 @@ Usage:
--v=0: log level for V logs
--validate=false: If true, use a schema to validate the input before sending it
--vmodule=: comma-separated list of pattern=N settings for file-filtered logging
-w, --www="": Also serve static files from the given directory under the prefix /static
-w, --www="": Also serve static files from the given directory under the specified prefix
-P, --www-prefix="/static/": Prefix to serve static files under, if static file dir is specified
```
@@ -337,7 +338,7 @@ Usage:
kubectl config [command]
Available Commands:
view displays the specified .kubeconfig file or a merged result
view displays merged .kubeconfig settings or a specified .kubeconfig file.
set-cluster name [--server=server] [--certificate-authority=path/to/certficate/authority] [--api-version=apiversion] [--insecure-skip-tls-verify=true] Sets a cluster entry in .kubeconfig
set-credentials name [--auth-path=path/to/auth/file] [--client-certificate=path/to/certficate/file] [--client-key=path/to/key/file] [--token=bearer_token_string] Sets a user entry in .kubeconfig
set-context name [--cluster=cluster-nickname] [--user=user-nickname] [--namespace=namespace] Sets a context entry in .kubeconfig
@@ -394,7 +395,13 @@ Use "kubectl help [command]" for more information about that command.
```
#### config view
displays the specified .kubeconfig file or a merged result
displays merged .kubeconfig settings or a specified .kubeconfig file.
Examples:
// Show merged .kubeconfig settings.
$ kubectl config view
// Show only local ./.kubeconfig settings
$ kubectl config view --local
Usage:
```
@@ -420,10 +427,14 @@ Usage:
--log_flush_frequency=5s: Maximum number of seconds between log flushes
--logtostderr=true: log to standard error instead of files
--match-server-version=false: Require server version to match client version
--merge=false: merge together the full hierarchy of .kubeconfig files
--merge=true: merge together the full hierarchy of .kubeconfig files
--namespace="": If present, the namespace scope for this CLI request.
--no-headers=false: When using the default output, don't print headers
-o, --output="": Output format: json|yaml|template|templatefile
--output-version="": Output the formatted object with the given version (default api-version)
-s, --server="": The address of the Kubernetes API server
--stderrthreshold=2: logs at or above this threshold go to stderr
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile.
--token="": Bearer token for authentication to the API server.
--user="": The name of the kubeconfig user to use
--v=0: log level for V logs

View File

@@ -11,6 +11,15 @@ key/value labels set on it, with at most one label with a particular key.
}
```
While there are no restrictions on the format of label values, label keys must be of the form:
```
label-key ::= prefixed-name | name
prefixed-name ::= prefix '/' name
prefix ::= DNS_SUBDOMAIN
name ::= DNS_LABEL
```
DNS_LABEL and DNS_SUBDOMAIN are defined in the [identifiers design doc](/docs/design/identifiers.md). The prefix is optional. If the prefix is not specified, the key is assumed to be private to the user. Other system components that wish to use labels must specify a prefix. The "kubernetes.io/" prefix is reserved for use by kubernetes components.
Unlike [names and UIDs](identifiers.md), labels do not provide uniqueness. In general, we expect many objects to carry the same label(s).
Via a _label selector_, the client/user can identify a set of objects. The label selector is the core grouping primitive in Kubernetes.

View File

@@ -6,7 +6,7 @@ The example combines a web frontend, a redis master for storage and a replicated
### Step Zero: Prerequisites
This example assumes that have a basic understanding of kubernetes services and that you have forked the repository and [turned up a Kubernetes cluster](https://github.com/GoogleCloudPlatform/kubernetes#contents):
This example assumes that you have a basic understanding of kubernetes services and that you have forked the repository and [turned up a Kubernetes cluster](https://github.com/GoogleCloudPlatform/kubernetes#contents):
```shell
$ cd kubernetes

View File

@@ -22,8 +22,17 @@ set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}}
: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"}
: ${KUBE_CONFIG_FILE:="config-test.sh"}
export KUBECTL KUBE_CONFIG_FILE
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
prepare-e2e
if [[ "${KUBERNETES_PROVIDER}" != "gce" ]] && [[ "${KUBERNETES_PROVIDER}" != "gke" ]]; then
echo "WARNING: Skipping certs.sh for cloud provider: ${KUBERNETES_PROVIDER}."

View File

@@ -23,8 +23,17 @@ set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}}
: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"}
: ${KUBE_CONFIG_FILE:="config-test.sh"}
export KUBECTL KUBE_CONFIG_FILE
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh"
source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
prepare-e2e
GUESTBOOK="${KUBE_ROOT}/examples/guestbook"

View File

@@ -22,8 +22,17 @@ set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}}
: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"}
: ${KUBE_CONFIG_FILE:="config-test.sh"}
export KUBECTL KUBE_CONFIG_FILE
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh"
source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
prepare-e2e
liveness_tests="http exec"
if [[ ${KUBERNETES_PROVIDER} == "gke" ]]; then

View File

@@ -23,8 +23,17 @@ set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}}
: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"}
: ${KUBE_CONFIG_FILE:="config-test.sh"}
export KUBECTL KUBE_CONFIG_FILE
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh"
source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
prepare-e2e
MONITORING="${KUBE_ROOT}/cluster/addons/cluster-monitoring"
KUBECTL="${KUBE_ROOT}/cluster/kubectl.sh"

View File

@@ -22,8 +22,17 @@ set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}}
: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"}
: ${KUBE_CONFIG_FILE:="config-test.sh"}
export KUBECTL KUBE_CONFIG_FILE
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh"
source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
prepare-e2e
if [[ "$KUBERNETES_PROVIDER" != "gce" ]] && [[ "$KUBERNETES_PROVIDER" != "gke" ]]; then
echo "WARNING: Skipping pd.sh for cloud provider: ${KUBERNETES_PROVIDER}."

View File

@@ -21,8 +21,17 @@ set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}}
: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"}
: ${KUBE_CONFIG_FILE:="config-test.sh"}
export KUBECTL KUBE_CONFIG_FILE
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
prepare-e2e
if [[ "$KUBERNETES_PROVIDER" == "vagrant" ]]; then
echo "WARNING: Skipping services.sh for ${KUBERNETES_PROVIDER}. See https://github.com/GoogleCloudPlatform/kubernetes/issues/3655"

View File

@@ -21,9 +21,17 @@ set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh"
: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}}
: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"}
: ${KUBE_CONFIG_FILE:="config-test.sh"}
export KUBECTL KUBE_CONFIG_FILE
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
prepare-e2e
CONTROLLER_NAME=update-demo

View File

@@ -15,6 +15,6 @@
# limitations under the License.
# Provided for backwards compatibility
go run "$(dirname $0)/e2e.go" -v -build -up -tests="*" -down
go run "$(dirname $0)/e2e.go" -v -build -up -test -down
exit $?

View File

@@ -30,10 +30,8 @@ import (
"os/signal"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
)
var (
@@ -44,12 +42,8 @@ var (
push = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
pushup = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
down = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
orderseed = flag.Int64("orderseed", 0, "If non-zero, seed of random test shuffle order. (Otherwise random.)")
test = flag.Bool("test", false, "Run all tests in hack/e2e-suite.")
tests = flag.String("tests", "", "Run only tests in hack/e2e-suite matching this glob. Ignored if -test is set.")
times = flag.Int("times", 1, "Number of times each test is eligible to be run. Individual order is determined by shuffling --times instances of each test using --orderseed (like a multi-deck shoe of cards).")
test = flag.Bool("test", false, "Run Ginkgo tests.")
root = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
tap = flag.Bool("tap", false, "Enable Test Anything Protocol (TAP) output (disables --verbose, only failure output recorded)")
verbose = flag.Bool("v", false, "If true, print all command output.")
trace_bash = flag.Bool("trace-bash", false, "If true, pass -x to bash to trace all bash commands")
checkVersionSkew = flag.Bool("check_version_skew", true, ""+
@@ -95,21 +89,6 @@ func main() {
flag.Parse()
signal.Notify(signals, os.Interrupt)
if *tap {
fmt.Printf("TAP version 13\n")
log.SetPrefix("# ")
// TODO: this limitation is fixable by moving runBash to
// outputing to temp files, which still lets people check on
// stuck things interactively. The current stdout/stderr
// approach isn't really going to work with TAP, though.
*verbose = false
}
if *test {
*tests = "*"
}
if *isup {
status := 1
if IsUp() {
@@ -137,9 +116,12 @@ func main() {
log.Fatalf("Error preparing a binary of version %s: %s. Aborting.", *version, err)
} else {
versionRoot = newVersionRoot
os.Setenv("KUBE_VERSION_ROOT", newVersionRoot)
}
}
os.Setenv("KUBECTL", versionRoot+`/cluster/kubectl.sh`+kubectlArgs())
if *pushup {
if IsUp() {
log.Printf("e2e cluster is up, pushing.")
@@ -165,8 +147,8 @@ func main() {
switch {
case *ctlCmd != "":
failure = !runBash("'kubectl "+*ctlCmd+"'", "$KUBECTL "+*ctlCmd)
case *tests != "":
failure = PrintResults(Test())
case *test:
failure = Test()
}
if *down {
@@ -275,7 +257,7 @@ func shuffleStrings(strings []string, r *rand.Rand) {
}
}
func Test() (results ResultsByTest) {
func Test() bool {
defer runBashUntil("watchEvents", "while true; do $KUBECTL --watch-only get events; done")()
if !IsUp() {
@@ -284,128 +266,7 @@ func Test() (results ResultsByTest) {
ValidateClusterSize()
// run tests!
dir, err := os.Open(filepath.Join(*root, "hack", "e2e-suite"))
if err != nil {
log.Fatal("Couldn't open e2e-suite dir")
}
defer dir.Close()
names, err := dir.Readdirnames(0)
if err != nil {
log.Fatal("Couldn't read names in e2e-suite dir")
}
toRun := make([]string, 0, len(names))
for i := range names {
name := names[i]
if name == "." || name == ".." {
continue
}
if match, err := path.Match(*tests, name); !match && err == nil {
continue
}
if err != nil {
log.Fatalf("Bad test pattern: %v", *tests)
}
toRun = append(toRun, name)
}
if *orderseed == 0 {
// Use low order bits of NanoTime as the default seed. (Using
// all the bits makes for a long, very similar looking seed
// between runs.)
*orderseed = time.Now().UnixNano() & (1<<32 - 1)
}
sort.Strings(toRun)
if *times != 1 {
if *times <= 0 {
log.Fatal("Invalid --times (negative or no testing requested)!")
}
newToRun := make([]string, 0, *times*len(toRun))
for i := 0; i < *times; i++ {
newToRun = append(newToRun, toRun...)
}
toRun = newToRun
}
shuffleStrings(toRun, rand.New(rand.NewSource(*orderseed)))
log.Printf("Running tests matching %v shuffled with seed %#x: %v", *tests, *orderseed, toRun)
results = ResultsByTest{}
if *tap {
fmt.Printf("1..%v\n", len(toRun))
}
for i, name := range toRun {
absName := filepath.Join(*root, "hack", "e2e-suite", name)
log.Printf("Starting test [%v/%v]: %v", i+1, len(toRun), name)
start := time.Now()
testResult := results[name]
res, stdout, stderr := runBashWithOutputs(name, absName)
// The duration_ms output is an undocumented Jenkins TAP
// plugin feature for test duration. One might think _ms means
// milliseconds, but Jenkins interprets this field in seconds.
duration_secs := time.Now().Sub(start).Seconds()
if res {
fmt.Printf("ok %v - %v\n", i+1, name)
if *tap {
fmt.Printf(" ---\n duration_ms: %.3f\n ...\n", duration_secs)
}
testResult.Pass++
} else {
fmt.Printf("not ok %v - %v\n", i+1, name)
if *tap {
fmt.Printf(" ---\n duration_ms: %.3f\n", duration_secs)
}
printBashOutputs(" ", " ", stdout, stderr, *tap)
if *tap {
fmt.Printf(" ...\n")
}
testResult.Fail++
}
results[name] = testResult
}
return
}
func PrintResults(results ResultsByTest) bool {
failures := 0
passed := []string{}
flaky := []string{}
failed := []string{}
for test, result := range results {
if result.Pass > 0 && result.Fail == 0 {
passed = append(passed, test)
} else if result.Pass > 0 && result.Fail > 0 {
flaky = append(flaky, test)
failures += result.Fail
} else {
failed = append(failed, test)
failures += result.Fail
}
}
sort.Strings(passed)
sort.Strings(flaky)
sort.Strings(failed)
printSubreport("Passed", passed, results)
printSubreport("Flaky", flaky, results)
printSubreport("Failed", failed, results)
if failures > 0 {
log.Printf("%v test(s) failed.", failures)
} else {
log.Printf("Success!")
}
return failures > 0
}
func printSubreport(title string, tests []string, results ResultsByTest) {
report := title + " tests:"
for _, test := range tests {
result := results[test]
report += fmt.Sprintf(" %v[%v/%v]", test, result.Pass, result.Pass+result.Fail)
}
log.Printf(report)
return runBash("Ginkgo tests", filepath.Join(*root, "hack", "ginkgo-e2e.sh"))
}
// All nonsense below is temporary until we have go versions of these things.
@@ -448,10 +309,6 @@ func runBashUntil(stepName, bashFragment string) func() {
cmd.Process.Signal(os.Interrupt)
headerprefix := stepName + " "
lineprefix := " "
if *tap {
headerprefix = "# " + headerprefix
lineprefix = "# " + lineprefix
}
printBashOutputs(headerprefix, lineprefix, string(stdout.Bytes()), string(stderr.Bytes()), false)
}
}

View File

@@ -14,11 +14,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_ROOT}/cluster/$KUBERNETES_PROVIDER/util.sh"
set -o errexit
set -o nounset
set -o pipefail
detect-master > /dev/null
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
: ${KUBE_VERSION_ROOT:=${KUBE_ROOT}}
: ${KUBECTL:="${KUBE_VERSION_ROOT}/cluster/kubectl.sh"}
: ${KUBE_CONFIG_FILE:="config-test.sh"}
export KUBECTL KUBE_CONFIG_FILE
source "${KUBE_ROOT}/cluster/kube-env.sh"
source "${KUBE_VERSION_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
prepare-e2e
detect-master >/dev/null
# Detect the OS name/arch so that we can find our binary
case "$(uname -s)" in
@@ -89,5 +102,6 @@ fi
"${e2e}" "${auth_config[@]:+${auth_config[@]}}" \
--host="https://${KUBE_MASTER_IP-}" \
--provider="${KUBERNETES_PROVIDER}" \
--ginkgo.v \
${E2E_REPORT_DIR+"--report_dir=${E2E_REPORT_DIR}"} \
"${@}"

View File

@@ -83,5 +83,5 @@ export E2E_REPORT_DIR=${WORKSPACE}
go run ./hack/e2e.go ${E2E_OPT} -v --down
go run ./hack/e2e.go ${E2E_OPT} -v --up
go run ./hack/e2e.go -v --ctl="version --match-server-version=false"
go run ./hack/e2e.go ${E2E_OPT} --test --tap | tee ../e2e.${JOB_NAME}.${BUILD_NUMBER}.${GITHASH}.tap
go run ./hack/e2e.go ${E2E_OPT} -v --test || echo "Ignored, Jenkins will pass/fail based on test failures"
go run ./hack/e2e.go ${E2E_OPT} -v --down

View File

@@ -55,6 +55,7 @@ readonly KUBE_TEST_PORTABLE=(
contrib/for-tests/network-tester/service.json
hack/e2e.go
hack/e2e-suite
hack/ginkgo-e2e.sh
)
# If we update this we need to also update the set of golang compilers we build

View File

@@ -55,6 +55,7 @@ kube::log::status "Starting kubelet in masterless mode"
"${KUBE_OUTPUT_HOSTBIN}/kubelet" \
--really_crash_for_testing=true \
--root_dir=/tmp/kubelet.$$ \
--docker_endpoint="fake://" \
--address="127.0.0.1" \
--port="$KUBELET_PORT" 1>&2 &
KUBELET_PID=$!
@@ -65,6 +66,7 @@ kube::log::status "Starting kubelet in masterful mode"
"${KUBE_OUTPUT_HOSTBIN}/kubelet" \
--really_crash_for_testing=true \
--root_dir=/tmp/kubelet.$$ \
--docker_endpoint="fake://" \
--etcd_servers="http://${ETCD_HOST}:${ETCD_PORT}" \
--hostname_override="127.0.0.1" \
--address="127.0.0.1" \

71
hack/update-swagger-spec.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/bin/bash
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Script to fetch latest swagger spec.
# Puts the updated spec at swagger-spec/
set -o errexit
set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
SWAGGER_ROOT_DIR="${KUBE_ROOT}/api/swagger-spec"
source "${KUBE_ROOT}/hack/lib/init.sh"
function cleanup()
{
[[ -n ${APISERVER_PID-} ]] && kill ${APISERVER_PID} 1>&2 2>/dev/null
kube::etcd::cleanup
kube::log::status "Clean up complete"
}
trap cleanup EXIT SIGINT
kube::etcd::start
ETCD_HOST=${ETCD_HOST:-127.0.0.1}
ETCD_PORT=${ETCD_PORT:-4001}
API_PORT=${API_PORT:-8050}
API_HOST=${API_HOST:-127.0.0.1}
KUBELET_PORT=${KUBELET_PORT:-10250}
# Start kube-apiserver
kube::log::status "Starting kube-apiserver"
"${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \
--address="127.0.0.1" \
--public_address_override="127.0.0.1" \
--port="${API_PORT}" \
--etcd_servers="http://${ETCD_HOST}:${ETCD_PORT}" \
--public_address_override="127.0.0.1" \
--kubelet_port=${KUBELET_PORT} \
--runtime_config=api/v1beta3 \
--portal_net="10.0.0.0/24" 1>&2 &
APISERVER_PID=$!
kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: "
SWAGGER_API_PATH="http://127.0.0.1:${API_PORT}/swaggerapi/"
kube::log::status "Updating " ${SWAGGER_ROOT_DIR}
curl ${SWAGGER_API_PATH} > ${SWAGGER_ROOT_DIR}/resourceListing.json
curl ${SWAGGER_API_PATH}version > ${SWAGGER_ROOT_DIR}/version.json
curl ${SWAGGER_API_PATH}api > ${SWAGGER_ROOT_DIR}/api.json
curl ${SWAGGER_API_PATH}api/v1beta1 > ${SWAGGER_ROOT_DIR}/v1beta1.json
curl ${SWAGGER_API_PATH}api/v1beta2 > ${SWAGGER_ROOT_DIR}/v1beta2.json
curl ${SWAGGER_API_PATH}api/v1beta3 > ${SWAGGER_ROOT_DIR}/v1beta3.json
kube::log::status "SUCCESS"

View File

@@ -43,7 +43,7 @@ func InterpretCreateError(err error, kind, name string) error {
}
}
// InterpretUpdateError converts a generic etcd error on a create
// InterpretUpdateError converts a generic etcd error on a update
// operation into the appropriate API error.
func InterpretUpdateError(err error, kind, name string) error {
switch {
@@ -54,7 +54,7 @@ func InterpretUpdateError(err error, kind, name string) error {
}
}
// InterpretDeleteError converts a generic etcd error on a create
// InterpretDeleteError converts a generic etcd error on a delete
// operation into the appropriate API error.
func InterpretDeleteError(err error, kind, name string) error {
switch {

View File

@@ -35,8 +35,6 @@ func init() {
&NodeList{},
&Node{},
&Status{},
&OperationList{},
&Operation{},
&Endpoints{},
&EndpointsList{},
&Binding{},
@@ -56,8 +54,6 @@ func init() {
// Legacy names are supported
Scheme.AddKnownTypeWithName("", "Minion", &Node{})
Scheme.AddKnownTypeWithName("", "MinionList", &NodeList{})
Scheme.AddKnownTypeWithName("", "ServerOp", &Operation{})
Scheme.AddKnownTypeWithName("", "ServerOpList", &OperationList{})
}
func (*Pod) IsAnAPIObject() {}
@@ -73,8 +69,6 @@ func (*Node) IsAnAPIObject() {}
func (*NodeList) IsAnAPIObject() {}
func (*Binding) IsAnAPIObject() {}
func (*Status) IsAnAPIObject() {}
func (*Operation) IsAnAPIObject() {}
func (*OperationList) IsAnAPIObject() {}
func (*Event) IsAnAPIObject() {}
func (*EventList) IsAnAPIObject() {}
func (*ContainerManifest) IsAnAPIObject() {}

View File

@@ -292,6 +292,8 @@ type Probe struct {
Handler `json:",inline"`
// Length of time before health checking is activated. In seconds.
InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty"`
// Length of time before health checking times out. In seconds.
TimeoutSeconds int64 `json:"timeoutSeconds,omitempty"`
}
// PullPolicy describes a policy for if/when to pull a container image
@@ -1028,20 +1030,6 @@ const (
CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported"
)
// Operation is an operation delivered to API clients.
type Operation struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty"`
}
// OperationList is a list of operations, as delivered to API clients.
type OperationList struct {
TypeMeta `json:",inline"`
ListMeta `json:"metadata,omitempty"`
Items []Operation `json:"items"`
}
// ObjectReference contains enough information to let you inspect or modify the referred object.
type ObjectReference struct {
Kind string `json:"kind,omitempty"`

View File

@@ -1050,6 +1050,7 @@ func init() {
return err
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
return nil
},
func(in *LivenessProbe, out *newer.Probe, s conversion.Scope) error {
@@ -1063,6 +1064,7 @@ func init() {
return err
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
return nil
},
)

View File

@@ -39,8 +39,6 @@ func init() {
&MinionList{},
&Binding{},
&Status{},
&ServerOp{},
&ServerOpList{},
&Event{},
&EventList{},
&ContainerManifest{},
@@ -57,8 +55,6 @@ func init() {
// Future names are supported
api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{})
api.Scheme.AddKnownTypeWithName("v1beta1", "NodeList", &MinionList{})
api.Scheme.AddKnownTypeWithName("v1beta1", "Operation", &ServerOp{})
api.Scheme.AddKnownTypeWithName("v1beta1", "OperationList", &ServerOpList{})
}
func (*Pod) IsAnAPIObject() {}
@@ -74,8 +70,6 @@ func (*Minion) IsAnAPIObject() {}
func (*MinionList) IsAnAPIObject() {}
func (*Binding) IsAnAPIObject() {}
func (*Status) IsAnAPIObject() {}
func (*ServerOp) IsAnAPIObject() {}
func (*ServerOpList) IsAnAPIObject() {}
func (*Event) IsAnAPIObject() {}
func (*EventList) IsAnAPIObject() {}
func (*ContainerManifest) IsAnAPIObject() {}

View File

@@ -232,6 +232,8 @@ type LivenessProbe struct {
Exec *ExecAction `json:"exec,omitempty" description:"parameters for exec-based liveness probe"`
// Length of time before health checking is activated. In seconds.
InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty" description:"number of seconds after the container has started before liveness probes are initiated"`
// Length of time before health checking times out. In seconds.
TimeoutSeconds int64 `json:"timeoutSeconds,omitempty" description:"number of seconds after which liveness probes timeout; defaults to 1 second"`
}
// PullPolicy describes a policy for if/when to pull a container image
@@ -808,17 +810,6 @@ const (
CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported"
)
// ServerOp is an operation delivered to API clients.
type ServerOp struct {
TypeMeta `json:",inline"`
}
// ServerOpList is a list of operations, as delivered to API clients.
type ServerOpList struct {
TypeMeta `json:",inline"`
Items []ServerOp `json:"items" description:"list of operations"`
}
// ObjectReference contains enough information to let you inspect or modify the referred object.
type ObjectReference struct {
Kind string `json:"kind,omitempty" description:"kind of the referent"`

View File

@@ -966,6 +966,7 @@ func init() {
return err
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
return nil
},
func(in *LivenessProbe, out *newer.Probe, s conversion.Scope) error {
@@ -979,6 +980,7 @@ func init() {
return err
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
return nil
},
)

View File

@@ -39,8 +39,6 @@ func init() {
&MinionList{},
&Binding{},
&Status{},
&ServerOp{},
&ServerOpList{},
&Event{},
&EventList{},
&ContainerManifest{},
@@ -57,8 +55,6 @@ func init() {
// Future names are supported
api.Scheme.AddKnownTypeWithName("v1beta2", "Node", &Minion{})
api.Scheme.AddKnownTypeWithName("v1beta2", "NodeList", &MinionList{})
api.Scheme.AddKnownTypeWithName("v1beta2", "Operation", &ServerOp{})
api.Scheme.AddKnownTypeWithName("v1beta2", "OperationList", &ServerOpList{})
}
func (*Pod) IsAnAPIObject() {}
@@ -74,8 +70,6 @@ func (*Minion) IsAnAPIObject() {}
func (*MinionList) IsAnAPIObject() {}
func (*Binding) IsAnAPIObject() {}
func (*Status) IsAnAPIObject() {}
func (*ServerOp) IsAnAPIObject() {}
func (*ServerOpList) IsAnAPIObject() {}
func (*Event) IsAnAPIObject() {}
func (*EventList) IsAnAPIObject() {}
func (*ContainerManifest) IsAnAPIObject() {}

View File

@@ -191,6 +191,8 @@ type LivenessProbe struct {
Exec *ExecAction `json:"exec,omitempty" description:"parameters for exec-based liveness probe"`
// Length of time before health checking is activated. In seconds.
InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty" description:"number of seconds after the container has started before liveness probes are initiated"`
// Length of time before health checking times out. In seconds.
TimeoutSeconds int64 `json:"timeoutSeconds,omitempty" description:"number of seconds after which liveness probes timeout; defaults to 1 second"`
}
// PullPolicy describes a policy for if/when to pull a container image
@@ -782,17 +784,6 @@ const (
CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported"
)
// ServerOp is an operation delivered to API clients.
type ServerOp struct {
TypeMeta `json:",inline"`
}
// ServerOpList is a list of operations, as delivered to API clients.
type ServerOpList struct {
TypeMeta `json:",inline"`
Items []ServerOp `json:"items" description:"list of operations"`
}
// ObjectReference contains enough information to let you inspect or modify the referred object.
type ObjectReference struct {
Kind string `json:"kind,omitempty" description:"kind of the referent"`

View File

@@ -43,8 +43,6 @@ func init() {
&NodeList{},
&Binding{},
&Status{},
&Operation{},
&OperationList{},
&Event{},
&EventList{},
&List{},
@@ -57,8 +55,6 @@ func init() {
// Legacy names are supported
api.Scheme.AddKnownTypeWithName("v1beta3", "Minion", &Node{})
api.Scheme.AddKnownTypeWithName("v1beta3", "MinionList", &NodeList{})
api.Scheme.AddKnownTypeWithName("v1beta3", "ServerOp", &Operation{})
api.Scheme.AddKnownTypeWithName("v1beta3", "ServerOpList", &OperationList{})
}
func (*Pod) IsAnAPIObject() {}
@@ -78,8 +74,6 @@ func (*Node) IsAnAPIObject() {}
func (*NodeList) IsAnAPIObject() {}
func (*Binding) IsAnAPIObject() {}
func (*Status) IsAnAPIObject() {}
func (*Operation) IsAnAPIObject() {}
func (*OperationList) IsAnAPIObject() {}
func (*Event) IsAnAPIObject() {}
func (*EventList) IsAnAPIObject() {}
func (*List) IsAnAPIObject() {}

View File

@@ -310,6 +310,8 @@ type Probe struct {
Handler `json:",inline"`
// Length of time before health checking is activated. In seconds.
InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty"`
// Length of time before health checking times out. In seconds.
TimeoutSeconds int64 `json:"timeoutSeconds,omitempty"`
}
// PullPolicy describes a policy for if/when to pull a container image
@@ -1008,22 +1010,6 @@ const (
CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported"
)
// Operation is a request from a client that has not yet been satisfied. The name of an
// Operation is assigned by the server when an operation is started, and can be used by
// clients to retrieve the final result of the operation at a later time.
type Operation struct {
TypeMeta `json:",inline"`
ObjectMeta `json:"metadata"`
}
// OperationList is a list of operations, as delivered to API clients.
type OperationList struct {
TypeMeta `json:",inline"`
ListMeta `json:"metadata,omitempty"`
Items []Operation `json:"items"`
}
// ObjectReference contains enough information to let you inspect or modify the referred object.
type ObjectReference struct {
Kind string `json:"kind,omitempty"`

View File

@@ -214,7 +214,7 @@ func TestValidatePorts(t *testing.T) {
"invalid container port": {[]api.Port{{ContainerPort: 65536, Protocol: "TCP"}}, errors.ValidationErrorTypeInvalid, "[0].containerPort"},
"invalid host port": {[]api.Port{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}}, errors.ValidationErrorTypeInvalid, "[0].hostPort"},
"invalid protocol": {[]api.Port{{ContainerPort: 80, Protocol: "ICMP"}}, errors.ValidationErrorTypeNotSupported, "[0].protocol"},
"protocol required": {[]api.Port{{Name: "abc", ContainerPort: 80}}, errors.ValidationErrorTypeRequired, "[0].protocol"}, //yjhong
"protocol required": {[]api.Port{{Name: "abc", ContainerPort: 80}}, errors.ValidationErrorTypeRequired, "[0].protocol"},
}
for k, v := range errorCases {
errs := validatePorts(v.P)
@@ -371,23 +371,26 @@ func TestValidateContainers(t *testing.T) {
AllowPrivileged: false,
})
errorCases := map[string][]api.Container{
"zero-length name": {{Name: "", Image: "image"}},
"name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image"}},
"name not a DNS label": {{Name: "a.b.c", Image: "image"}},
"zero-length name": {{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent"}},
"name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent"}},
"name not a DNS label": {{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent"}},
"name not unique": {
{Name: "abc", Image: "image"},
{Name: "abc", Image: "image"},
{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"},
{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"},
},
"zero-length image": {{Name: "abc", Image: ""}},
"zero-length image": {{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent"}},
"host port not unique": {
{Name: "abc", Image: "image", Ports: []api.Port{{ContainerPort: 80, HostPort: 80}}},
{Name: "def", Image: "image", Ports: []api.Port{{ContainerPort: 81, HostPort: 80}}},
{Name: "abc", Image: "image", Ports: []api.Port{{ContainerPort: 80, HostPort: 80, Protocol: "TCP"}},
ImagePullPolicy: "IfNotPresent"},
{Name: "def", Image: "image", Ports: []api.Port{{ContainerPort: 81, HostPort: 80, Protocol: "TCP"}},
ImagePullPolicy: "IfNotPresent"},
},
"invalid env var name": {
{Name: "abc", Image: "image", Env: []api.EnvVar{{Name: "ev.1"}}},
{Name: "abc", Image: "image", Env: []api.EnvVar{{Name: "ev.1"}}, ImagePullPolicy: "IfNotPresent"},
},
"unknown volume name": {
{Name: "abc", Image: "image", VolumeMounts: []api.VolumeMount{{Name: "anything", MountPath: "/foo"}}},
{Name: "abc", Image: "image", VolumeMounts: []api.VolumeMount{{Name: "anything", MountPath: "/foo"}},
ImagePullPolicy: "IfNotPresent"},
},
"invalid lifecycle, no exec command.": {
{
@@ -398,6 +401,7 @@ func TestValidateContainers(t *testing.T) {
Exec: &api.ExecAction{},
},
},
ImagePullPolicy: "IfNotPresent",
},
},
"invalid lifecycle, no http path.": {
@@ -409,6 +413,7 @@ func TestValidateContainers(t *testing.T) {
HTTPGet: &api.HTTPGetAction{},
},
},
ImagePullPolicy: "IfNotPresent",
},
},
"invalid lifecycle, no action.": {
@@ -418,6 +423,7 @@ func TestValidateContainers(t *testing.T) {
Lifecycle: &api.Lifecycle{
PreStop: &api.Handler{},
},
ImagePullPolicy: "IfNotPresent",
},
},
"privilege disabled": {
@@ -432,6 +438,7 @@ func TestValidateContainers(t *testing.T) {
"disk": resource.MustParse("10G"),
},
},
ImagePullPolicy: "IfNotPresent",
},
},
"Resource CPU invalid": {
@@ -441,6 +448,7 @@ func TestValidateContainers(t *testing.T) {
Resources: api.ResourceRequirementSpec{
Limits: getResourceLimits("-10", "0"),
},
ImagePullPolicy: "IfNotPresent",
},
},
"Resource Memory invalid": {
@@ -450,6 +458,7 @@ func TestValidateContainers(t *testing.T) {
Resources: api.ResourceRequirementSpec{
Limits: getResourceLimits("0", "-10"),
},
ImagePullPolicy: "IfNotPresent",
},
},
}
@@ -553,17 +562,26 @@ func TestValidateManifest(t *testing.T) {
}
errorCases := map[string]api.ContainerManifest{
"empty version": {Version: "", ID: "abc"},
"invalid version": {Version: "bogus", ID: "abc"},
"empty version": {Version: "", ID: "abc",
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst},
"invalid version": {Version: "bogus", ID: "abc",
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst},
"invalid volume name": {
Version: "v1beta1",
ID: "abc",
Volumes: []api.Volume{{Name: "vol.1"}},
Version: "v1beta1",
ID: "abc",
Volumes: []api.Volume{{Name: "vol.1", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}}},
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
"invalid container name": {
Version: "v1beta1",
ID: "abc",
Containers: []api.Container{{Name: "ctr.1", Image: "image"}},
Version: "v1beta1",
ID: "abc",
Containers: []api.Container{{Name: "ctr.1", Image: "image", ImagePullPolicy: "IfNotPresent",
TerminationMessagePath: "/foo/bar"}},
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
}
for k, v := range errorCases {
@@ -602,13 +620,22 @@ func TestValidatePodSpec(t *testing.T) {
failureCases := map[string]api.PodSpec{
"bad volume": {
Volumes: []api.Volume{{}},
Volumes: []api.Volume{{}},
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
"bad container": {
Containers: []api.Container{{}},
Containers: []api.Container{{}},
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
"bad DNS policy": {
DNSPolicy: api.DNSPolicy("invalid"),
DNSPolicy: api.DNSPolicy("invalid"),
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
},
"bad restart policy": {
RestartPolicy: api.RestartPolicy{},
DNSPolicy: api.DNSClusterFirst,
},
}
for k, v := range failureCases {
@@ -652,8 +679,20 @@ func TestValidatePod(t *testing.T) {
}
errorCases := map[string]api.Pod{
"bad name": {ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"}},
"bad namespace": {ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}},
"bad name": {
ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
"bad namespace": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
"bad spec": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"},
Spec: api.PodSpec{
@@ -668,6 +707,10 @@ func TestValidatePod(t *testing.T) {
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
"bad annotation": {
ObjectMeta: api.ObjectMeta{
@@ -677,6 +720,10 @@ func TestValidatePod(t *testing.T) {
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
}
for k, v := range errorCases {
@@ -857,6 +904,26 @@ func TestValidatePodUpdate(t *testing.T) {
false,
"port change",
},
{
api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"foo": "bar",
},
},
},
api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"Bar": "foo",
},
},
},
true,
"bad label change",
},
}
for _, test := range tests {
@@ -914,18 +981,53 @@ func TestValidateBoundPods(t *testing.T) {
}
errorCases := map[string]api.Pod{
"bad name": {ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"}},
"bad namespace": {ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}},
"zero-length name": {
ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
"bad namespace": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
"bad spec": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"},
Spec: api.PodSpec{
Containers: []api.Container{{}},
Containers: []api.Container{{Name: "name", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
"name > 253 characters": {
ObjectMeta: api.ObjectMeta{Name: strings.Repeat("a", 254), Namespace: "ns"},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
"name not a DNS subdomain": {
ObjectMeta: api.ObjectMeta{Name: "a..b.c", Namespace: "ns"},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
"name with underscore": {
ObjectMeta: api.ObjectMeta{Name: "a_b_c", Namespace: "ns"},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
}
for k, v := range errorCases {
if errs := ValidatePod(&v); len(errs) == 0 {
t.Errorf("expected failure for %s", k)
if errs := ValidatePod(&v); len(errs) != 1 {
t.Errorf("expected one failure for %s; got %d: %s", k, len(errs), errs)
}
}
}
@@ -1314,7 +1416,9 @@ func TestValidateReplicationController(t *testing.T) {
invalidVolumePodTemplate := api.PodTemplate{
Spec: api.PodTemplateSpec{
Spec: api.PodSpec{
Volumes: []api.Volume{{Name: "gcepd", Source: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}},
Volumes: []api.Volume{{Name: "gcepd", Source: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}},
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
DNSPolicy: api.DNSClusterFirst,
},
},
}
@@ -1502,24 +1606,6 @@ func TestValidateReplicationController(t *testing.T) {
}
}
func TestValidateBoundPodNoName(t *testing.T) {
errorCases := map[string]api.BoundPod{
// manifest is tested in api/validation_test.go, ensure it is invoked
"empty version": {ObjectMeta: api.ObjectMeta{Name: "test"}, Spec: api.PodSpec{Containers: []api.Container{{Name: ""}}}},
// Name
"zero-length name": {ObjectMeta: api.ObjectMeta{Name: ""}},
"name > 255 characters": {ObjectMeta: api.ObjectMeta{Name: strings.Repeat("a", 256)}},
"name not a DNS subdomain": {ObjectMeta: api.ObjectMeta{Name: "a.b.c."}},
"name with underscore": {ObjectMeta: api.ObjectMeta{Name: "a_b_c"}},
}
for k, v := range errorCases {
if errs := ValidateBoundPod(&v); len(errs) == 0 {
t.Errorf("expected failure for %s", k)
}
}
}
func TestValidateMinion(t *testing.T) {
validSelector := map[string]string{"a": "b"}
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
@@ -1692,6 +1778,17 @@ func TestValidateMinionUpdate(t *testing.T) {
Labels: map[string]string{"bar": "fooobaz"},
},
}, true},
{api.Node{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "baz"},
},
}, api.Node{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"Foo": "baz"},
},
}, false},
}
for i, test := range tests {
errs := ValidateMinionUpdate(&test.oldMinion, &test.minion)
@@ -1848,6 +1945,19 @@ func TestValidateServiceUpdate(t *testing.T) {
PortalIP: "127.0.0.2",
},
}, false},
{ // 10
api.Service{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "baz"},
},
},
api.Service{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"Foo": "baz"},
},
}, false},
}
for i, test := range tests {
errs := ValidateServiceUpdate(&test.oldService, &test.service)

View File

@@ -17,7 +17,6 @@ limitations under the License.
package apiserver
import (
"sort"
"strconv"
"sync"
"sync/atomic"
@@ -79,23 +78,6 @@ func (ops *Operations) insert(op *Operation) {
ops.ops[op.ID] = op
}
// List lists operations for an API client.
func (ops *Operations) List() *api.OperationList {
ops.lock.Lock()
defer ops.lock.Unlock()
ids := []string{}
for id := range ops.ops {
ids = append(ids, id)
}
sort.StringSlice(ids).Sort()
ol := &api.OperationList{}
for _, id := range ids {
ol.Items = append(ol.Items, api.Operation{ObjectMeta: api.ObjectMeta{Name: id}})
}
return ol
}
// Get returns the operation with the given ID, or nil.
func (ops *Operations) Get(id string) *Operation {
ops.lock.Lock()

View File

@@ -46,7 +46,7 @@ type validator struct {
}
// TODO: can this use pkg/probe/http
func (s *Server) check(client httpGet) (probe.Status, string, error) {
func (s *Server) check(client httpGet) (probe.Result, string, error) {
resp, err := client.Get("http://" + net.JoinHostPort(s.Addr, strconv.Itoa(s.Port)) + s.Path)
if err != nil {
return probe.Unknown, "", err
@@ -66,7 +66,7 @@ func (s *Server) check(client httpGet) (probe.Status, string, error) {
type ServerStatus struct {
Component string `json:"component,omitempty"`
Health string `json:"health,omitempty"`
HealthCode probe.Status `json:"healthCode,omitempty"`
HealthCode probe.Result `json:"healthCode,omitempty"`
Msg string `json:"msg,omitempty"`
Err string `json:"err,omitempty"`
}

View File

@@ -54,7 +54,7 @@ func TestValidate(t *testing.T) {
tests := []struct {
err error
data string
expectedStatus probe.Status
expectedStatus probe.Result
code int
expectErr bool
}{

View File

@@ -41,7 +41,7 @@ type KubeletClient interface {
// KubeletHealthchecker is an interface for healthchecking kubelets
type KubeletHealthChecker interface {
HealthCheck(host string) (probe.Status, error)
HealthCheck(host string) (probe.Result, error)
}
// PodInfoGetter is an interface for things that can get information about a pod's containers.
@@ -134,7 +134,7 @@ func (c *HTTPKubeletClient) GetPodStatus(host, podNamespace, podID string) (api.
return status, nil
}
func (c *HTTPKubeletClient) HealthCheck(host string) (probe.Status, error) {
func (c *HTTPKubeletClient) HealthCheck(host string) (probe.Result, error) {
return httprobe.DoHTTPProbe(fmt.Sprintf("%s/healthz", c.url(host)), c.Client)
}
@@ -148,6 +148,6 @@ func (c FakeKubeletClient) GetPodStatus(host, podNamespace string, podID string)
return api.PodStatusResult{}, errors.New("Not Implemented")
}
func (c FakeKubeletClient) HealthCheck(host string) (probe.Status, error) {
func (c FakeKubeletClient) HealthCheck(host string) (probe.Result, error) {
return probe.Unknown, errors.New("Not Implemented")
}

View File

@@ -102,7 +102,7 @@ func (m *FakeNodeHandler) Update(node *api.Node) (*api.Node, error) {
// FakeKubeletClient is a fake implementation of KubeletClient.
type FakeKubeletClient struct {
Status probe.Status
Status probe.Result
Err error
}
@@ -110,7 +110,7 @@ func (c *FakeKubeletClient) GetPodStatus(host, podNamespace, podID string) (api.
return api.PodStatusResult{}, errors.New("Not Implemented")
}
func (c *FakeKubeletClient) HealthCheck(host string) (probe.Status, error) {
func (c *FakeKubeletClient) HealthCheck(host string) (probe.Result, error) {
return c.Status, c.Err
}

View File

@@ -17,11 +17,17 @@ limitations under the License.
package constraint
import (
"fmt"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
)
// Allowed returns true if pods is a collection of bound pods
// which can run without conflict on a single minion.
func Allowed(pods []api.BoundPod) bool {
return !PortsConflict(pods)
func Allowed(pods []api.BoundPod) []error {
errors := []error{}
for _, port := range hostPortsConflict(pods) {
errors = append(errors, fmt.Errorf("host port %v is already in use", port))
}
return errors
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package constraint
import (
"fmt"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
@@ -40,11 +41,11 @@ func podWithContainers(containers ...api.Container) api.BoundPod {
func TestAllowed(t *testing.T) {
table := []struct {
allowed bool
pods []api.BoundPod
err string
pods []api.BoundPod
}{
{
allowed: true,
err: "[]",
pods: []api.BoundPod{
podWithContainers(
containerWithHostPorts(1, 2, 3),
@@ -57,7 +58,7 @@ func TestAllowed(t *testing.T) {
},
},
{
allowed: true,
err: "[]",
pods: []api.BoundPod{
podWithContainers(
containerWithHostPorts(0, 0),
@@ -70,7 +71,7 @@ func TestAllowed(t *testing.T) {
},
},
{
allowed: false,
err: "[host port 3 is already in use]",
pods: []api.BoundPod{
podWithContainers(
containerWithHostPorts(3, 3),
@@ -78,7 +79,7 @@ func TestAllowed(t *testing.T) {
},
},
{
allowed: false,
err: "[host port 6 is already in use]",
pods: []api.BoundPod{
podWithContainers(
containerWithHostPorts(6),
@@ -91,7 +92,7 @@ func TestAllowed(t *testing.T) {
}
for _, item := range table {
if e, a := item.allowed, Allowed(item.pods); e != a {
if e, a := item.err, Allowed(item.pods); e != fmt.Sprintf("%v", a) {
t.Errorf("Expected %v, got %v: \n%v\v", e, a, item.pods)
}
}

View File

@@ -20,10 +20,12 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
)
// PortsConflict returns true iff two containers attempt to expose
// the same host port.
func PortsConflict(pods []api.BoundPod) bool {
// hostPortsConflict returns an array of host ports that at least two
// containers attempt to expose. The array is empty if no such port
// exists.
func hostPortsConflict(pods []api.BoundPod) []int {
hostPorts := map[int]struct{}{}
conflictingPorts := []int{}
for _, pod := range pods {
for _, container := range pod.Spec.Containers {
for _, port := range container.Ports {
@@ -31,11 +33,11 @@ func PortsConflict(pods []api.BoundPod) bool {
continue
}
if _, exists := hostPorts[port.HostPort]; exists {
return true
conflictingPorts = append(conflictingPorts, port.HostPort)
}
hostPorts[port.HostPort] = struct{}{}
}
}
}
return false
return conflictingPorts
}

View File

@@ -141,11 +141,24 @@ func (rm *ReplicationManager) watchControllers(resourceVersion *string) {
}
if event.Type == watch.Error {
util.HandleError(fmt.Errorf("error from watch during sync: %v", errors.FromObject(event.Object)))
// Clear the resource version, this may cause us to skip some elements on the watch,
// but we'll catch them on the synchronize() call, so it works out.
*resourceVersion = ""
continue
}
glog.V(4).Infof("Got watch: %#v", event)
rc, ok := event.Object.(*api.ReplicationController)
if !ok {
if status, ok := event.Object.(*api.Status); ok {
if status.Status == api.StatusFailure {
glog.Errorf("failed to watch: %v", status)
// Clear resource version here, as above, this won't hurt consistency, but we
// should consider introspecting more carefully here. (or make the apiserver smarter)
// "why not both?"
*resourceVersion = ""
continue
}
}
util.HandleError(fmt.Errorf("unexpected object: %#v", event.Object))
continue
}
@@ -166,7 +179,8 @@ func FilterActivePods(pods []api.Pod) []api.Pod {
var result []api.Pod
for _, value := range pods {
if api.PodSucceeded != value.Status.Phase &&
api.PodFailed != value.Status.Phase {
api.PodFailed != value.Status.Phase &&
api.PodUnknown != value.Status.Phase {
result = append(result, value)
}
}

View File

@@ -29,6 +29,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
cmdconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/config"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@@ -143,7 +144,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return kubectl.ReaperFor(mapping.Kind, client)
},
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
if GetFlagBool(cmd, "validate") {
if cmdutil.GetFlagBool(cmd, "validate") {
client, err := clients.ClientForVersion("")
if err != nil {
return nil, err
@@ -217,6 +218,62 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
return cmds
}
// PrintObject prints an api object given command line flags to modify the output format
func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error {
mapper, _ := f.Object(cmd)
_, kind, err := api.Scheme.ObjectVersionAndKind(obj)
if err != nil {
return err
}
mapping, err := mapper.RESTMapping(kind)
if err != nil {
return err
}
printer, err := f.PrinterForMapping(cmd, mapping)
if err != nil {
return err
}
return printer.PrintObj(obj, out)
}
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) {
printer, ok, err := cmdutil.PrinterForCommand(cmd)
if err != nil {
return nil, err
}
if ok {
clientConfig, err := f.ClientConfig(cmd)
checkErr(err)
defaultVersion := clientConfig.Version
version := cmdutil.OutputVersion(cmd, defaultVersion)
if len(version) == 0 {
version = mapping.APIVersion
}
if len(version) == 0 {
return nil, fmt.Errorf("you must specify an output-version when using this output format")
}
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version)
} else {
printer, err = f.Printer(cmd, mapping, cmdutil.GetFlagBool(cmd, "no-headers"))
if err != nil {
return nil, err
}
}
return printer, nil
}
// ClientMapperForCommand returns a ClientMapper for the given command and factory.
func (f *Factory) ClientMapperForCommand(cmd *cobra.Command) resource.ClientMapper {
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.RESTClient(cmd, mapping)
})
}
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
// 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me.
// 1. Merge together the kubeconfig itself. This is done with the following hierarchy and merge rules:
@@ -266,13 +323,6 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
return clientConfig
}
// ClientMapperForCommand returns a ClientMapper for the given command and factory.
func ClientMapperForCommand(cmd *cobra.Command, f *Factory) resource.ClientMapper {
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.RESTClient(cmd, mapping)
})
}
func checkErr(err error) {
if err != nil {
glog.FatalDepth(1, err)

View File

@@ -21,6 +21,7 @@ import (
"fmt"
"io"
"io/ioutil"
"os"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
@@ -203,3 +204,43 @@ func TestClientVersions(t *testing.T) {
}
}
}
func ExamplePrintReplicationController() {
f, tf, codec := NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(false)
tf.Client = &client.FakeRESTClient{
Codec: codec,
Client: nil,
}
cmd := f.NewCmdRunContainer(os.Stdout)
ctrl := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"foo": "bar"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
},
},
},
},
},
}
err := f.PrintObject(cmd, ctrl, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
// foo foo someimage foo=bar 1
}

View File

@@ -21,11 +21,12 @@ import (
"io"
"os"
"github.com/ghodss/yaml"
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
)
type viewOptions struct {
@@ -38,40 +39,45 @@ func NewCmdConfigView(out io.Writer, pathOptions *pathOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "view",
Short: "displays the specified .kubeconfig file or a merged result",
Long: `displays the specified .kubeconfig file or a merged result`,
Short: "displays merged .kubeconfig settings or a specified .kubeconfig file.",
Long: `displays merged .kubeconfig settings or a specified .kubeconfig file.
Examples:
// Show merged .kubeconfig settings.
$ kubectl config view
// Show only local ./.kubeconfig settings
$ kubectl config view --local`,
Run: func(cmd *cobra.Command, args []string) {
err := options.run()
printer, _, err := util.PrinterForCommand(cmd)
if err != nil {
fmt.Printf("%v\n", err)
glog.FatalDepth(1, err)
}
config, err := options.loadConfig()
if err != nil {
glog.FatalDepth(1, err)
}
err = printer.PrintObj(config, out)
if err != nil {
glog.FatalDepth(1, err)
}
},
}
cmd.Flags().BoolVar(&options.merge, "merge", false, "merge together the full hierarchy of .kubeconfig files")
util.AddPrinterFlags(cmd)
// Default to yaml
cmd.Flags().Set("output", "yaml")
cmd.Flags().BoolVar(&options.merge, "merge", true, "merge together the full hierarchy of .kubeconfig files")
return cmd
}
func (o viewOptions) run() error {
func (o viewOptions) loadConfig() (*clientcmdapi.Config, error) {
err := o.validate()
if err != nil {
return err
return nil, err
}
config, _, err := o.getStartingConfig()
if err != nil {
return err
}
content, err := yaml.Marshal(config)
if err != nil {
return err
}
fmt.Printf("%v", string(content))
return nil
return config, err
}
func (o viewOptions) validate() error {

View File

@@ -51,7 +51,7 @@ Examples:
checkErr(err)
mapper, typer := f.Object(cmd)
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
FilenameParam(flags.Filenames...).

View File

@@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
@@ -62,11 +63,11 @@ Examples:
checkErr(err)
mapper, typer := f.Object(cmd)
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(flags.Filenames...).
SelectorParam(GetFlagString(cmd, "selector")).
SelectorParam(cmdutil.GetFlagString(cmd, "selector")).
ResourceTypeOrNameArgs(args...).
Flatten().
Do()

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
@@ -36,7 +37,7 @@ given resource.`,
checkErr(err)
mapper, _ := f.Object(cmd)
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
mapping, namespace, name := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace)
describer, err := f.Describer(cmd, mapping)
checkErr(err)

View File

@@ -22,6 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
@@ -51,27 +52,28 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream
client, err := f.Client(cmd)
checkErr(err)
generatorName := GetFlagString(cmd, "generator")
generatorName := util.GetFlagString(cmd, "generator")
generator, found := kubectl.Generators[generatorName]
if !found {
usageError(cmd, fmt.Sprintf("Generator: %s not found.", generator))
}
if GetFlagInt(cmd, "port") < 1 {
if util.GetFlagInt(cmd, "port") < 1 {
usageError(cmd, "--port is required and must be a positive integer.")
}
names := generator.ParamNames()
params := kubectl.MakeParams(cmd, names)
if len(GetFlagString(cmd, "service-name")) == 0 {
if len(util.GetFlagString(cmd, "service-name")) == 0 {
params["name"] = args[0]
} else {
params["name"] = GetFlagString(cmd, "service-name")
params["name"] = util.GetFlagString(cmd, "service-name")
}
if _, found := params["selector"]; !found {
rc, err := client.ReplicationControllers(namespace).Get(args[0])
checkErr(err)
params["selector"] = kubectl.MakeLabels(rc.Spec.Selector)
}
if GetFlagBool(cmd, "create-external-load-balancer") {
if util.GetFlagBool(cmd, "create-external-load-balancer") {
params["create-external-load-balancer"] = "true"
}
@@ -81,22 +83,22 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream
service, err := generator.Generate(params)
checkErr(err)
inline := GetFlagString(cmd, "overrides")
inline := util.GetFlagString(cmd, "overrides")
if len(inline) > 0 {
Merge(service, inline, "Service")
util.Merge(service, inline, "Service")
}
// TODO: extract this flag to a central location, when such a location exists.
if !GetFlagBool(cmd, "dry-run") {
if !util.GetFlagBool(cmd, "dry-run") {
service, err = client.Services(namespace).Create(service.(*api.Service))
checkErr(err)
}
err = PrintObject(cmd, service, f, out)
err = f.PrintObject(cmd, service, out)
checkErr(err)
},
}
AddPrinterFlags(cmd)
util.AddPrinterFlags(cmd)
cmd.Flags().String("generator", "service/v1", "The name of the api generator that you want to use. Default 'service/v1'")
cmd.Flags().String("protocol", "TCP", "The network protocol for the service you want to be created. Default 'tcp'")
cmd.Flags().Int("port", -1, "The port that the service should serve on. Required.")

View File

@@ -23,6 +23,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@@ -59,7 +60,7 @@ Examples:
RunGet(f, out, cmd, args)
},
}
AddPrinterFlags(cmd)
util.AddPrinterFlags(cmd)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.")
cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.")
@@ -70,16 +71,16 @@ Examples:
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd
// TODO: return an error instead of using glog.Fatal and checkErr
func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
selector := GetFlagString(cmd, "selector")
selector := util.GetFlagString(cmd, "selector")
mapper, typer := f.Object(cmd)
cmdNamespace, err := f.DefaultNamespace(cmd)
checkErr(err)
// handle watch separately since we cannot watch multiple resource types
isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only")
isWatch, isWatchOnly := util.GetFlagBool(cmd, "watch"), util.GetFlagBool(cmd, "watch-only")
if isWatch || isWatchOnly {
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)).
NamespaceParam(cmdNamespace).DefaultNamespace().
SelectorParam(selector).
ResourceTypeOrNameArgs(args...).
@@ -89,7 +90,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
mapping, err := r.ResourceMapping()
checkErr(err)
printer, err := PrinterForMapping(f, cmd, mapping)
printer, err := f.PrinterForMapping(cmd, mapping)
checkErr(err)
obj, err := r.Object()
@@ -115,12 +116,12 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
return
}
b := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)).
NamespaceParam(cmdNamespace).DefaultNamespace().
SelectorParam(selector).
ResourceTypeOrNameArgs(args...).
Latest()
printer, generic, err := PrinterForCommand(cmd)
printer, generic, err := util.PrinterForCommand(cmd)
checkErr(err)
if generic {
@@ -129,7 +130,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
defaultVersion := clientConfig.Version
// the outermost object will be converted to the output-version
version := outputVersion(cmd, defaultVersion)
version := util.OutputVersion(cmd, defaultVersion)
if len(version) == 0 {
// TODO: add a new ResourceBuilder mode for Object() that attempts to ensure the objects
// are in the appropriate version if one exists (and if not, use the best effort).
@@ -149,7 +150,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
// use the default printer for each object
err = b.Do().Visit(func(r *resource.Info) error {
printer, err := PrinterForMapping(f, cmd, r.Mapping)
printer, err := f.PrinterForMapping(cmd, r.Mapping)
if err != nil {
return err
}

View File

@@ -20,6 +20,7 @@ import (
"io"
"strconv"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
@@ -66,7 +67,7 @@ Examples:
}
follow := false
if GetFlagBool(cmd, "follow") {
if util.GetFlagBool(cmd, "follow") {
follow = true
}

View File

@@ -1,67 +0,0 @@
/*
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 cmd_test
import (
"fmt"
"os"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
. "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
)
func ExamplePrintReplicationController() {
f, tf, codec := NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(false)
tf.Client = &client.FakeRESTClient{
Codec: codec,
Client: nil,
}
cmd := f.NewCmdRunContainer(os.Stdout)
ctrl := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"foo": "bar"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
},
},
},
},
},
}
err := PrintObject(cmd, ctrl, f, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
// foo foo someimage foo=bar 1
}

View File

@@ -18,8 +18,10 @@ package cmd
import (
"io"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
@@ -30,18 +32,23 @@ func (f *Factory) NewCmdProxy(out io.Writer) *cobra.Command {
Short: "Run a proxy to the Kubernetes API server",
Long: `Run a proxy to the Kubernetes API server.`,
Run: func(cmd *cobra.Command, args []string) {
port := GetFlagInt(cmd, "port")
port := util.GetFlagInt(cmd, "port")
glog.Infof("Starting to serve on localhost:%d", port)
clientConfig, err := f.ClientConfig(cmd)
checkErr(err)
server, err := kubectl.NewProxyServer(GetFlagString(cmd, "www"), clientConfig)
staticPrefix := util.GetFlagString(cmd, "www-prefix")
if !strings.HasSuffix(staticPrefix, "/") {
staticPrefix += "/"
}
server, err := kubectl.NewProxyServer(util.GetFlagString(cmd, "www"), staticPrefix, clientConfig)
checkErr(err)
glog.Fatal(server.Serve(port))
},
}
cmd.Flags().StringP("www", "w", "", "Also serve static files from the given directory under the prefix /static")
cmd.Flags().StringP("www", "w", "", "Also serve static files from the given directory under the specified prefix")
cmd.Flags().StringP("www-prefix", "P", "/static/", "Prefix to serve static files under, if static file dir is specified")
cmd.Flags().IntP("port", "p", 8001, "The port on which to run the proxy")
return cmd
}

View File

@@ -21,6 +21,7 @@ import (
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
@@ -44,7 +45,7 @@ Examples:
$ kubectl resize --current-replicas=2 --replicas=3 replicationcontrollers foo
`,
Run: func(cmd *cobra.Command, args []string) {
count := GetFlagInt(cmd, "replicas")
count := util.GetFlagInt(cmd, "replicas")
if len(args) != 2 || count < 0 {
usageError(cmd, "--replicas=<count> <resource> <id>")
}
@@ -53,13 +54,13 @@ Examples:
checkErr(err)
mapper, _ := f.Object(cmd)
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
mapping, namespace, name := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace)
resizer, err := f.Resizer(cmd, mapping)
checkErr(err)
resourceVersion := GetFlagString(cmd, "resource-version")
currentSize := GetFlagInt(cmd, "current-replicas")
resourceVersion := util.GetFlagString(cmd, "resource-version")
currentSize := util.GetFlagInt(cmd, "current-replicas")
s, err := resizer.Resize(namespace, name, &kubectl.ResizePrecondition{currentSize, resourceVersion}, uint(count))
checkErr(err)
fmt.Fprintf(out, "%s\n", s)

View File

@@ -22,6 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
@@ -48,13 +49,13 @@ $ kubectl rollingupdate frontend-v1 -f frontend-v2.json
$ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f -
<update pods of frontend-v1 using json data passed into stdin>`,
Run: func(cmd *cobra.Command, args []string) {
filename := GetFlagString(cmd, "filename")
filename := util.GetFlagString(cmd, "filename")
if len(filename) == 0 {
usageError(cmd, "Must specify filename for new controller")
}
period := GetFlagDuration(cmd, "update-period")
interval := GetFlagDuration(cmd, "poll-interval")
timeout := GetFlagDuration(cmd, "timeout")
period := util.GetFlagDuration(cmd, "update-period")
interval := util.GetFlagDuration(cmd, "poll-interval")
timeout := util.GetFlagDuration(cmd, "timeout")
if len(args) != 1 {
usageError(cmd, "Must specify the controller to update")
}
@@ -67,7 +68,7 @@ $ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f -
cmdApiVersion := clientConfig.Version
mapper, typer := f.Object(cmd)
mapping, namespace, newName, data := ResourceFromFile(filename, typer, mapper, schema, cmdApiVersion)
mapping, namespace, newName, data := util.ResourceFromFile(filename, typer, mapper, schema, cmdApiVersion)
if mapping.Kind != "ReplicationController" {
usageError(cmd, "%s does not specify a valid ReplicationController", filename)
}
@@ -78,7 +79,7 @@ $ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f -
cmdNamespace, err := f.DefaultNamespace(cmd)
checkErr(err)
err = CompareNamespace(cmdNamespace, namespace)
err = util.CompareNamespace(cmdNamespace, namespace)
checkErr(err)
client, err := f.Client(cmd)

View File

@@ -22,6 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
@@ -55,7 +56,7 @@ Examples:
client, err := f.Client(cmd)
checkErr(err)
generatorName := GetFlagString(cmd, "generator")
generatorName := util.GetFlagString(cmd, "generator")
generator, found := kubectl.Generators[generatorName]
if !found {
usageError(cmd, fmt.Sprintf("Generator: %s not found.", generator))
@@ -70,22 +71,22 @@ Examples:
controller, err := generator.Generate(params)
checkErr(err)
inline := GetFlagString(cmd, "overrides")
inline := util.GetFlagString(cmd, "overrides")
if len(inline) > 0 {
Merge(controller, inline, "ReplicationController")
util.Merge(controller, inline, "ReplicationController")
}
// TODO: extract this flag to a central location, when such a location exists.
if !GetFlagBool(cmd, "dry-run") {
if !util.GetFlagBool(cmd, "dry-run") {
controller, err = client.ReplicationControllers(namespace).Create(controller.(*api.ReplicationController))
checkErr(err)
}
err = PrintObject(cmd, controller, f, out)
err = f.PrintObject(cmd, controller, out)
checkErr(err)
},
}
AddPrinterFlags(cmd)
util.AddPrinterFlags(cmd)
cmd.Flags().String("generator", "run-container/v1", "The name of the api generator that you want to use. Default 'run-container-controller/v1'")
cmd.Flags().String("image", "", "The image for the container you wish to run.")
cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default 1")

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
@@ -42,7 +43,7 @@ Examples:
}
cmdNamespace, err := f.DefaultNamespace(cmd)
mapper, _ := f.Object(cmd)
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
mapping, namespace, name := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace)
reaper, err := f.Reaper(cmd, mapping)
checkErr(err)

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"io"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/spf13/cobra"
@@ -53,14 +54,14 @@ Examples:
checkErr(err)
mapper, typer := f.Object(cmd)
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
FilenameParam(flags.Filenames...).
Flatten().
Do()
patch := GetFlagString(cmd, "patch")
patch := cmdutil.GetFlagString(cmd, "patch")
if len(flags.Filenames) == 0 && len(patch) == 0 {
usageError(cmd, "Must specify --filename or --patch to update")
}
@@ -102,7 +103,7 @@ func updateWithPatch(cmd *cobra.Command, args []string, f *Factory, patch string
checkErr(err)
mapper, _ := f.Object(cmd)
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
mapping, namespace, name := cmdutil.ResourceFromArgs(cmd, args, mapper, cmdNamespace)
client, err := f.RESTClient(cmd, mapping)
checkErr(err)
@@ -110,7 +111,7 @@ func updateWithPatch(cmd *cobra.Command, args []string, f *Factory, patch string
obj, err := helper.Get(namespace, name)
checkErr(err)
Merge(obj, patch, mapping.Kind)
cmdutil.Merge(obj, patch, mapping.Kind)
data, err := helper.Codec.Encode(obj)
checkErr(err)

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
package util
import (
"encoding/json"
@@ -34,6 +34,18 @@ import (
"github.com/spf13/cobra"
)
func checkErr(err error) {
if err != nil {
glog.FatalDepth(1, err)
}
}
func usageError(cmd *cobra.Command, format string, args ...interface{}) {
glog.Errorf(format, args...)
glog.Errorf("See '%s -h' for help.", cmd.CommandPath())
os.Exit(1)
}
func GetFlagString(cmd *cobra.Command, flag string) string {
f := cmd.Flags().Lookup(flag)
if f == nil {

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
package util
import (
"reflect"

View File

@@ -14,16 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
package util
import (
"fmt"
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/spf13/cobra"
)
@@ -35,28 +29,8 @@ func AddPrinterFlags(cmd *cobra.Command) {
cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile.")
}
// PrintObject prints an api object given command line flags to modify the output format
func PrintObject(cmd *cobra.Command, obj runtime.Object, f *Factory, out io.Writer) error {
mapper, _ := f.Object(cmd)
_, kind, err := api.Scheme.ObjectVersionAndKind(obj)
if err != nil {
return err
}
mapping, err := mapper.RESTMapping(kind)
if err != nil {
return err
}
printer, err := PrinterForMapping(f, cmd, mapping)
if err != nil {
return err
}
return printer.PrintObj(obj, out)
}
// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
func outputVersion(cmd *cobra.Command, defaultVersion string) string {
// OutputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
func OutputVersion(cmd *cobra.Command, defaultVersion string) string {
outputVersion := GetFlagString(cmd, "output-version")
if len(outputVersion) == 0 {
outputVersion = defaultVersion
@@ -75,32 +49,3 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error
return kubectl.GetPrinter(outputFormat, templateFile)
}
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
func PrinterForMapping(f *Factory, cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) {
printer, ok, err := PrinterForCommand(cmd)
if err != nil {
return nil, err
}
if ok {
clientConfig, err := f.ClientConfig(cmd)
checkErr(err)
defaultVersion := clientConfig.Version
version := outputVersion(cmd, defaultVersion)
if len(version) == 0 {
version = mapping.APIVersion
}
if len(version) == 0 {
return nil, fmt.Errorf("you must specify an output-version when using this output format")
}
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version)
} else {
printer, err = f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers"))
if err != nil {
return nil, err
}
}
return printer, nil
}

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
package util
import (
"fmt"

View File

@@ -22,6 +22,7 @@ import (
"github.com/spf13/cobra"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
)
func (f *Factory) NewCmdVersion(out io.Writer) *cobra.Command {
@@ -29,7 +30,7 @@ func (f *Factory) NewCmdVersion(out io.Writer) *cobra.Command {
Use: "version",
Short: "Print version of client and server",
Run: func(cmd *cobra.Command, args []string) {
if GetFlagBool(cmd, "client") {
if util.GetFlagBool(cmd, "client") {
kubectl.GetClientVersion(out)
return
}

View File

@@ -33,7 +33,7 @@ type ProxyServer struct {
// NewProxyServer creates and installs a new ProxyServer.
// It automatically registers the created ProxyServer to http.DefaultServeMux.
func NewProxyServer(filebase string, cfg *client.Config) (*ProxyServer, error) {
func NewProxyServer(filebase string, staticPrefix string, cfg *client.Config) (*ProxyServer, error) {
prefix := cfg.Prefix
if prefix == "" {
prefix = "/api"
@@ -47,7 +47,7 @@ func NewProxyServer(filebase string, cfg *client.Config) (*ProxyServer, error) {
return nil, err
}
http.Handle("/api/", http.StripPrefix("/api/", proxy))
http.Handle("/static/", newFileHandler("/static/", filebase))
http.Handle(staticPrefix, newFileHandler(staticPrefix, filebase))
return proxy, nil
}

View File

@@ -25,6 +25,7 @@ import (
"io"
"io/ioutil"
"math/rand"
"os"
"os/exec"
"strconv"
"strings"
@@ -104,7 +105,7 @@ type dockerContainerCommandRunner struct {
var dockerAPIVersionWithExec = []uint{1, 15}
// Returns the major and minor version numbers of docker server.
func (d *dockerContainerCommandRunner) getDockerServerVersion() ([]uint, error) {
func (d *dockerContainerCommandRunner) GetDockerServerVersion() ([]uint, error) {
env, err := d.client.Version()
if err != nil {
return nil, fmt.Errorf("failed to get docker server version - %v", err)
@@ -127,7 +128,7 @@ func (d *dockerContainerCommandRunner) getDockerServerVersion() ([]uint, error)
}
func (d *dockerContainerCommandRunner) nativeExecSupportExists() (bool, error) {
version, err := d.getDockerServerVersion()
version, err := d.GetDockerServerVersion()
if err != nil {
return false, err
}
@@ -641,6 +642,35 @@ func parseImageName(image string) (string, string) {
return image, tag
}
// Get a docker endpoint, either from the string passed in, or $DOCKER_HOST environment variables
func getDockerEndpoint(dockerEndpoint string) string {
var endpoint string
if len(dockerEndpoint) > 0 {
endpoint = dockerEndpoint
} else if len(os.Getenv("DOCKER_HOST")) > 0 {
endpoint = os.Getenv("DOCKER_HOST")
} else {
endpoint = "unix:///var/run/docker.sock"
}
glog.Infof("Connecting to docker on %s", endpoint)
return endpoint
}
func ConnectToDockerOrDie(dockerEndpoint string) DockerInterface {
if dockerEndpoint == "fake://" {
return &FakeDockerClient{
VersionInfo: []string{"apiVersion=1.16"},
}
}
client, err := docker.NewClient(getDockerEndpoint(dockerEndpoint))
if err != nil {
glog.Fatal("Couldn't connect to docker.")
}
return client
}
type ContainerCommandRunner interface {
RunInContainer(containerID string, cmd []string) ([]byte, error)
GetDockerServerVersion() ([]uint, error)
}

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