Update guestbook example to use replication controller.

This commit is contained in:
rsokolowski 2015-02-06 00:20:27 +01:00
parent e77322a561
commit 635281ebe3
15 changed files with 315 additions and 264 deletions

View File

@ -108,12 +108,12 @@ func TestExampleObjectSchemas(t *testing.T) {
"service-list": &api.ServiceList{}, "service-list": &api.ServiceList{},
}, },
"../examples/guestbook": { "../examples/guestbook": {
"frontend-controller": &api.ReplicationController{}, "frontend-controller": &api.ReplicationController{},
"redis-slave-controller": &api.ReplicationController{}, "redis-slave-controller": &api.ReplicationController{},
"redis-master": &api.Pod{}, "redis-master-controller": &api.ReplicationController{},
"frontend-service": &api.Service{}, "frontend-service": &api.Service{},
"redis-master-service": &api.Service{}, "redis-master-service": &api.Service{},
"redis-slave-service": &api.Service{}, "redis-slave-service": &api.Service{},
}, },
"../examples/guestbook/v1beta3": { "../examples/guestbook/v1beta3": {
"frontend-controller": &api.ReplicationController{}, "frontend-controller": &api.ReplicationController{},

View File

@ -21,25 +21,36 @@ See the companion [Setup Kubernetes](https://github.com/GoogleCloudPlatform/kube
Note: This redis-master is *not* highly available. Making it highly available would be a very interesting, but intricate exersize - redis doesn't actually support multi-master deployments at the time of this writing, so high availability would be a somewhat tricky thing implement, and might involve periodic serialization to disk, and so on. Note: This redis-master is *not* highly available. Making it highly available would be a very interesting, but intricate exersize - redis doesn't actually support multi-master deployments at the time of this writing, so high availability would be a somewhat tricky thing implement, and might involve periodic serialization to disk, and so on.
Use (or just create) the file `examples/guestbook/redis-master.json` which describes a single pod running a redis key-value server in a container: Use (or just create) the file `examples/guestbook/redis-master-controller.json` which describes a single pod running a redis key-value server in a container:
Note that, although the redis server runs just with a single replica, we use replication controller to enforce that exactly one pod keeps running (e.g. in a event of node going down, the replication controller will ensure that the redis master gets restarted on a healthy node).
```js ```js
{ {
"id": "redis-master", "id": "redis-master-controller",
"kind": "Pod", "kind": "ReplicationController",
"apiVersion": "v1beta1", "apiVersion": "v1beta1",
"desiredState": { "desiredState": {
"manifest": { "replicas": 1,
"version": "v1beta1", "replicaSelector": {"name": "redis-master"},
"id": "redis-master", "podTemplate": {
"containers": [{ "desiredState": {
"name": "master", "manifest": {
"image": "dockerfile/redis", "version": "v1beta1",
"cpu": 100, "id": "redis-master",
"ports": [{ "containers": [{
"containerPort": 6379, # containerPort: Where traffic to redis ultimately is routed to. "name": "redis-master",
}] "image": "dockerfile/redis",
}] "cpu": 100,
"ports": [{
"containerPort": 6379, # containerPort: Where traffic to redis ultimately is routed to.
}]
}]
}
},
"labels": {
"name": "redis-master",
"app": "redis"
}
} }
}, },
"labels": { "labels": {
@ -51,31 +62,34 @@ Use (or just create) the file `examples/guestbook/redis-master.json` which descr
Now, create the redis pod in your Kubernetes cluster by running: Now, create the redis pod in your Kubernetes cluster by running:
```shell ```shell
kubectl create -f examples/guestbook/redis-master.json $ kubectl create -f examples/guestbook/redis-master-controller.json
$ cluster/kubectl.sh get rc
CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
redis-master-controller redis-master dockerfile/redis name=redis-master 1
``` ```
Once that's up you can list the pods in the cluster, to verify that the master is running: Once that's up you can list the pods in the cluster, to verify that the master is running:
```shell ```shell
kubectl get pods $ kubectl get pods
``` ```
You'll see all kubernetes components, most importantly the redis master pod. It will also display the machine that the pod is running on once it gets placed (may take up to thirty seconds): You'll see all kubernetes components, most importantly the redis master pod. It will also display the machine that the pod is running on once it gets placed (may take up to thirty seconds):
```shell ```shell
NAME IMAGE(S) HOST LABELS STATUS POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS
redis-master dockerfile/redis kubernetes-minion-2.c.myproject.internal/130.211.156.189 name=redis-master Running redis-master-controller-gb50a 10.244.3.7 redis-master dockerfile/redis kubernetes-minion-7agi.c.hazel-mote-834.internal/104.154.54.203 app=redis,name=redis-master Running
``` ```
If you ssh to that machine, you can run `docker ps` to see the actual pod: If you ssh to that machine, you can run `docker ps` to see the actual pod:
```shell ```shell
me@workstation$ gcloud compute ssh --zone us-central1-b kubernetes-minion-2 me@workstation$ gcloud compute ssh kubernetes-minion-7agi
me@kubernetes-minion-2:~$ sudo docker ps me@kubernetes-minion-7agi:~$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e3eed3e5e6d1 dockerfile/redis:latest "redis-server /etc/re 2 minutes ago Up 2 minutes k8s_master.9c0a9146_redis-master.etcd_6296f4bd-70fa-11e4-8469-0800279696e1_45331ebc 0ffef9649265 dockerfile/redis:latest "redis-server /etc/r About a minute ago Up About a minute k8s_redis-master.767aef46_redis-master-controller-gb50a.default.api_4530d7b3-ae5d-11e4-bf77-42010af0d719_579ee964
``` ```
(Note that initial `docker pull` may take a few minutes, depending on network conditions. You can monitor the status of this by running `journalctl -f -u docker` to check when the image is being downloaded. Of course, you can also run `journalctl -f -u kubelet` to see what state the kubelet is in as well during this time. (Note that initial `docker pull` may take a few minutes, depending on network conditions. You can monitor the status of this by running `journalctl -f -u docker` to check when the image is being downloaded. Of course, you can also run `journalctl -f -u kubelet` to see what state the kubelet is in as well during this time.
@ -113,14 +127,11 @@ to create the service by running:
$ kubectl create -f examples/guestbook/redis-master-service.json $ kubectl create -f examples/guestbook/redis-master-service.json
redis-master redis-master
$ cluster/kubectl.sh get services $ kubectl get services
NAME LABELS SELECTOR IP PORT NAME LABELS SELECTOR IP PORT
kubernetes <none> component=apiserver,provider=kubernetes 10.0.29.11 443 redis-master name=redis-master name=redis-master 10.0.246.242 6379
kubernetes-ro <none> component=apiserver,provider=kubernetes 10.0.141.25 80
redis-master name=redis-master name=redis-master 10.0.16.143 6379
``` ```
This will cause all pods to see the redis master apparently running on <ip>:6379. The traffic flow from slaves to masters can be described in two steps, like so. This will cause all pods to see the redis master apparently running on <ip>:6379. The traffic flow from slaves to masters can be described in two steps, like so.
- A *redis slave* will connect to "port" on the *redis master service* - A *redis slave* will connect to "port" on the *redis master service*
@ -140,26 +151,28 @@ Use the file `examples/guestbook/redis-slave-controller.json`, which looks like
"apiVersion": "v1beta1", "apiVersion": "v1beta1",
"desiredState": { "desiredState": {
"replicas": 2, "replicas": 2,
"replicaSelector": {"name": "redisslave"}, "replicaSelector": {"name": "redis-slave"},
"podTemplate": { "podTemplate": {
"desiredState": { "desiredState": {
"manifest": { "manifest": {
"version": "v1beta1", "version": "v1beta1",
"id": "redis-slave-controller", "id": "redis-slave",
"containers": [{ "containers": [{
"name": "slave", "name": "redis-slave",
"image": "brendanburns/redis-slave", "image": "brendanburns/redis-slave",
"cpu": 200, "cpu": 200,
"ports": [{"containerPort": 6379, "hostPort": 6380}] "ports": [{"containerPort": 6379}]
}] }]
} }
}, },
"labels": { "labels": {
"name": "redisslave", "name": "redis-slave",
"uses": "redis-master" "uses": "redis-master",
} "app": "redis"
}}, }
"labels": {"name": "redisslave"} }
},
"labels": {"name": "redis-slave"}
} }
``` ```
@ -169,9 +182,10 @@ to create the replication controller by running:
$ kubectl create -f examples/guestbook/redis-slave-controller.json $ kubectl create -f examples/guestbook/redis-slave-controller.json
redis-slave-controller redis-slave-controller
# kubectl.sh get replicationcontrollers $ kubectl get rc
NAME IMAGE(S) SELECTOR REPLICAS CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
redis-slave-controller brendanburns/redis-slave name=redisslave 2 redis-master-controller redis-master dockerfile/redis name=redis-master 1
redis-slave-controller redis-slave brendanburns/redis-slave name=redis-slave 2
``` ```
The redis slave configures itself by looking for the Kubernetes service environment variables in the container environment. In particular, the redis slave is started with the following command: The redis slave configures itself by looking for the Kubernetes service environment variables in the container environment. In particular, the redis slave is started with the following command:
@ -186,10 +200,10 @@ Once that's up you can list the pods in the cluster, to verify that the master a
```shell ```shell
$ kubectl get pods $ kubectl get pods
NAME IMAGE(S) HOST LABELS STATUS POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS
redis-master dockerfile/redis kubernetes-minion-2.c.myproject.internal/130.211.156.189 name=redis-master Running redis-master-controller-gb50a 10.244.3.7 redis-master dockerfile/redis kubernetes-minion-7agi.c.hazel-mote-834.internal/104.154.54.203 app=redis,name=redis-master Running
ee68394b-7fca-11e4-a220-42010af0a5f1 brendanburns/redis-slave kubernetes-minion-3.c.myproject.internal/130.211.179.212 name=redisslave,uses=redis-master Running redis-slave-controller-182tv 10.244.3.6 redis-slave brendanburns/redis-slave kubernetes-minion-7agi.c.hazel-mote-834.internal/104.154.54.203 app=redis,name=redis-slave,uses=redis-master Running
ee694768-7fca-11e4-a220-42010af0a5f1 brendanburns/redis-slave kubernetes-minion-4.c.myproject.internal/130.211.168.210 name=redisslave,uses=redis-master Running redis-slave-controller-zwk1b 10.244.2.8 redis-slave brendanburns/redis-slave kubernetes-minion-3vxa.c.hazel-mote-834.internal/104.154.54.6 app=redis,name=redis-slave,uses=redis-master Running
``` ```
You will see a single redis master pod and two redis slave pods. You will see a single redis master pod and two redis slave pods.
@ -208,29 +222,26 @@ The service specification for the slaves is in `examples/guestbook/redis-slave-s
"port": 6379, "port": 6379,
"containerPort": 6379, "containerPort": 6379,
"labels": { "labels": {
"name": "redisslave" "name": "redis-slave"
}, },
"selector": { "selector": {
"name": "redisslave" "name": "redis-slave"
} }
} }
``` ```
This time the selector for the service is `name=redisslave`, because that identifies the pods running redis slaves. It may also be helpful to set labels on your service itself as we've done here to make it easy to locate them with the `cluster/kubectl.sh get services -l "label=value"` command. This time the selector for the service is `name=redis-slave`, because that identifies the pods running redis slaves. It may also be helpful to set labels on your service itself as we've done here to make it easy to locate them with the `cluster/kubectl.sh get services -l "label=value"` command.
Now that you have created the service specification, create it in your cluster by running: Now that you have created the service specification, create it in your cluster by running:
```shell ```shell
$ cluster/kubectl.sh create -f examples/guestbook/redis-slave-service.json $ kubectl create -f examples/guestbook/redis-slave-service.json
redisslave redisslave
$ cluster/kubectl.sh get services $ kubectl get services
NAME LABELS SELECTOR IP PORT
NAME LABELS SELECTOR IP PORT redis-master name=redis-master name=redis-master 10.0.246.242 6379
kubernetes <none> component=apiserver,provider=kubernetes 10.0.29.11 443 redisslave name=redis-slave name=redis-slave 10.0.72.62 6379
kubernetes-ro <none> component=apiserver,provider=kubernetes 10.0.141.25 80
redis-master name=redis-master name=redis-master 10.0.16.143 6379
redisslave name=redisslave name=redisslave 10.0.217.148 6379
``` ```
### Step Five: Create the frontend pod ### Step Five: Create the frontend pod
@ -253,19 +264,20 @@ The pod is described in the file `examples/guestbook/frontend-controller.json`:
"desiredState": { "desiredState": {
"manifest": { "manifest": {
"version": "v1beta1", "version": "v1beta1",
"id": "frontend-controller", "id": "frontend",
"containers": [{ "containers": [{
"name": "php-redis", "name": "php-redis",
"image": "kubernetes/example-guestbook-php-redis", "image": "kubernetes/example-guestbook-php-redis",
"cpu": 100, "cpu": 100,
"memory": 50000000, "memory": 50000000,
"ports": [{"containerPort": 80, "hostPort": 8000}] "ports": [{"name": "http-server", "containerPort": 80}]
}] }]
} }
}, },
"labels": { "labels": {
"name": "frontend", "name": "frontend",
"uses": "redisslave,redis-master" "uses": "redis-slave,redis-master",
"app": "frontend"
} }
}}, }},
"labels": {"name": "frontend"} "labels": {"name": "frontend"}
@ -275,26 +287,27 @@ The pod is described in the file `examples/guestbook/frontend-controller.json`:
Using this file, you can turn up your frontend with: Using this file, you can turn up your frontend with:
```shell ```shell
$ cluster/kubectl.sh create -f examples/guestbook/frontend-controller.json $ kubectl create -f examples/guestbook/frontend-controller.json
frontend-controller frontend-controller
$ cluster/kubectl.sh get replicationcontrollers $ kubectl get rc
NAME IMAGE(S) SELECTOR REPLICAS CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
redis-slave-controller brendanburns/redis-slave name=redisslave 2 frontend-controller php-redis kubernetes/example-guestbook-php-redis name=frontend 3
frontend-controller kubernetes/example-guestbook-php-redis name=frontend 3 redis-master-controller redis-master dockerfile/redis name=redis-master 1
redis-slave-controller redis-slave brendanburns/redis-slave name=redis-slave 2
``` ```
Once that's up (it may take ten to thirty seconds to create the pods) you can list the pods in the cluster, to verify that the master, slaves and frontends are running: Once that's up (it may take ten to thirty seconds to create the pods) you can list the pods in the cluster, to verify that the master, slaves and frontends are running:
```shell ```shell
$ cluster/kubectl.sh get pods $ kubectl get pods
NAME IMAGE(S) HOST LABELS STATUS POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS
redis-master dockerfile/redis kubernetes-minion-2.c.myproject.internal/130.211.156.189 name=redis-master Running frontend-controller-5m1zc 10.244.1.131 php-redis kubernetes/example-guestbook-php-redis kubernetes-minion-3vxa.c.hazel-mote-834.internal/146.148.71.71 app=frontend,name=frontend,uses=redis-slave,redis-master Running
ee68394b-7fca-11e4-a220-42010af0a5f1 brendanburns/redis-slave kubernetes-minion-3.c.myproject.internal/130.211.179.212 name=redisslave,uses=redis-master Running frontend-controller-ckn42 10.244.2.134 php-redis kubernetes/example-guestbook-php-redis kubernetes-minion-by92.c.hazel-mote-834.internal/104.154.54.6 app=frontend,name=frontend,uses=redis-slave,redis-master Running
ee694768-7fca-11e4-a220-42010af0a5f1 brendanburns/redis-slave kubernetes-minion-4.c.myproject.internal/130.211.168.210 name=redisslave,uses=redis-master Running frontend-controller-v5drx 10.244.0.128 php-redis kubernetes/example-guestbook-php-redis kubernetes-minion-wilb.c.hazel-mote-834.internal/23.236.61.63 app=frontend,name=frontend,uses=redis-slave,redis-master Running
9fbad0d6-7fcb-11e4-a220-42010af0a5f1 kubernetes/example-guestbook-php-redis kubernetes-minion-1.c.myproject.internal/130.211.185.78 name=frontend,uses=redisslave,redis-master Running redis-master-controller-gb50a 10.244.3.7 redis-master dockerfile/redis kubernetes-minion-7agi.c.hazel-mote-834.internal/104.154.54.203 app=redis,name=redis-master Running
9fbbf70e-7fcb-11e4-a220-42010af0a5f1 kubernetes/example-guestbook-php-redis kubernetes-minion-2.c.myproject.internal/130.211.156.189 name=frontend,uses=redisslave,redis-master Running redis-slave-controller-182tv 10.244.3.6 redis-slave brendanburns/redis-slave kubernetes-minion-7agi.c.hazel-mote-834.internal/104.154.54.203 app=redis,name=redis-slave,uses=redis-master Running
9fbdbeca-7fcb-11e4-a220-42010af0a5f1 kubernetes/example-guestbook-php-redis kubernetes-minion-4.c.myproject.internal/130.211.168.210 name=frontend,uses=redisslave,redis-master Running redis-slave-controller-zwk1b 10.244.2.8 redis-slave brendanburns/redis-slave kubernetes-minion-3vxa.c.hazel-mote-834.internal/104.154.54.6 app=redis,name=redis-slave,uses=redis-master Running
``` ```
You will see a single redis master pod, two redis slaves, and three frontend pods. You will see a single redis master pod, two redis slaves, and three frontend pods.
@ -341,14 +354,51 @@ if (isset($_GET['cmd']) === true) {
} ?> } ?>
``` ```
To play with the service itself, find the name of a frontend, ### Step Six: Create the guestbook service.
Just like the others, you want a service to group your frontend pods.
The service is described in the file `examples/guestbook/frontend-service.json`:
```js
{
"id": "frontend",
"kind": "Service",
"apiVersion": "v1beta1",
"port": 8000,
"containerPort": "http-server",
"selector": {
"name": "frontend"
},
"labels": {
"name": "frontend"
},
"createExternalLoadBalancer": true
}
```
```shell
$ kubectl create -f examples/guestbook/frontend-service.json
frontend
$ kubectl get services
NAME LABELS SELECTOR IP PORT
frontend name=frontend name=frontend 10.0.93.211 8000
redis-master name=redis-master name=redis-master 10.0.246.242 6379
redisslave name=redis-slave name=redis-slave 10.0.72.62 6379
```
### A few Google Container Engine specifics for playing around with the services. ### A few Google Container Engine specifics for playing around with the services.
- In GCE, you can grab the external IP of that host from the [Google Cloud Console][cloud-console] or the `gcloud` tool, and visit `http://<host-ip>:8000`. In GCE, `cluster/kubectl.sh` automatically creates forwarding rule for services with `createExternalLoadBalancer`.
```shell ```shell
$ gcloud compute instances list $ gcloud compute forwarding-rules list
NAME REGION IP_ADDRESS IP_PROTOCOL TARGET
frontend us-central1 130.211.188.51 TCP us-central1/targetPools/frontend
``` ```
You can grab the external IP of the load balancer associated with that rule and visit `http://130.211.188.51:8000`.
In GCE, you also may need to open the firewall for port 8000 using the [console][cloud-console] or the `gcloud` tool. The following command will allow traffic from any source to instances tagged `kubernetes-minion`: In GCE, you also may need to open the firewall for port 8000 using the [console][cloud-console] or the `gcloud` tool. The following command will allow traffic from any source to instances tagged `kubernetes-minion`:
```shell ```shell
@ -364,7 +414,7 @@ In other environments, you can get the service IP from looking at the output of
And of course, finally, if you are running Kubernetes locally, you can just visit http://localhost:8000. And of course, finally, if you are running Kubernetes locally, you can just visit http://localhost:8000.
### Step Six: Cleanup ### Step Seven: Cleanup
To turn down a Kubernetes cluster, if you ran this from source, you can use To turn down a Kubernetes cluster, if you ran this from source, you can use

View File

@ -9,19 +9,20 @@
"desiredState": { "desiredState": {
"manifest": { "manifest": {
"version": "v1beta1", "version": "v1beta1",
"id": "frontend-controller", "id": "frontend",
"containers": [{ "containers": [{
"name": "php-redis", "name": "php-redis",
"image": "kubernetes/example-guestbook-php-redis", "image": "kubernetes/example-guestbook-php-redis",
"cpu": 100, "cpu": 100,
"memory": 50000000, "memory": 50000000,
"ports": [{"containerPort": 80, "hostPort": 8000}] "ports": [{"name": "http-server", "containerPort": 80}]
}] }]
} }
}, },
"labels": { "labels": {
"name": "frontend", "name": "frontend",
"uses": "redisslave,redis-master" "uses": "redis-slave,redis-master",
"app": "frontend"
} }
}}, }},
"labels": {"name": "frontend"} "labels": {"name": "frontend"}

View File

@ -2,12 +2,13 @@
"id": "frontend", "id": "frontend",
"kind": "Service", "kind": "Service",
"apiVersion": "v1beta1", "apiVersion": "v1beta1",
"port": 80, "port": 8000,
"containerPort": 80, "containerPort": "http-server",
"selector": { "selector": {
"name": "frontend" "name": "frontend"
}, },
"labels": { "labels": {
"name": "frontend" "name": "frontend"
} },
"createExternalLoadBalancer": true
} }

View File

@ -0,0 +1,31 @@
{
"id": "redis-master-controller",
"kind": "ReplicationController",
"apiVersion": "v1beta1",
"desiredState": {
"replicas": 1,
"replicaSelector": {"name": "redis-master"},
"podTemplate": {
"desiredState": {
"manifest": {
"version": "v1beta1",
"id": "redis-master",
"containers": [{
"name": "redis-master",
"image": "dockerfile/redis",
"cpu": 100,
"ports": [{"containerPort": 6379}]
}]
}
},
"labels": {
"name": "redis-master",
"app": "redis"
}
}
},
"labels": {
"name": "redis-master"
}
}

View File

@ -1,24 +0,0 @@
{
"id": "redis-master",
"kind": "Pod",
"apiVersion": "v1beta1",
"desiredState": {
"manifest": {
"version": "v1beta1",
"id": "redis-master",
"containers": [{
"name": "master",
"image": "dockerfile/redis",
"cpu": 100,
"ports": [{
"containerPort": 6379,
"hostPort": 6379
}]
}]
}
},
"labels": {
"name": "redis-master"
}
}

View File

@ -4,25 +4,26 @@
"apiVersion": "v1beta1", "apiVersion": "v1beta1",
"desiredState": { "desiredState": {
"replicas": 2, "replicas": 2,
"replicaSelector": {"name": "redisslave"}, "replicaSelector": {"name": "redis-slave"},
"podTemplate": { "podTemplate": {
"desiredState": { "desiredState": {
"manifest": { "manifest": {
"version": "v1beta1", "version": "v1beta1",
"id": "redis-slave-controller", "id": "redis-slave",
"containers": [{ "containers": [{
"name": "slave", "name": "redis-slave",
"image": "brendanburns/redis-slave", "image": "brendanburns/redis-slave",
"cpu": 200, "cpu": 200,
"ports": [{"containerPort": 6379, "hostPort": 6380}] "ports": [{"containerPort": 6379}]
}] }]
} }
}, },
"labels": { "labels": {
"name": "redisslave", "name": "redis-slave",
"uses": "redis-master" "uses": "redis-master",
"app": "redis"
} }
} }
}, },
"labels": {"name": "redisslave"} "labels": {"name": "redis-slave"}
} }

View File

@ -5,9 +5,9 @@
"port": 6379, "port": 6379,
"containerPort": 6379, "containerPort": 6379,
"labels": { "labels": {
"name": "redisslave" "name": "redis-slave"
}, },
"selector": { "selector": {
"name": "redisslave" "name": "redis-slave"
} }
} }

View File

@ -8,7 +8,7 @@
"desiredState": { "desiredState": {
"manifest": { "manifest": {
"version": "v1beta1", "version": "v1beta1",
"id": "invalid-pod", "id": "valid-pod",
"containers": [{ "containers": [{
"name": "kubernetes-serve-hostname", "name": "kubernetes-serve-hostname",
"image": "kubernetes/serve_hostname", "image": "kubernetes/serve_hostname",
@ -17,4 +17,4 @@
}] }]
} }
}, },
} }

View File

@ -132,67 +132,68 @@ for version in "${kube_api_versions[@]}"; do
kube::log::status "Testing kubectl(${version}:pods)" kube::log::status "Testing kubectl(${version}:pods)"
kubectl get pods "${kube_flags[@]}" kubectl get pods "${kube_flags[@]}"
kubectl create -f examples/guestbook/redis-master.json "${kube_flags[@]}" kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
kubectl get pods "${kube_flags[@]}" kubectl get pods "${kube_flags[@]}"
kubectl get pod redis-master "${kube_flags[@]}" kubectl get pod valid-pod "${kube_flags[@]}"
[ "$(kubectl get pod redis-master -o template --output-version=v1beta1 -t '{{ .id }}' "${kube_flags[@]}")" == "redis-master" ] [ "$(kubectl get pod valid-pod -o template --output-version=v1beta1 -t '{{ .id }}' "${kube_flags[@]}")" == "valid-pod" ]
output_pod=$(kubectl get pod redis-master -o yaml --output-version=v1beta1 "${kube_flags[@]}") output_pod=$(kubectl get pod valid-pod -o yaml --output-version=v1beta1 "${kube_flags[@]}")
kubectl delete pod redis-master "${kube_flags[@]}" kubectl delete pod valid-pod "${kube_flags[@]}"
before="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")" before="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
echo "${output_pod}" | kubectl create -f - "${kube_flags[@]}" echo "${output_pod}" | kubectl create -f - "${kube_flags[@]}"
after="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")" after="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$((${after} - ${before}))" -eq 1 ] [ "$((${after} - ${before}))" -eq 1 ]
kubectl get pods -o yaml --output-version=v1beta1 "${kube_flags[@]}" | grep -q "id: redis-master" kubectl get pods -o yaml --output-version=v1beta1 "${kube_flags[@]}" | grep -q "id: valid-pod"
kubectl describe pod redis-master "${kube_flags[@]}" | grep -q 'Name:.*redis-master' kubectl describe pod valid-pod "${kube_flags[@]}" | grep -q 'Name:.*valid-pod'
kubectl delete -f examples/guestbook/redis-master.json "${kube_flags[@]}" kubectl delete -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
kubectl delete pods -l name=redis-master "${kube_flags[@]}"
[ ! $(kubectl get pods "${kube_flags[@]}" -lname=redis-master | grep -q 'redis-master') ] kubectl delete pods -l name=valid-pod "${kube_flags[@]}"
kubectl create -f examples/guestbook/redis-master.json "${kube_flags[@]}" [ ! $(kubectl get pods "${kube_flags[@]}" -lname=valid-pod | grep -q 'valid-pod') ]
kubectl get pods "${kube_flags[@]}" -lname=redis-master | grep -q 'redis-master' kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
kubectl get pods "${kube_flags[@]}" -lname=valid-pod | grep -q 'valid-pod'
[ ! $(kubectl delete pods "${kube_flags[@]}" ) ] [ ! $(kubectl delete pods "${kube_flags[@]}" ) ]
kubectl get pods "${kube_flags[@]}" -lname=redis-master | grep -q 'redis-master' kubectl get pods "${kube_flags[@]}" -lname=valid-pod | grep -q 'valid-pod'
[ ! $(kubectl delete pods --all pods -l name=redis-master "${kube_flags[@]}" ) ] # not --all and label selector together [ ! $(kubectl delete pods --all pods -l name=valid-pod "${kube_flags[@]}" ) ] # not --all and label selector together
kubectl delete --all pods "${kube_flags[@]}" # --all remove all the pods kubectl delete --all pods "${kube_flags[@]}" # --all remove all the pods
kubectl create -f examples/guestbook/redis-master.json "${kube_flags[@]}" kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
kubectl create -f examples/redis/redis-proxy.yaml "${kube_flags[@]}" kubectl create -f examples/redis/redis-proxy.yaml "${kube_flags[@]}"
kubectl get pods redis-master redis-proxy "${kube_flags[@]}" kubectl get pods valid-pod redis-proxy "${kube_flags[@]}"
kubectl delete pods redis-master redis-proxy # delete multiple pods at once kubectl delete pods valid-pod redis-proxy "${kube_flags[@]}" # delete multiple pods at once
howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")" howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$howmanypods" -eq 0 ] [ "$howmanypods" -eq 0 ]
kubectl create -f examples/guestbook/redis-master.json "${kube_flags[@]}" kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
kubectl create -f examples/redis/redis-proxy.yaml "${kube_flags[@]}" kubectl create -f examples/redis/redis-proxy.yaml "${kube_flags[@]}"
kubectl stop pods redis-master redis-proxy # stop multiple pods at once kubectl stop pods valid-pod redis-proxy "${kube_flags[@]}" # stop multiple pods at once
howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")" howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$howmanypods" -eq 0 ] [ "$howmanypods" -eq 0 ]
kubectl create -f examples/guestbook/redis-master.json "${kube_flags[@]}" kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")" howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$howmanypods" -eq 1 ] [ "$howmanypods" -eq 1 ]
#testing pods and label command command #testing pods and label command command
kubectl label pods redis-master new-name=new-redis-master "${kube_flags[@]}" kubectl label pods valid-pod new-name=new-valid-pod "${kube_flags[@]}"
kubectl delete pods -lnew-name=new-redis-master "${kube_flags[@]}" kubectl delete pods -lnew-name=new-valid-pod "${kube_flags[@]}"
howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")" howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$howmanypods" -eq 0 ] [ "$howmanypods" -eq 0 ]
kubectl create -f examples/guestbook/redis-master.json "${kube_flags[@]}" kubectl create -f examples/limitrange/valid-pod.json "${kube_flags[@]}"
howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")" howmanypods="$(kubectl get pods -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$howmanypods" -eq 1 ] [ "$howmanypods" -eq 1 ]
! $(kubectl label pods redis-master name=redis-super-sayan "${kube_flags[@]}" ) ! $(kubectl label pods valid-pod name=valid-pod-super-sayan "${kube_flags[@]}" )
kubectl label --overwrite pods redis-master name=redis-super-sayan "${kube_flags[@]}" kubectl label --overwrite pods valid-pod name=valid-pod-super-sayan "${kube_flags[@]}"
kubectl delete pods -lname=redis-super-sayan "${kube_flags[@]}" kubectl delete pods -lname=valid-pod-super-sayan "${kube_flags[@]}"
howmanypods="$(kubectl get pods -lname=redis-super-sayan -o template -t "{{ len .items }}" "${kube_flags[@]}")" howmanypods="$(kubectl get pods -lname=valid-pod-super-sayan -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$howmanypods" -eq 0 ] [ "$howmanypods" -eq 0 ]
# make calls in another namespace # make calls in another namespace
kubectl create --namespace=other -f examples/guestbook/redis-master.json "${kube_flags[@]}" kubectl create "${kube_flags[@]}" --namespace=other -f examples/limitrange/valid-pod.json
kubectl get pod --namespace=other redis-master kubectl get "${kube_flags[@]}" pod --namespace=other valid-pod
kubectl delete pod --namespace=other redis-master kubectl delete "${kube_flags[@]}" pod --namespace=other valid-pod
kube::log::status "Testing kubectl(${version}:services)" kube::log::status "Testing kubectl(${version}:services)"
kubectl get services "${kube_flags[@]}" kubectl get services "${kube_flags[@]}"
kubectl create -f examples/guestbook/frontend-service.json "${kube_flags[@]}" kubectl create -f examples/guestbook/redis-master-service.json "${kube_flags[@]}"
kubectl get services "${kube_flags[@]}" kubectl get services "${kube_flags[@]}"
output_service=$(kubectl get service frontend -o json --output-version=v1beta3 "${kube_flags[@]}") output_service=$(kubectl get service redis-master -o json --output-version=v1beta3 "${kube_flags[@]}")
kubectl delete service frontend "${kube_flags[@]}" kubectl delete service redis-master "${kube_flags[@]}"
echo "${output_service}" | kubectl create -f - "${kube_flags[@]}" echo "${output_service}" | kubectl create -f - "${kube_flags[@]}"
kubectl create -f - "${kube_flags[@]}" << __EOF__ kubectl create -f - "${kube_flags[@]}" << __EOF__
{ {
@ -206,15 +207,15 @@ for version in "${kube_api_versions[@]}"; do
} }
} }
__EOF__ __EOF__
kubectl update service service-${version}-test --patch="{\"selector\":{\"version\":\"${version}\"},\"apiVersion\":\"${version}\"}" kubectl update service "${kube_flags[@]}" service-${version}-test --patch="{\"selector\":{\"version\":\"${version}\"},\"apiVersion\":\"${version}\"}"
kubectl get service service-${version}-test -o json | kubectl update -f - kubectl get service "${kube_flags[@]}" service-${version}-test -o json | kubectl update "${kube_flags[@]}" -f -
kubectl get services "${kube_flags[@]}" kubectl get services "${kube_flags[@]}"
kubectl get services "service-${version}-test" "${kube_flags[@]}" kubectl get services "service-${version}-test" "${kube_flags[@]}"
kubectl delete service frontend "${kube_flags[@]}" kubectl delete service redis-master "${kube_flags[@]}"
servicesbefore="$(kubectl get services -o template -t "{{ len .items }}" "${kube_flags[@]}")" servicesbefore="$(kubectl get services -o template -t "{{ len .items }}" "${kube_flags[@]}")"
kubectl create -f examples/guestbook/frontend-service.json "${kube_flags[@]}" kubectl create -f examples/guestbook/redis-master-service.json "${kube_flags[@]}"
kubectl create -f examples/guestbook/redis-slave-service.json "${kube_flags[@]}" kubectl create -f examples/guestbook/redis-slave-service.json "${kube_flags[@]}"
kubectl delete services frontend redisslave # delete multiple services at once kubectl delete services redis-master redisslave "${kube_flags[@]}" # delete multiple services at once
servicesafter="$(kubectl get services -o template -t "{{ len .items }}" "${kube_flags[@]}")" servicesafter="$(kubectl get services -o template -t "{{ len .items }}" "${kube_flags[@]}")"
[ "$((${servicesafter} - ${servicesbefore}))" -eq 0 ] [ "$((${servicesafter} - ${servicesbefore}))" -eq 0 ]

View File

@ -25,8 +25,8 @@ import (
) )
func TestCreateObject(t *testing.T) { func TestCreateObject(t *testing.T) {
pods, _ := testData() _, _, rc := testData()
pods.Items[0].Name = "redis-master" rc.Items[0].Name = "redis-master-controller"
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -34,8 +34,8 @@ func TestCreateObject(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods" && m == "POST": case p == "/namespaces/test/replicationcontrollers" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &pods.Items[0])}, nil return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil
default: default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil return nil, nil
@ -46,17 +46,17 @@ func TestCreateObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdCreate(buf) cmd := f.NewCmdCreate(buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
// uses the name from the file, not the response // uses the name from the file, not the response
if buf.String() != "redis-master\n" { if buf.String() != "redis-master-controller\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }
func TestCreateMultipleObject(t *testing.T) { func TestCreateMultipleObject(t *testing.T) {
pods, svc := testData() _, svc, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -64,10 +64,10 @@ func TestCreateMultipleObject(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &pods.Items[0])}, nil
case p == "/namespaces/test/services" && m == "POST": case p == "/namespaces/test/services" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil
case p == "/namespaces/test/replicationcontrollers" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil
default: default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil return nil, nil
@ -78,19 +78,19 @@ func TestCreateMultipleObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdCreate(buf) cmd := f.NewCmdCreate(buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
// Names should come from the REST response, NOT the files // Names should come from the REST response, NOT the files
if buf.String() != "foo\nbaz\n" { if buf.String() != "rc1\nbaz\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }
func TestCreateDirectory(t *testing.T) { func TestCreateDirectory(t *testing.T) {
pods, svc := testData() _, svc, rc := testData()
pods.Items[0].Name = "redis-master" rc.Items[0].Name = "name"
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -98,12 +98,10 @@ func TestCreateDirectory(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &pods.Items[0])}, nil
case p == "/namespaces/test/services" && m == "POST": case p == "/namespaces/test/services" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil
case p == "/namespaces/test/replicationcontrollers" && m == "POST": case p == "/namespaces/test/replicationcontrollers" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil
default: default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil return nil, nil
@ -117,7 +115,7 @@ func TestCreateDirectory(t *testing.T) {
cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Flags().Set("filename", "../../../examples/guestbook")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
if buf.String() != "baz\nbaz\nbaz\nredis-master\nbaz\nbaz\n" { if buf.String() != "name\nbaz\nname\nbaz\nname\nbaz\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }

View File

@ -26,7 +26,7 @@ import (
) )
func TestDeleteObject(t *testing.T) { func TestDeleteObject(t *testing.T) {
pods, _ := testData() _, _, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -34,8 +34,8 @@ func TestDeleteObject(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods/redis-master" && m == "DELETE": case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
default: default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil return nil, nil
@ -46,11 +46,11 @@ func TestDeleteObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := f.NewCmdDelete(buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
// uses the name from the file, not the response // uses the name from the file, not the response
if buf.String() != "redis-master\n" { if buf.String() != "redis-master-controller\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }
@ -62,7 +62,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods/redis-master" && m == "DELETE": case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE":
return &http.Response{StatusCode: 404, Body: stringBody("")}, nil return &http.Response{StatusCode: 404, Body: stringBody("")}, nil
default: default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
@ -74,7 +74,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := f.NewCmdDelete(buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
if buf.String() != "" { if buf.String() != "" {
@ -83,7 +83,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
} }
func TestDeleteMultipleObject(t *testing.T) { func TestDeleteMultipleObject(t *testing.T) {
pods, svc := testData() _, svc, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -91,8 +91,8 @@ func TestDeleteMultipleObject(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods/redis-master" && m == "DELETE": case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
case p == "/namespaces/test/services/frontend" && m == "DELETE": case p == "/namespaces/test/services/frontend" && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil
default: default:
@ -105,17 +105,17 @@ func TestDeleteMultipleObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := f.NewCmdDelete(buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
if buf.String() != "redis-master\nfrontend\n" { if buf.String() != "redis-master-controller\nfrontend\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }
func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) { func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) {
_, svc := testData() _, svc, _ := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -123,7 +123,7 @@ func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods/redis-master" && m == "DELETE": case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE":
return &http.Response{StatusCode: 404, Body: stringBody("")}, nil return &http.Response{StatusCode: 404, Body: stringBody("")}, nil
case p == "/namespaces/test/services/frontend" && m == "DELETE": case p == "/namespaces/test/services/frontend" && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil
@ -137,7 +137,7 @@ func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := f.NewCmdDelete(buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -147,7 +147,7 @@ func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) {
} }
func TestDeleteDirectory(t *testing.T) { func TestDeleteDirectory(t *testing.T) {
pods, svc := testData() _, svc, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -155,12 +155,10 @@ func TestDeleteDirectory(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case strings.HasPrefix(p, "/namespaces/test/pods/") && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil
case strings.HasPrefix(p, "/namespaces/test/services/") && m == "DELETE": case strings.HasPrefix(p, "/namespaces/test/services/") && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil
case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && m == "DELETE": case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && m == "DELETE":
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
default: default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil return nil, nil
@ -174,13 +172,13 @@ func TestDeleteDirectory(t *testing.T) {
cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Flags().Set("filename", "../../../examples/guestbook")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
if buf.String() != "frontend-controller\nfrontend\nredis-master\nredis-master\nredis-slave-controller\nredisslave\n" { if buf.String() != "frontend-controller\nfrontend\nredis-master-controller\nredis-master\nredis-slave-controller\nredisslave\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }
func TestDeleteMultipleSelector(t *testing.T) { func TestDeleteMultipleSelector(t *testing.T) {
pods, svc := testData() pods, svc, _ := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}

View File

@ -34,7 +34,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch/json" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch/json"
) )
func testData() (*api.PodList, *api.ServiceList) { func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) {
pods := &api.PodList{ pods := &api.PodList{
ListMeta: api.ListMeta{ ListMeta: api.ListMeta{
ResourceVersion: "15", ResourceVersion: "15",
@ -70,7 +70,20 @@ func testData() (*api.PodList, *api.ServiceList) {
}, },
}, },
} }
return pods, svc rc := &api.ReplicationControllerList{
ListMeta: api.ListMeta{
ResourceVersion: "17",
},
Items: []api.ReplicationController{
{
ObjectMeta: api.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
},
},
},
}
return pods, svc, rc
} }
// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. // Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get.
@ -123,7 +136,7 @@ func TestGetSchemaObject(t *testing.T) {
} }
func TestGetObjects(t *testing.T) { func TestGetObjects(t *testing.T) {
pods, _ := testData() pods, _, _ := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -149,7 +162,7 @@ func TestGetObjects(t *testing.T) {
} }
func TestGetListObjects(t *testing.T) { func TestGetListObjects(t *testing.T) {
pods, _ := testData() pods, _, _ := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -175,7 +188,7 @@ func TestGetListObjects(t *testing.T) {
} }
func TestGetMultipleTypeObjects(t *testing.T) { func TestGetMultipleTypeObjects(t *testing.T) {
pods, svc := testData() pods, svc, _ := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -211,7 +224,7 @@ func TestGetMultipleTypeObjects(t *testing.T) {
} }
func TestGetMultipleTypeObjectsAsList(t *testing.T) { func TestGetMultipleTypeObjectsAsList(t *testing.T) {
pods, svc := testData() pods, svc, _ := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -260,7 +273,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
} }
func TestGetMultipleTypeObjectsWithSelector(t *testing.T) { func TestGetMultipleTypeObjectsWithSelector(t *testing.T) {
pods, svc := testData() pods, svc, _ := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}

View File

@ -22,26 +22,11 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
) )
func rcTestData() *api.ReplicationControllerList {
rc := &api.ReplicationControllerList{
ListMeta: api.ListMeta{
ResourceVersion: "17",
},
Items: []api.ReplicationController{
{
ObjectMeta: api.ObjectMeta{Name: "qux", Namespace: "test", ResourceVersion: "13"},
},
},
}
return rc
}
func TestUpdateObject(t *testing.T) { func TestUpdateObject(t *testing.T) {
pods, _ := testData() _, _, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -49,10 +34,10 @@ func TestUpdateObject(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods/redis-master" && m == "GET": case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "GET":
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
case p == "/namespaces/test/pods/redis-master" && m == "PUT": case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "PUT":
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
default: default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil return nil, nil
@ -63,17 +48,17 @@ func TestUpdateObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdUpdate(buf) cmd := f.NewCmdUpdate(buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
// uses the name from the file, not the response // uses the name from the file, not the response
if buf.String() != "foo\n" { if buf.String() != "rc1\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }
func TestUpdateMultipleObject(t *testing.T) { func TestUpdateMultipleObject(t *testing.T) {
pods, svc := testData() _, svc, rc := testData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -81,11 +66,10 @@ func TestUpdateMultipleObject(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/pods/redis-master" && m == "GET": case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "GET":
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
case p == "/namespaces/test/pods/redis-master" && m == "PUT": case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "PUT":
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil
case p == "/namespaces/test/services/frontend" && m == "GET": case p == "/namespaces/test/services/frontend" && m == "GET":
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil
case p == "/namespaces/test/services/frontend" && m == "PUT": case p == "/namespaces/test/services/frontend" && m == "PUT":
@ -100,18 +84,17 @@ func TestUpdateMultipleObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdUpdate(buf) cmd := f.NewCmdUpdate(buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
if buf.String() != "foo\nbaz\n" { if buf.String() != "rc1\nbaz\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }
func TestUpdateDirectory(t *testing.T) { func TestUpdateDirectory(t *testing.T) {
pods, svc := testData() _, svc, rc := testData()
rc := rcTestData()
f, tf, codec := NewAPIFactory() f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
@ -119,8 +102,6 @@ func TestUpdateDirectory(t *testing.T) {
Codec: codec, Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { switch p, m := req.URL.Path, req.Method; {
case strings.HasPrefix(p, "/namespaces/test/pods/") && (m == "GET" || m == "PUT"):
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil
case strings.HasPrefix(p, "/namespaces/test/services/") && (m == "GET" || m == "PUT"): case strings.HasPrefix(p, "/namespaces/test/services/") && (m == "GET" || m == "PUT"):
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil
case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && (m == "GET" || m == "PUT"): case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && (m == "GET" || m == "PUT"):
@ -139,7 +120,7 @@ func TestUpdateDirectory(t *testing.T) {
cmd.Flags().Set("namespace", "test") cmd.Flags().Set("namespace", "test")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
if buf.String() != "qux\nbaz\nbaz\nfoo\nqux\nbaz\n" { if buf.String() != "rc1\nbaz\nrc1\nbaz\nrc1\nbaz\n" {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
} }

View File

@ -166,7 +166,7 @@ func (v *testVisitor) Objects() []runtime.Object {
func TestPathBuilder(t *testing.T) { func TestPathBuilder(t *testing.T) {
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()). b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam("../../../examples/guestbook/redis-master.json") FilenameParam("../../../examples/guestbook/redis-master-controller.json")
test := &testVisitor{} test := &testVisitor{}
singular := false singular := false
@ -177,7 +177,7 @@ func TestPathBuilder(t *testing.T) {
} }
info := test.Infos[0] info := test.Infos[0]
if info.Name != "redis-master" || info.Namespace != "" || info.Object == nil { if info.Name != "redis-master-controller" || info.Namespace != "" || info.Object == nil {
t.Errorf("unexpected info: %#v", info) t.Errorf("unexpected info: %#v", info)
} }
} }
@ -215,8 +215,8 @@ func TestNodeBuilder(t *testing.T) {
func TestPathBuilderWithMultiple(t *testing.T) { func TestPathBuilderWithMultiple(t *testing.T) {
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()). b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam("../../../examples/guestbook/redis-master.json"). FilenameParam("../../../examples/guestbook/redis-master-controller.json").
FilenameParam("../../../examples/guestbook/redis-master.json"). FilenameParam("../../../examples/guestbook/redis-master-controller.json").
NamespaceParam("test").DefaultNamespace() NamespaceParam("test").DefaultNamespace()
test := &testVisitor{} test := &testVisitor{}
@ -228,7 +228,7 @@ func TestPathBuilderWithMultiple(t *testing.T) {
} }
info := test.Infos[1] info := test.Infos[1]
if info.Name != "redis-master" || info.Namespace != "test" || info.Object == nil { if info.Name != "redis-master-controller" || info.Namespace != "test" || info.Object == nil {
t.Errorf("unexpected info: %#v", info) t.Errorf("unexpected info: %#v", info)
} }
} }
@ -248,7 +248,7 @@ func TestDirectoryBuilder(t *testing.T) {
found := false found := false
for _, info := range test.Infos { for _, info := range test.Infos {
if info.Name == "redis-master" && info.Namespace == "test" && info.Object != nil { if info.Name == "redis-master-controller" && info.Namespace == "test" && info.Object != nil {
found = true found = true
} }
} }
@ -474,7 +474,7 @@ func TestMultipleObject(t *testing.T) {
func TestSingularObject(t *testing.T) { func TestSingularObject(t *testing.T) {
obj, err := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()). obj, err := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
FilenameParam("../../../examples/guestbook/redis-master.json"). FilenameParam("../../../examples/guestbook/redis-master-controller.json").
Flatten(). Flatten().
Do().Object() Do().Object()
@ -482,12 +482,12 @@ func TestSingularObject(t *testing.T) {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
pod, ok := obj.(*api.Pod) rc, ok := obj.(*api.ReplicationController)
if !ok { if !ok {
t.Fatalf("unexpected object: %#v", obj) t.Fatalf("unexpected object: %#v", obj)
} }
if pod.Name != "redis-master" || pod.Namespace != "test" { if rc.Name != "redis-master-controller" || rc.Namespace != "test" {
t.Errorf("unexpected pod: %#v", pod) t.Errorf("unexpected controller: %#v", rc)
} }
} }
@ -550,16 +550,16 @@ func TestListObjectWithDifferentVersions(t *testing.T) {
} }
func TestWatch(t *testing.T) { func TestWatch(t *testing.T) {
pods, _ := testData() _, svc := testData()
w, err := NewBuilder(latest.RESTMapper, api.Scheme, fakeClientWith(t, map[string]string{ w, err := NewBuilder(latest.RESTMapper, api.Scheme, fakeClientWith(t, map[string]string{
"/watch/namespaces/test/pods/redis-master?resourceVersion=10": watchBody(watch.Event{ "/watch/namespaces/test/services/redis-master?resourceVersion=12": watchBody(watch.Event{
Type: watch.Added, Type: watch.Added,
Object: &pods.Items[0], Object: &svc.Items[0],
}), }),
})). })).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
FilenameParam("../../../examples/guestbook/redis-master.json").Flatten(). FilenameParam("../../../examples/guestbook/redis-master-service.json").Flatten().
Do().Watch("10") Do().Watch("12")
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@ -572,12 +572,12 @@ func TestWatch(t *testing.T) {
if obj.Type != watch.Added { if obj.Type != watch.Added {
t.Fatalf("unexpected watch event", obj) t.Fatalf("unexpected watch event", obj)
} }
pod, ok := obj.Object.(*api.Pod) service, ok := obj.Object.(*api.Service)
if !ok { if !ok {
t.Fatalf("unexpected object: %#v", obj) t.Fatalf("unexpected object: %#v", obj)
} }
if pod.Name != "foo" || pod.ResourceVersion != "10" { if service.Name != "baz" || service.ResourceVersion != "12" {
t.Errorf("unexpected pod: %#v", pod) t.Errorf("unexpected service: %#v", service)
} }
} }
} }
@ -585,8 +585,8 @@ func TestWatch(t *testing.T) {
func TestWatchMultipleError(t *testing.T) { func TestWatchMultipleError(t *testing.T) {
_, err := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()). _, err := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
FilenameParam("../../../examples/guestbook/redis-master.json").Flatten(). FilenameParam("../../../examples/guestbook/redis-master-controller.json").Flatten().
FilenameParam("../../../examples/guestbook/redis-master.json").Flatten(). FilenameParam("../../../examples/guestbook/redis-master-controller.json").Flatten().
Do().Watch("") Do().Watch("")
if err == nil { if err == nil {