mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-17 02:06:23 +00:00
@@ -551,6 +551,14 @@ function kube::release::package_salt_tarball() {
|
||||
|
||||
cp -R "${KUBE_ROOT}/cluster/saltbase" "${release_stage}/"
|
||||
|
||||
# TODO(#3579): This is a temporary hack. It gathers up the yaml,
|
||||
# yaml.in files in cluster/addons (minus any demos) and overlays
|
||||
# them into kube-addons, where we expect them. (This pipeline is a
|
||||
# fancy copy, stripping anything but the files we don't want.)
|
||||
local objects
|
||||
objects=$(cd "${KUBE_ROOT}/cluster/addons" && find . -name \*.yaml -or -name \*.yaml.in | grep -v demo)
|
||||
tar c -C "${KUBE_ROOT}/cluster/addons" ${objects} | tar x -C "${release_stage}/saltbase/salt/kube-addons"
|
||||
|
||||
local package_name="${RELEASE_DIR}/kubernetes-salt.tar.gz"
|
||||
kube::release::create_tarball "${package_name}" "${release_stage}/.."
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# DNS in Kubernetes
|
||||
This directory holds an example of how to run
|
||||
[SkyDNS](https://github.com/skynetservices/skydns) in a Kubernetes cluster.
|
||||
[SkyDNS](https://github.com/skynetservices/skydns) can be configured
|
||||
to automatically run in a Kubernetes cluster.
|
||||
|
||||
## What things get DNS names?
|
||||
The only objects to which we are assigning DNS names are Services. Every
|
||||
@@ -18,23 +18,14 @@ Of course, giving services a name is just half of the problem - DNS names need a
|
||||
domain also. This implementation uses the variable `DNS_DOMAIN` (see below).
|
||||
You can configure your docker daemon with the flag `--dns-search`.
|
||||
|
||||
## How do I run it?
|
||||
The first thing you have to do is substitute the variables into the
|
||||
configuration. You can then feed the result into `kubectl`.
|
||||
## How do I configure it?
|
||||
The following environment variables are used at cluster startup to create the SkyDNS pods and configure the kubelets. If you need to, you can reconfigure your provider as necessary (e.g. `cluster/gce/config-default.sh`):
|
||||
|
||||
```shell
|
||||
DNS_SERVER_IP=10.0.0.10
|
||||
DNS_DOMAIN=kubernetes.local
|
||||
DNS_REPLICAS=2
|
||||
|
||||
sed -e "s/{DNS_DOMAIN}/$DNS_DOMAIN/g" \
|
||||
-e "s/{DNS_REPLICAS}/$DNS_REPLICAS/g" \
|
||||
./cluster/addons/dns/skydns-rc.yaml.in \
|
||||
| ./cluster/kubectl.sh create -f -
|
||||
|
||||
sed -e "s/{DNS_SERVER_IP}/$DNS_SERVER_IP/g" \
|
||||
./cluster/addons/dns/skydns-svc.yaml.in \
|
||||
| ./cluster/kubectl.sh create -f -
|
||||
ENABLE_CLUSTER_DNS=true
|
||||
DNS_SERVER_IP="10.0.0.10"
|
||||
DNS_DOMAIN="kubernetes.local"
|
||||
DNS_REPLICAS=1
|
||||
```
|
||||
|
||||
## How does it work?
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace: default
|
||||
labels:
|
||||
k8s-app: skydns
|
||||
desiredState:
|
||||
replicas: {DNS_REPLICAS}
|
||||
replicas: {{ pillar['dns_replicas'] }}
|
||||
replicaSelector:
|
||||
k8s-app: skydns
|
||||
podTemplate:
|
||||
@@ -28,7 +28,7 @@ desiredState:
|
||||
image: kubernetes/kube2sky:1.0
|
||||
command: [
|
||||
# entrypoint = "/kube2sky",
|
||||
"-domain={DNS_DOMAIN}",
|
||||
"-domain={{ pillar['dns_domain'] }}",
|
||||
]
|
||||
- name: skydns
|
||||
image: kubernetes/skydns:2014-12-23-001
|
||||
@@ -36,7 +36,7 @@ desiredState:
|
||||
# entrypoint = "/skydns",
|
||||
"-machines=http://localhost:4001",
|
||||
"-addr=0.0.0.0:53",
|
||||
"-domain={DNS_DOMAIN}.",
|
||||
"-domain={{ pillar['dns_domain'] }}.",
|
||||
]
|
||||
ports:
|
||||
- name: dns
|
||||
|
||||
@@ -4,7 +4,7 @@ id: skydns
|
||||
namespace: default
|
||||
protocol: UDP
|
||||
port: 53
|
||||
portalIP: {DNS_SERVER_IP}
|
||||
portalIP: {{ pillar['dns_server'] }}
|
||||
containerPort: 53
|
||||
labels:
|
||||
k8s-app: skydns
|
||||
|
||||
@@ -2,7 +2,7 @@ apiVersion: v1beta1
|
||||
kind: ReplicationController
|
||||
id: elasticsearch-logging-controller
|
||||
desiredState:
|
||||
replicas: {ELASTICSEARCH_LOGGING_REPLICAS}
|
||||
replicas: {{ pillar['elasticsearch_replicas'] }}
|
||||
replicaSelector:
|
||||
name: elasticsearch-logging
|
||||
podTemplate:
|
||||
|
||||
@@ -106,14 +106,14 @@ function ensure-temp-dir {
|
||||
fi
|
||||
}
|
||||
|
||||
function setup-monitoring {
|
||||
function setup-monitoring-firewall {
|
||||
if [[ "${ENABLE_CLUSTER_MONITORING:-false}" == "true" ]]; then
|
||||
# TODO: Implement this.
|
||||
echo "Monitoring not currently supported on AWS"
|
||||
fi
|
||||
}
|
||||
|
||||
function teardown-monitoring {
|
||||
function teardown-monitoring-firewall {
|
||||
if [[ "${ENABLE_CLUSTER_MONITORING:-false}" == "true" ]]; then
|
||||
# TODO: Implement this.
|
||||
echo "Monitoring not currently supported on AWS"
|
||||
@@ -296,10 +296,14 @@ function kube-up {
|
||||
echo "readonly AWS_ZONE='${ZONE}'"
|
||||
echo "readonly MASTER_HTPASSWD='${htpasswd}'"
|
||||
echo "readonly PORTAL_NET='${PORTAL_NET}'"
|
||||
echo "readonly ENABLE_CLUSTER_MONITORING='${ENABLE_CLUSTER_MONITORING:-false}'"
|
||||
echo "readonly ENABLE_NODE_MONITORING='${ENABLE_NODE_MONITORING:-false}'"
|
||||
echo "readonly ENABLE_CLUSTER_LOGGING='${ENABLE_CLUSTER_LOGGING:-false}'"
|
||||
echo "readonly ENABLE_NODE_LOGGING='${ENABLE_NODE_LOGGING:-false}'"
|
||||
echo "readonly LOGGING_DESTINATION='${LOGGING_DESTINATION:-}'"
|
||||
echo "readonly ELASTICSEARCH_LOGGING_REPLICAS='${ELASTICSEARCH_LOGGING_REPLICAS:-}'"
|
||||
echo "readonly ENABLE_CLUSTER_DNS='${ENABLE_CLUSTER_DNS:-false}'"
|
||||
echo "readonly DNS_REPLICAS='${DNS_REPLICAS:-}'"
|
||||
echo "readonly DNS_SERVER_IP='${DNS_SERVER_IP:-}'"
|
||||
echo "readonly DNS_DOMAIN='${DNS_DOMAIN:-}'"
|
||||
grep -v "^#" "${KUBE_ROOT}/cluster/aws/templates/create-dynamic-salt-files.sh"
|
||||
@@ -498,10 +502,10 @@ function kube-down {
|
||||
$AWS_CMD delete-vpc --vpc-id $vpc_id > $LOG
|
||||
}
|
||||
|
||||
function setup-logging {
|
||||
function setup-logging-firewall {
|
||||
echo "TODO: setup logging"
|
||||
}
|
||||
|
||||
function teardown-logging {
|
||||
function teardown-logging-firewall {
|
||||
echo "TODO: teardown logging"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,18 +558,18 @@ function restart-kube-proxy {
|
||||
}
|
||||
|
||||
# Setup monitoring using heapster and InfluxDB
|
||||
function setup-monitoring {
|
||||
function setup-monitoring-firewall {
|
||||
echo "not implemented" >/dev/null
|
||||
}
|
||||
|
||||
function teardown-monitoring {
|
||||
function teardown-monitoring-firewall {
|
||||
echo "not implemented" >/dev/null
|
||||
}
|
||||
|
||||
function setup-logging {
|
||||
function setup-logging-firewall {
|
||||
echo "TODO: setup logging"
|
||||
}
|
||||
|
||||
function teardown-logging {
|
||||
function teardown-logging-firewall {
|
||||
echo "TODO: teardown logging"
|
||||
}
|
||||
|
||||
@@ -22,10 +22,14 @@ mkdir -p /srv/salt-overlay/pillar
|
||||
cat <<EOF >/srv/salt-overlay/pillar/cluster-params.sls
|
||||
node_instance_prefix: '$(echo "$NODE_INSTANCE_PREFIX" | sed -e "s/'/''/g")'
|
||||
portal_net: '$(echo "$PORTAL_NET" | sed -e "s/'/''/g")'
|
||||
enable_cluster_monitoring: '$(echo "$ENABLE_CLUSTER_MONITORING" | sed -e "s/'/''/g")'
|
||||
enable_node_monitoring: '$(echo "$ENABLE_NODE_MONITORING" | sed -e "s/'/''/g")'
|
||||
enable_cluster_logging: '$(echo "$ENABLE_CLUSTER_LOGGING" | sed -e "s/'/''/g")'
|
||||
enable_node_logging: '$(echo "$ENABLE_NODE_LOGGING" | sed -e "s/'/''/g")'
|
||||
logging_destination: '$(echo "$LOGGING_DESTINATION" | sed -e "s/'/''/g")'
|
||||
elasticsearch_replicas: '$(echo "$ELASTICSEARCH_LOGGING_REPLICAS" | sed -e "s/'/''/g")'
|
||||
enable_cluster_dns: '$(echo "$ENABLE_CLUSTER_DNS" | sed -e "s/'/''/g")'
|
||||
dns_replicas: '$(echo "$DNS_REPLICAS" | sed -e "s/'/''/g")'
|
||||
dns_server: '$(echo "$DNS_SERVER_IP" | sed -e "s/'/''/g")'
|
||||
dns_domain: '$(echo "$DNS_DOMAIN" | sed -e "s/'/''/g")'
|
||||
EOF
|
||||
|
||||
@@ -393,10 +393,14 @@ function kube-up {
|
||||
echo "readonly SALT_TAR_URL='${SALT_TAR_URL}'"
|
||||
echo "readonly MASTER_HTPASSWD='${htpasswd}'"
|
||||
echo "readonly PORTAL_NET='${PORTAL_NET}'"
|
||||
echo "readonly ENABLE_CLUSTER_MONITORING='${ENABLE_CLUSTER_MONITORING:-false}'"
|
||||
echo "readonly ENABLE_NODE_MONITORING='${ENABLE_NODE_MONITORING:-false}'"
|
||||
echo "readonly ENABLE_CLUSTER_LOGGING='${ENABLE_CLUSTER_LOGGING:-false}'"
|
||||
echo "readonly ENABLE_NODE_LOGGING='${ENABLE_NODE_LOGGING:-false}'"
|
||||
echo "readonly LOGGING_DESTINATION='${LOGGING_DESTINATION:-}'"
|
||||
echo "readonly ELASTICSEARCH_LOGGING_REPLICAS='${ELASTICSEARCH_LOGGING_REPLICAS:-}'"
|
||||
echo "readonly ENABLE_CLUSTER_DNS='${ENABLE_CLUSTER_DNS:-false}'"
|
||||
echo "readonly DNS_REPLICAS='${DNS_REPLICAS:-}'"
|
||||
echo "readonly DNS_SERVER_IP='${DNS_SERVER_IP:-}'"
|
||||
echo "readonly DNS_DOMAIN='${DNS_DOMAIN:-}'"
|
||||
grep -v "^#" "${KUBE_ROOT}/cluster/gce/templates/common.sh"
|
||||
@@ -731,106 +735,70 @@ function restart-kube-proxy {
|
||||
ssh-to-node "$1" "sudo /etc/init.d/kube-proxy restart"
|
||||
}
|
||||
|
||||
# Setup monitoring using heapster and InfluxDB
|
||||
function setup-monitoring {
|
||||
if [[ "${ENABLE_CLUSTER_MONITORING}" == "true" ]]; then
|
||||
echo "Setting up cluster monitoring using Heapster."
|
||||
# Setup monitoring firewalls using heapster and InfluxDB
|
||||
function setup-monitoring-firewall {
|
||||
if [[ "${ENABLE_CLUSTER_MONITORING}" != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
detect-project
|
||||
if ! gcloud compute firewall-rules --project "${PROJECT}" describe monitoring-heapster &> /dev/null; then
|
||||
if ! gcloud compute firewall-rules create monitoring-heapster \
|
||||
--project "${PROJECT}" \
|
||||
--target-tags="${MINION_TAG}" \
|
||||
--network="${NETWORK}" \
|
||||
--allow tcp:80 tcp:8083 tcp:8086; then
|
||||
echo -e "${color_red}Failed to set up firewall for monitoring ${color_norm}" && false
|
||||
fi
|
||||
fi
|
||||
echo "Setting up firewalls to Heapster based cluster monitoring."
|
||||
|
||||
local kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
|
||||
local grafana_host=""
|
||||
if "${kubectl}" create -f "${KUBE_ROOT}/cluster/addons/cluster-monitoring/" &> /dev/null; then
|
||||
# wait for pods to be scheduled on a node.
|
||||
echo "waiting for monitoring pods to be scheduled."
|
||||
for i in `seq 1 10`; do
|
||||
grafana_host=$("${kubectl}" get pods -l name=influxGrafana -o template -t {{range.items}}{{.currentState.hostIP}}:{{end}} | sed s/://g)
|
||||
if [[ $grafana_host != *"<"* ]]; then
|
||||
detect-project
|
||||
gcloud compute firewall-rules create "${INSTANCE_PREFIX}-monitoring-heapster" --project "${PROJECT}" \
|
||||
--allow tcp:80 tcp:8083 tcp:8086 --target-tags="${MINION_TAG}" --network="${NETWORK}"
|
||||
|
||||
local kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
|
||||
local grafana_host=""
|
||||
echo "waiting for monitoring pods to be scheduled."
|
||||
for i in `seq 1 10`; do
|
||||
grafana_host=$("${kubectl}" get pods -l name=influxGrafana -o template -t {{range.items}}{{.currentState.hostIP}}:{{end}} | sed s/://g)
|
||||
if [[ ${grafana_host} != *"<"* ]]; then
|
||||
break
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
if [[ $grafana_host != *"<"* ]]; then
|
||||
echo
|
||||
echo -e "${color_green}Grafana dashboard will be available at ${color_yellow}http://$grafana_host${color_green}. Wait for the monitoring dashboard to be online.${color_norm}"
|
||||
echo
|
||||
else
|
||||
echo -e "${color_red}monitoring pods failed to be scheduled.${color_norm}"
|
||||
fi
|
||||
else
|
||||
echo -e "${color_red}Failed to Setup Monitoring ${color_norm}"
|
||||
teardown-monitoring
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
if [[ ${grafana_host} != *"<"* ]]; then
|
||||
echo
|
||||
echo -e "${color_green}Grafana dashboard will be available at ${color_yellow}http://${grafana_host}${color_green}. Wait for the monitoring dashboard to be online.${color_norm}"
|
||||
echo
|
||||
else
|
||||
echo -e "${color_red}Monitoring pods failed to be scheduled!${color_norm}"
|
||||
fi
|
||||
}
|
||||
|
||||
function teardown-monitoring {
|
||||
if [[ "${ENABLE_CLUSTER_MONITORING}" == "true" ]]; then
|
||||
detect-project
|
||||
|
||||
local kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
|
||||
local kubecfg="${KUBE_ROOT}/cluster/kubecfg.sh"
|
||||
"${kubecfg}" resize monitoring-influxGrafanaController 0 &> /dev/null || true
|
||||
"${kubecfg}" resize monitoring-heapsterController 0 &> /dev/null || true
|
||||
"${kubectl}" delete -f "${KUBE_ROOT}/cluster/addons/cluster-monitoring/" &> /dev/null || true
|
||||
if gcloud compute firewall-rules describe --project "${PROJECT}" monitoring-heapster &> /dev/null; then
|
||||
gcloud compute firewall-rules delete \
|
||||
--project "${PROJECT}" \
|
||||
--quiet \
|
||||
monitoring-heapster &> /dev/null || true
|
||||
fi
|
||||
function teardown-monitoring-firewall {
|
||||
if [[ "${ENABLE_CLUSTER_MONITORING}" != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
detect-project
|
||||
gcloud compute firewall-rules delete -q "${INSTANCE_PREFIX}-monitoring-heapster" --project "${PROJECT}" || true
|
||||
}
|
||||
|
||||
function setup-logging {
|
||||
function setup-logging-firewall {
|
||||
# If logging with Fluentd to Elasticsearch is enabled then create pods
|
||||
# and services for Elasticsearch (for ingesting logs) and Kibana (for
|
||||
# viewing logs).
|
||||
if [[ "${ENABLE_NODE_LOGGING-}" == "true" ]] && \
|
||||
[[ "${LOGGING_DESTINATION-}" == "elasticsearch" ]] && \
|
||||
[[ "${ENABLE_CLUSTER_LOGGING-}" == "true" ]]; then
|
||||
local -r kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
|
||||
if sed -e "s/{ELASTICSEARCH_LOGGING_REPLICAS}/${ELASTICSEARCH_LOGGING_REPLICAS}/g" \
|
||||
"${KUBE_ROOT}"/cluster/addons/fluentd-elasticsearch/es-controller.yaml.in | \
|
||||
"${kubectl}" create -f - &> /dev/null && \
|
||||
"${kubectl}" create -f "${KUBE_ROOT}"/cluster/addons/fluentd-elasticsearch/es-service.yaml &> /dev/null && \
|
||||
"${kubectl}" create -f "${KUBE_ROOT}"/cluster/addons/fluentd-elasticsearch/kibana-controller.yaml &> /dev/null && \
|
||||
"${kubectl}" create -f "${KUBE_ROOT}"/cluster/addons/fluentd-elasticsearch/kibana-service.yaml &> /dev/null; then
|
||||
gcloud compute firewall-rules create fluentd-elasticsearch-logging --project "${PROJECT}" \
|
||||
--allow tcp:5601 tcp:9200 tcp:9300 --target-tags "${INSTANCE_PREFIX}"-minion || true
|
||||
local -r region="${ZONE::-2}"
|
||||
local -r es_ip=$(gcloud compute forwarding-rules --project "${PROJECT}" describe --region "${region}" elasticsearch-logging | grep IPAddress | awk '{print $2}')
|
||||
local -r kibana_ip=$(gcloud compute forwarding-rules --project "${PROJECT}" describe --region "${region}" kibana-logging | grep IPAddress | awk '{print $2}')
|
||||
echo
|
||||
echo -e "${color_green}Cluster logs are ingested into Elasticsearch running at ${color_yellow}http://${es_ip}:9200"
|
||||
echo -e "${color_green}Kibana logging dashboard will be available at ${color_yellow}http://${kibana_ip}:5601${color_norm}"
|
||||
echo
|
||||
else
|
||||
echo -e "${color_red}Failed to launch Elasticsearch and Kibana pods and services for logging.${color_norm}"
|
||||
fi
|
||||
if [[ "${ENABLE_NODE_LOGGING-}" != "true" ]] || \
|
||||
[[ "${LOGGING_DESTINATION-}" != "elasticsearch" ]] || \
|
||||
[[ "${ENABLE_CLUSTER_LOGGING-}" != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
detect-project
|
||||
gcloud compute firewall-rules create "${INSTANCE_PREFIX}-fluentd-elasticsearch-logging" --project "${PROJECT}" \
|
||||
--allow tcp:5601 tcp:9200 tcp:9300 --target-tags "${MINION_TAG}" --network="${NETWORK}"
|
||||
}
|
||||
|
||||
function teardown-logging {
|
||||
if [[ "${ENABLE_NODE_LOGGING-}" == "true" ]] && \
|
||||
[[ "${LOGGING_DESTINATION-}" == "elasticsearch" ]] && \
|
||||
[[ "${ENABLE_CLUSTER_LOGGING-}" == "true" ]]; then
|
||||
local -r kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
|
||||
"${kubectl}" delete replicationController elasticsearch-logging-controller &> /dev/null || true
|
||||
"${kubectl}" delete service elasticsearch-logging &> /dev/null || true
|
||||
"${kubectl}" delete replicationController kibana-logging-controller &> /dev/null || true
|
||||
"${kubectl}" delete service kibana-logging &> /dev/null || true
|
||||
gcloud compute firewall-rules delete -q fluentd-elasticsearch-logging --project "${PROJECT}" || true
|
||||
function teardown-logging-firewall {
|
||||
if [[ "${ENABLE_NODE_LOGGING-}" != "true" ]] || \
|
||||
[[ "${LOGGING_DESTINATION-}" != "elasticsearch" ]] || \
|
||||
[[ "${ENABLE_CLUSTER_LOGGING-}" != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
detect-project
|
||||
gcloud compute firewall-rules delete -q "${INSTANCE_PREFIX}-fluentd-elasticsearch-logging" --project "${PROJECT}" || true
|
||||
}
|
||||
|
||||
# Perform preparations required to run e2e tests
|
||||
|
||||
@@ -115,8 +115,8 @@ function kube-up() {
|
||||
}
|
||||
|
||||
# Called during cluster/kube-up.sh
|
||||
function setup-monitoring() {
|
||||
echo "... in setup-monitoring()" >&2
|
||||
function setup-monitoring-firewall() {
|
||||
echo "... in setup-monitoring-firewall()" >&2
|
||||
# TODO(mbforbes): This isn't currently supported in GKE.
|
||||
}
|
||||
|
||||
@@ -239,8 +239,8 @@ function test-teardown() {
|
||||
}
|
||||
|
||||
# Tears down monitoring.
|
||||
function teardown-monitoring() {
|
||||
echo "... in teardown-monitoring()" >&2
|
||||
function teardown-monitoring-firewall() {
|
||||
echo "... in teardown-monitoring-firewall()" >&2
|
||||
# TODO(mbforbes): This isn't currently supported in GKE.
|
||||
}
|
||||
|
||||
@@ -257,10 +257,10 @@ function kube-down() {
|
||||
--zone="${ZONE}" "${CLUSTER_NAME}"
|
||||
}
|
||||
|
||||
function setup-logging {
|
||||
function setup-logging-firewall {
|
||||
echo "TODO: setup logging"
|
||||
}
|
||||
|
||||
function teardown-logging {
|
||||
function teardown-logging-firewall {
|
||||
echo "TODO: teardown logging"
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ source "${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
|
||||
echo "Bringing down cluster using provider: $KUBERNETES_PROVIDER"
|
||||
|
||||
verify-prereqs
|
||||
teardown-monitoring
|
||||
teardown-logging
|
||||
teardown-monitoring-firewall
|
||||
teardown-logging-firewall
|
||||
|
||||
kube-down
|
||||
|
||||
|
||||
@@ -39,22 +39,10 @@ kube-up
|
||||
echo "... calling validate-cluster" >&2
|
||||
"${KUBE_ROOT}/cluster/validate-cluster.sh"
|
||||
|
||||
echo "... calling setup-monitoring" >&2
|
||||
setup-monitoring
|
||||
echo "... calling setup-monitoring-firewall" >&2
|
||||
setup-monitoring-firewall
|
||||
|
||||
if [[ "${ENABLE_CLUSTER_DNS}" == "true" ]]; then
|
||||
echo "... setting up cluster DNS"
|
||||
sed -e "s/{DNS_DOMAIN}/$DNS_DOMAIN/g" \
|
||||
-e "s/{DNS_REPLICAS}/$DNS_REPLICAS/g" \
|
||||
"${KUBE_ROOT}/cluster/addons/dns/skydns-rc.yaml.in" \
|
||||
| "${KUBE_ROOT}/cluster/kubectl.sh" create -f -
|
||||
|
||||
sed -e "s/{DNS_SERVER_IP}/$DNS_SERVER_IP/g" \
|
||||
"${KUBE_ROOT}/cluster/addons/dns/skydns-svc.yaml.in" \
|
||||
| "${KUBE_ROOT}/cluster/kubectl.sh" create -f -
|
||||
fi
|
||||
|
||||
echo "... calling setup-logging" >&2
|
||||
setup-logging
|
||||
echo "... calling setup-logging-firewall" >&2
|
||||
setup-logging-firewall
|
||||
|
||||
echo "Done" >&2
|
||||
|
||||
@@ -342,19 +342,19 @@ kube-up() {
|
||||
echo
|
||||
}
|
||||
|
||||
function setup-monitoring {
|
||||
function setup-monitoring-firewall {
|
||||
echo "TODO"
|
||||
}
|
||||
|
||||
function teardown-monitoring {
|
||||
function teardown-monitoring-firewall {
|
||||
echo "TODO"
|
||||
}
|
||||
|
||||
function setup-logging {
|
||||
function setup-logging-firewall {
|
||||
echo "TODO: setup logging"
|
||||
}
|
||||
|
||||
function teardown-logging {
|
||||
function teardown-logging-firewall {
|
||||
echo "TODO: teardown logging"
|
||||
}
|
||||
|
||||
|
||||
79
cluster/saltbase/salt/kube-addons/init.sls
Normal file
79
cluster/saltbase/salt/kube-addons/init.sls
Normal file
@@ -0,0 +1,79 @@
|
||||
{% if pillar.get('enable_cluster_monitoring', '').lower() == 'true' %}
|
||||
/etc/kubernetes/addons/cluster-monitoring:
|
||||
file.recurse:
|
||||
- source: salt://kube-addons/cluster-monitoring
|
||||
- include_pat: E@^.+\.yaml$
|
||||
- user: root
|
||||
- group: root
|
||||
- dir_mode: 755
|
||||
- file_mode: 644
|
||||
{% endif %}
|
||||
|
||||
{% if pillar.get('enable_cluster_dns', '').lower() == 'true' %}
|
||||
/etc/kubernetes/addons/dns/skydns-svc.yaml:
|
||||
file.managed:
|
||||
- source: salt://kube-addons/dns/skydns-svc.yaml.in
|
||||
- template: jinja
|
||||
- group: root
|
||||
- dir_mode: 755
|
||||
- makedirs: True
|
||||
|
||||
/etc/kubernetes/addons/dns/skydns-rc.yaml:
|
||||
file.managed:
|
||||
- source: salt://kube-addons/dns/skydns-rc.yaml.in
|
||||
- template: jinja
|
||||
- group: root
|
||||
- dir_mode: 755
|
||||
- makedirs: True
|
||||
{% endif %}
|
||||
|
||||
{% if pillar.get('enable_node_logging', '').lower() == 'true'
|
||||
and pillar.get('logging_destination').lower() == 'elasticsearch'
|
||||
and pillar.get('enable_cluster_logging', '').lower() == 'true' %}
|
||||
/etc/kubernetes/addons/fluentd-elasticsearch:
|
||||
file.recurse:
|
||||
- source: salt://kube-addons/fluentd-elasticsearch
|
||||
- include_pat: E@^.+\.yaml$
|
||||
- user: root
|
||||
- group: root
|
||||
- dir_mode: 755
|
||||
- file_mode: 644
|
||||
|
||||
/etc/kubernetes/addons/fluentd-elasticsearch/es-controller.yaml:
|
||||
file.managed:
|
||||
- source: salt://kube-addons/fluentd-elasticsearch/es-controller.yaml.in
|
||||
- template: jinja
|
||||
- group: root
|
||||
- dir_mode: 755
|
||||
- makedirs: True
|
||||
{% endif %}
|
||||
|
||||
{% if grains['os_family'] == 'RedHat' %}
|
||||
|
||||
/usr/lib/systemd/system/kube-addons.service:
|
||||
file.managed:
|
||||
- source: salt://kube-addons/kube-addons.service
|
||||
- user: root
|
||||
- group: root
|
||||
|
||||
/usr/lib/systemd/scripts/kube-addons:
|
||||
file.managed:
|
||||
- source: salt://kube-addons/initd
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: 755
|
||||
|
||||
{% else %}
|
||||
|
||||
/etc/init.d/kube-addons:
|
||||
file.managed:
|
||||
- source: salt://kube-addons/initd
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: 755
|
||||
|
||||
{% endif %}
|
||||
|
||||
kube-addons:
|
||||
service.running:
|
||||
- enable: True
|
||||
123
cluster/saltbase/salt/kube-addons/initd
Normal file
123
cluster/saltbase/salt/kube-addons/initd
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: kube-addons
|
||||
# Required-Start: $local_fs $network $syslog kube-apiserver
|
||||
# Required-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Kubernetes Addon Object Manager
|
||||
# Description:
|
||||
# Enforces installation of Kubernetes Addon Objects
|
||||
### END INIT INFO
|
||||
|
||||
|
||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
||||
DESC="Kubernetes Addon Object Manager"
|
||||
NAME=kube-addons
|
||||
DAEMON_LOG_FILE=/var/log/$NAME.log
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
KUBECTL=/usr/local/bin/kubectl
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||
# and status_of_proc is working.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
function addon_manager_async() {
|
||||
# The business logic for whether a given object should be created
|
||||
# was already enforced by salt, and /etc/kubernetes/addons is the
|
||||
# managed result is of that. Start everything below that directory.
|
||||
echo "== Kubernetes addon manager started at $(date -Is) =="
|
||||
for obj in $(find /etc/kubernetes/addons -name \*.yaml); do
|
||||
${KUBECTL} create -f ${obj} &
|
||||
echo "++ addon ${obj} started in pid $! ++"
|
||||
done
|
||||
noerrors="true"
|
||||
for pid in $(jobs -p); do
|
||||
wait ${pid} || noerrors="false"
|
||||
echo "++ pid ${pid} complete ++"
|
||||
done
|
||||
if [ ${noerrors} == "true" ]; then
|
||||
echo "== Kubernetes addon manager completed successfully at $(date -Is) =="
|
||||
else
|
||||
echo "== Kubernetes addon manager completed with errors at $(date -Is) =="
|
||||
fi
|
||||
|
||||
# We stay around so that status checks by salt make it look like
|
||||
# the service is good. (We could do this is other ways, but this
|
||||
# is simple.)
|
||||
sleep infinity
|
||||
}
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
addon_manager_async </dev/null >>${DAEMON_LOG_FILE} 2>&1 &
|
||||
echo $! > ${PIDFILE}
|
||||
disown
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
kill $(cat ${PIDFILE})
|
||||
rm ${PIDFILE}
|
||||
return
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) log_end_msg 0 || exit 0 ;;
|
||||
2) log_end_msg 1 || exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) log_end_msg 0 ;;
|
||||
2) exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
if [ ! -e ${PIDFILE} ]; then
|
||||
exit 1
|
||||
fi
|
||||
pid=$(cat ${PIDFILE})
|
||||
# Checks that ${pid} is running AND is us.
|
||||
ps --no-headers ${pid} | grep ${SCRIPTNAME} > /dev/null || exit $?
|
||||
;;
|
||||
|
||||
restart|force-reload)
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
9
cluster/saltbase/salt/kube-addons/kube-addons.service
Normal file
9
cluster/saltbase/salt/kube-addons/kube-addons.service
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Kubernetes Addon Object Manager
|
||||
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/lib/systemd/scripts/kube-addons start
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -36,6 +36,7 @@ base:
|
||||
- nginx
|
||||
- kube-client-tools
|
||||
- logrotate
|
||||
- kube-addons
|
||||
{% if grains['cloud'] is defined and grains['cloud'] == 'azure' %}
|
||||
- openvpn
|
||||
{% endif %}
|
||||
|
||||
@@ -82,10 +82,14 @@ mkdir -p /srv/salt-overlay/pillar
|
||||
cat <<EOF >/srv/salt-overlay/pillar/cluster-params.sls
|
||||
portal_net: '$(echo "$PORTAL_NET" | sed -e "s/'/''/g")'
|
||||
cert_ip: '$(echo "$MASTER_IP" | sed -e "s/'/''/g")'
|
||||
enable_cluster_monitoring: '$(echo "$ENABLE_CLUSTER_MONITORING" | sed -e "s/'/''/g")'
|
||||
enable_node_monitoring: '$(echo "$ENABLE_NODE_MONITORING" | sed -e "s/'/''/g")'
|
||||
enable_cluster_logging: '$(echo "$ENABLE_CLUSTER_LOGGING" | sed -e "s/'/''/g")'
|
||||
enable_node_logging: '$(echo "$ENABLE_NODE_LOGGING" | sed -e "s/'/''/g")'
|
||||
logging_destination: '$(echo "$LOGGING_DESTINATION" | sed -e "s/'/''/g")'
|
||||
elasticsearch_replicas: '$(echo "$ELASTICSEARCH_LOGGING_REPLICAS" | sed -e "s/'/''/g")'
|
||||
enable_cluster_dns: '$(echo "$ENABLE_CLUSTER_DNS" | sed -e "s/'/''/g")'
|
||||
dns_replicas: '$(echo "$DNS_REPLICAS" | sed -e "s/'/''/g")'
|
||||
dns_server: '$(echo "$DNS_SERVER_IP" | sed -e "s/'/''/g")'
|
||||
dns_domain: '$(echo "$DNS_DOMAIN" | sed -e "s/'/''/g")'
|
||||
EOF
|
||||
|
||||
@@ -261,11 +261,11 @@ function restart-kube-proxy {
|
||||
ssh-to-node "$1" "sudo systemctl restart kube-proxy"
|
||||
}
|
||||
|
||||
function setup-monitoring {
|
||||
echo "TODO"
|
||||
function setup-monitoring-firewall {
|
||||
echo "TODO"
|
||||
}
|
||||
|
||||
function teardown-monitoring {
|
||||
function teardown-monitoring-firewall {
|
||||
echo "TODO"
|
||||
}
|
||||
|
||||
@@ -274,10 +274,10 @@ function prepare-e2e() {
|
||||
echo "Vagrant doesn't need special preparations for e2e tests"
|
||||
}
|
||||
|
||||
function setup-logging {
|
||||
function setup-logging-firewall {
|
||||
echo "TODO: setup logging"
|
||||
}
|
||||
|
||||
function teardown-logging {
|
||||
function teardown-logging-firewall {
|
||||
echo "TODO: teardown logging"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,14 @@ mkdir -p /srv/salt-overlay/pillar
|
||||
cat <<EOF >/srv/salt-overlay/pillar/cluster-params.sls
|
||||
node_instance_prefix: $NODE_INSTANCE_PREFIX
|
||||
portal_net: $PORTAL_NET
|
||||
enable_cluster_monitoring: $ENABLE_CLUSTER_MONITORING
|
||||
enable_node_monitoring: $ENABLE_NODE_MONITORING
|
||||
enable_cluster_logging: $ENABLE_CLUSTER_LOGGING
|
||||
enable_node_logging: $ENABLE_NODE_LOGGING
|
||||
logging_destination: $LOGGING_DESTINATION
|
||||
elasticsearch_replicas: $ELASTICSEARCH_LOGGING_REPLICAS
|
||||
enable_cluster_dns: $ENABLE_CLUSTER_DNS
|
||||
dns_replicas: $DNS_REPLICAS
|
||||
dns_server: $DNS_SERVER_IP
|
||||
dns_domain: $DNS_DOMAIN
|
||||
EOF
|
||||
|
||||
@@ -478,18 +478,18 @@ function test-teardown {
|
||||
echo "TODO"
|
||||
}
|
||||
|
||||
function setup-monitoring {
|
||||
function setup-monitoring-firewall {
|
||||
echo "TODO"
|
||||
}
|
||||
|
||||
function teardown-monitoring {
|
||||
function teardown-monitoring-firewall {
|
||||
echo "TODO"
|
||||
}
|
||||
|
||||
function setup-logging {
|
||||
function setup-logging-firewall {
|
||||
echo "TODO: setup logging"
|
||||
}
|
||||
|
||||
function teardown-logging {
|
||||
function teardown-logging-firewall {
|
||||
echo "TODO: teardown logging"
|
||||
}
|
||||
122
docs/kubectl.md
122
docs/kubectl.md
@@ -29,8 +29,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -65,8 +65,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-p, --port=8001: The port on which to run the proxy
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
@@ -122,9 +122,9 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--no-headers=false: When using the default output, don't print headers
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-o, --output="": Output format: json|yaml|template|templatefile
|
||||
--output-version="": Output the formatted object with the given version (default api-version)
|
||||
-l, --selector="": Selector (label query) to filter on
|
||||
@@ -167,8 +167,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -213,8 +213,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -262,8 +262,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--patch="": A JSON document to override the existing resource. The resource is downloaded, then patched with the JSON, the updated
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
@@ -322,8 +322,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-l, --selector="": Selector (label query) to filter on
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
@@ -370,8 +370,8 @@ Available Commands:
|
||||
--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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -394,6 +394,7 @@ Additional help topics:
|
||||
kubectl rollingupdate Perform a rolling update of the given ReplicationController
|
||||
kubectl resize Set a new size for a resizable resource (currently only Replication Controllers)
|
||||
kubectl run-container Run a particular image on the cluster.
|
||||
kubectl stop Gracefully shutdown a resource
|
||||
|
||||
Use "kubectl help [command]" for more information about that command.
|
||||
```
|
||||
@@ -425,8 +426,8 @@ Usage:
|
||||
--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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -466,8 +467,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--server="": server for the cluster entry in .kubeconfig
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -507,8 +508,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": token for the user entry in .kubeconfig
|
||||
@@ -549,7 +550,7 @@ Usage:
|
||||
--logtostderr=true: log to standard error instead of files
|
||||
--match-server-version=false: Require server version to match client version
|
||||
--namespace="": namespace for the context entry in .kubeconfig
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -591,8 +592,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -632,8 +633,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -670,8 +671,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -714,8 +715,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -757,8 +758,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
@@ -805,8 +806,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--poll-interval="3s": Time delay between polling controller status after update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
@@ -859,8 +860,8 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--replicas=-1: The new number desired number of replicas. Required.
|
||||
--resource-version="": Precondition for resource version. Requires that the current resource version match this value in order to resize
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
@@ -915,9 +916,9 @@ 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
|
||||
-n, --namespace="": If present, the namespace scope for this CLI request.
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--no-headers=false: When using the default output, don't print headers
|
||||
--ns-path="/home/username/.kubernetes_ns": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-o, --output="": Output format: json|yaml|template|templatefile
|
||||
--output-version="": Output the formatted object with the given version (default api-version)
|
||||
--overrides="": An inline JSON override for the generated object. If this is non-empty, it is parsed used to override the generated object. Requires that the object supply a valid apiVersion field.
|
||||
@@ -933,3 +934,46 @@ Usage:
|
||||
|
||||
```
|
||||
|
||||
#### stop
|
||||
Gracefully shutdown a resource
|
||||
|
||||
Attempts to shutdown and delete a resource that supports graceful termination.
|
||||
If the resource is resizable it will be resized to 0 before deletion.
|
||||
|
||||
Examples:
|
||||
$ kubectl stop replicationcontroller foo
|
||||
foo stopped
|
||||
|
||||
|
||||
Usage:
|
||||
```
|
||||
kubectl stop <resource> <id> [flags]
|
||||
|
||||
Available Flags:
|
||||
--alsologtostderr=false: log to standard error as well as files
|
||||
--api-version="": The API version to use when talking to the server
|
||||
-a, --auth-path="": Path to the auth info file. If missing, prompt the user. Only used if using https.
|
||||
--certificate-authority="": Path to a cert. file for the certificate authority.
|
||||
--client-certificate="": Path to a client key file for TLS.
|
||||
--client-key="": Path to a client key file for TLS.
|
||||
--cluster="": The name of the kubeconfig cluster to use
|
||||
--context="": The name of the kubeconfig context to use
|
||||
--insecure-skip-tls-verify=false: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
|
||||
--kubeconfig="": Path to the kubeconfig file to use for CLI requests.
|
||||
--log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
|
||||
--log_dir=: If non-empty, write log files in this directory
|
||||
--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
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
|
||||
-s, --server="": The address of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--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
|
||||
--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
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ type Context struct {
|
||||
AuthInfo string `json:"user"`
|
||||
// Namespace is the default namespace to use on unspecified requests
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
// NamespacePath is the path to a kubernetes ns file (~/.kubernetes_ns)
|
||||
NamespacePath string `json:"namespace-path,omitempty"`
|
||||
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
||||
Extensions map[string]runtime.EmbeddedObject `json:"extensions,omitempty"`
|
||||
}
|
||||
|
||||
@@ -83,6 +83,8 @@ type Context struct {
|
||||
AuthInfo string `json:"user"`
|
||||
// Namespace is the default namespace to use on unspecified requests
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
// NamespacePath is the path to a kubernetes ns file (~/.kubernetes_ns)
|
||||
NamespacePath string `json:"namespace-path,omitempty"`
|
||||
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
||||
Extensions []NamedExtension `json:"extensions,omitempty"`
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
||||
)
|
||||
|
||||
@@ -36,9 +37,12 @@ var (
|
||||
|
||||
// ClientConfig is used to make it easy to get an api server client
|
||||
type ClientConfig interface {
|
||||
// RawConfig returns the merged result of all overrides
|
||||
RawConfig() (clientcmdapi.Config, error)
|
||||
// ClientConfig returns a complete client config
|
||||
ClientConfig() (*client.Config, error)
|
||||
// Namespace returns the namespace resulting from the merged result of all overrides
|
||||
Namespace() (string, error)
|
||||
}
|
||||
|
||||
// DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information
|
||||
@@ -226,6 +230,35 @@ func canIdentifyUser(config client.Config) bool {
|
||||
|
||||
}
|
||||
|
||||
// Namespace implements KubeConfig
|
||||
func (config DirectClientConfig) Namespace() (string, error) {
|
||||
if err := config.ConfirmUsable(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
configContext := config.getContext()
|
||||
|
||||
if len(configContext.Namespace) != 0 {
|
||||
return configContext.Namespace, nil
|
||||
}
|
||||
|
||||
if len(configContext.NamespacePath) != 0 {
|
||||
nsInfo, err := kubectl.LoadNamespaceInfo(configContext.NamespacePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nsInfo.Namespace, nil
|
||||
}
|
||||
|
||||
// if nothing was specified, try the default file
|
||||
nsInfo, err := kubectl.LoadNamespaceInfo(os.Getenv("HOME") + "/.kubernetes_ns")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return nsInfo.Namespace, nil
|
||||
}
|
||||
|
||||
// ConfirmUsable looks a particular context and determines if that particular part of the config is useable. There might still be errors in the config,
|
||||
// but no errors in the sections requested or referenced. It does not return early so that it can find as many errors as possible.
|
||||
func (config DirectClientConfig) ConfirmUsable() error {
|
||||
@@ -248,21 +281,30 @@ func (config DirectClientConfig) getContextName() string {
|
||||
}
|
||||
|
||||
func (config DirectClientConfig) getAuthInfoName() string {
|
||||
if len(config.overrides.AuthInfoName) != 0 {
|
||||
return config.overrides.AuthInfoName
|
||||
if len(config.overrides.Context.AuthInfo) != 0 {
|
||||
return config.overrides.Context.AuthInfo
|
||||
}
|
||||
return config.getContext().AuthInfo
|
||||
}
|
||||
|
||||
func (config DirectClientConfig) getClusterName() string {
|
||||
if len(config.overrides.ClusterName) != 0 {
|
||||
return config.overrides.ClusterName
|
||||
if len(config.overrides.Context.Cluster) != 0 {
|
||||
return config.overrides.Context.Cluster
|
||||
}
|
||||
return config.getContext().Cluster
|
||||
}
|
||||
|
||||
func (config DirectClientConfig) getContext() clientcmdapi.Context {
|
||||
return config.config.Contexts[config.getContextName()]
|
||||
contexts := config.config.Contexts
|
||||
contextName := config.getContextName()
|
||||
|
||||
var mergedContext clientcmdapi.Context
|
||||
if configContext, exists := contexts[contextName]; exists {
|
||||
mergo.Merge(&mergedContext, configContext)
|
||||
}
|
||||
mergo.Merge(&mergedContext, config.overrides.Context)
|
||||
|
||||
return mergedContext
|
||||
}
|
||||
|
||||
func (config DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo {
|
||||
|
||||
@@ -48,6 +48,24 @@ func createValidTestConfig() *clientcmdapi.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func TestMergeContext(t *testing.T) {
|
||||
const namespace = "overriden-namespace"
|
||||
|
||||
config := createValidTestConfig()
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
|
||||
Context: clientcmdapi.Context{
|
||||
Namespace: namespace,
|
||||
},
|
||||
})
|
||||
|
||||
actual, err := clientBuilder.Namespace()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(namespace, actual, t)
|
||||
}
|
||||
|
||||
func TestCreateClean(t *testing.T) {
|
||||
config := createValidTestConfig()
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{})
|
||||
|
||||
@@ -78,3 +78,13 @@ func (config DeferredLoadingClientConfig) ClientConfig() (*client.Config, error)
|
||||
|
||||
return mergedClientConfig.ClientConfig()
|
||||
}
|
||||
|
||||
// Namespace implements KubeConfig
|
||||
func (config DeferredLoadingClientConfig) Namespace() (string, error) {
|
||||
mergedKubeConfig, err := config.createClientConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return mergedKubeConfig.Namespace()
|
||||
}
|
||||
|
||||
@@ -27,10 +27,8 @@ import (
|
||||
type ConfigOverrides struct {
|
||||
AuthInfo clientcmdapi.AuthInfo
|
||||
ClusterInfo clientcmdapi.Cluster
|
||||
Namespace string
|
||||
Context clientcmdapi.Context
|
||||
CurrentContext string
|
||||
ClusterName string
|
||||
AuthInfoName string
|
||||
}
|
||||
|
||||
// ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly
|
||||
@@ -38,10 +36,8 @@ type ConfigOverrides struct {
|
||||
type ConfigOverrideFlags struct {
|
||||
AuthOverrideFlags AuthOverrideFlags
|
||||
ClusterOverrideFlags ClusterOverrideFlags
|
||||
Namespace string
|
||||
ContextOverrideFlags ContextOverrideFlags
|
||||
CurrentContext string
|
||||
ClusterName string
|
||||
AuthInfoName string
|
||||
}
|
||||
|
||||
// AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects
|
||||
@@ -53,6 +49,14 @@ type AuthOverrideFlags struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
// ContextOverrideFlags holds the flag names to be used for binding command line flags for Cluster objects
|
||||
type ContextOverrideFlags struct {
|
||||
ClusterName string
|
||||
AuthInfoName string
|
||||
Namespace string
|
||||
NamespacePath string
|
||||
}
|
||||
|
||||
// ClusterOverride holds the flag names to be used for binding command line flags for Cluster objects
|
||||
type ClusterOverrideFlags struct {
|
||||
APIServer string
|
||||
@@ -63,18 +67,19 @@ type ClusterOverrideFlags struct {
|
||||
}
|
||||
|
||||
const (
|
||||
FlagClusterName = "cluster"
|
||||
FlagAuthInfoName = "user"
|
||||
FlagContext = "context"
|
||||
FlagNamespace = "namespace"
|
||||
FlagAPIServer = "server"
|
||||
FlagAPIVersion = "api-version"
|
||||
FlagAuthPath = "auth-path"
|
||||
FlagInsecure = "insecure-skip-tls-verify"
|
||||
FlagCertFile = "client-certificate"
|
||||
FlagKeyFile = "client-key"
|
||||
FlagCAFile = "certificate-authority"
|
||||
FlagBearerToken = "token"
|
||||
FlagClusterName = "cluster"
|
||||
FlagAuthInfoName = "user"
|
||||
FlagContext = "context"
|
||||
FlagNamespace = "namespace"
|
||||
FlagNamespacePath = "ns-path"
|
||||
FlagAPIServer = "server"
|
||||
FlagAPIVersion = "api-version"
|
||||
FlagAuthPath = "auth-path"
|
||||
FlagInsecure = "insecure-skip-tls-verify"
|
||||
FlagCertFile = "client-certificate"
|
||||
FlagKeyFile = "client-key"
|
||||
FlagCAFile = "certificate-authority"
|
||||
FlagBearerToken = "token"
|
||||
)
|
||||
|
||||
// RecommendedAuthOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
|
||||
@@ -102,10 +107,18 @@ func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags {
|
||||
return ConfigOverrideFlags{
|
||||
AuthOverrideFlags: RecommendedAuthOverrideFlags(prefix),
|
||||
ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix),
|
||||
Namespace: prefix + FlagNamespace,
|
||||
ContextOverrideFlags: RecommendedContextOverrideFlags(prefix),
|
||||
CurrentContext: prefix + FlagContext,
|
||||
ClusterName: prefix + FlagClusterName,
|
||||
AuthInfoName: prefix + FlagAuthInfoName,
|
||||
}
|
||||
}
|
||||
|
||||
// RecommendedContextOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
|
||||
func RecommendedContextOverrideFlags(prefix string) ContextOverrideFlags {
|
||||
return ContextOverrideFlags{
|
||||
ClusterName: prefix + FlagClusterName,
|
||||
AuthInfoName: prefix + FlagAuthInfoName,
|
||||
Namespace: prefix + FlagNamespace,
|
||||
NamespacePath: prefix + FlagNamespacePath,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,9 +152,14 @@ func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, flags *pflag.FlagSet, f
|
||||
func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNames ConfigOverrideFlags) {
|
||||
BindAuthInfoFlags(&overrides.AuthInfo, flags, flagNames.AuthOverrideFlags)
|
||||
BindClusterFlags(&overrides.ClusterInfo, flags, flagNames.ClusterOverrideFlags)
|
||||
// TODO not integrated yet
|
||||
// flags.StringVar(&overrides.Namespace, flagNames.Namespace, "", "If present, the namespace scope for this CLI request.")
|
||||
BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags)
|
||||
flags.StringVar(&overrides.CurrentContext, flagNames.CurrentContext, "", "The name of the kubeconfig context to use")
|
||||
flags.StringVar(&overrides.ClusterName, flagNames.ClusterName, "", "The name of the kubeconfig cluster to use")
|
||||
flags.StringVar(&overrides.AuthInfoName, flagNames.AuthInfoName, "", "The name of the kubeconfig user to use")
|
||||
}
|
||||
|
||||
// BindFlags is a convenience method to bind the specified flags to their associated variables
|
||||
func BindContextFlags(contextInfo *clientcmdapi.Context, flags *pflag.FlagSet, flagNames ContextOverrideFlags) {
|
||||
flags.StringVar(&contextInfo.Cluster, flagNames.ClusterName, "", "The name of the kubeconfig cluster to use")
|
||||
flags.StringVar(&contextInfo.AuthInfo, flagNames.AuthInfoName, "", "The name of the kubeconfig user to use")
|
||||
flags.StringVar(&contextInfo.Namespace, flagNames.Namespace, "", "If present, the namespace scope for this CLI request.")
|
||||
flags.StringVar(&contextInfo.NamespacePath, flagNames.NamespacePath, "", "Path to the namespace info file that holds the namespace context to use for CLI requests.")
|
||||
}
|
||||
|
||||
@@ -64,8 +64,12 @@ type Factory struct {
|
||||
Printer func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error)
|
||||
// Returns a Resizer for changing the size of the specified RESTMapping type or an error
|
||||
Resizer func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Resizer, error)
|
||||
// Returns a Reaper for gracefully shutting down resources.
|
||||
Reaper func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Reaper, error)
|
||||
// Returns a schema that can validate objects stored on disk.
|
||||
Validator func(*cobra.Command) (validation.Schema, error)
|
||||
// Returns the default namespace to use in cases where no other namespace is specified
|
||||
DefaultNamespace func(cmd *cobra.Command) (string, error)
|
||||
}
|
||||
|
||||
// NewFactory creates a factory with the default Kubernetes resources defined
|
||||
@@ -84,8 +88,11 @@ func NewFactory() *Factory {
|
||||
flags: flags,
|
||||
|
||||
Object: func(cmd *cobra.Command) (meta.RESTMapper, runtime.ObjectTyper) {
|
||||
version := GetFlagString(cmd, "api-version")
|
||||
return kubectl.OutputVersionMapper{mapper, version}, api.Scheme
|
||||
cfg, err := clientConfig.ClientConfig()
|
||||
checkErr(err)
|
||||
cmdApiVersion := cfg.Version
|
||||
|
||||
return kubectl.OutputVersionMapper{mapper, cmdApiVersion}, api.Scheme
|
||||
},
|
||||
Client: func(cmd *cobra.Command) (*client.Client, error) {
|
||||
return clients.ClientForVersion("")
|
||||
@@ -119,11 +126,14 @@ func NewFactory() *Factory {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resizer, ok := kubectl.ResizerFor(mapping.Kind, client)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no resizer has been implemented for %q", mapping.Kind)
|
||||
return kubectl.ResizerFor(mapping.Kind, client)
|
||||
},
|
||||
Reaper: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Reaper, error) {
|
||||
client, err := clients.ClientForVersion(mapping.APIVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resizer, nil
|
||||
return kubectl.ReaperFor(mapping.Kind, client)
|
||||
},
|
||||
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
|
||||
if GetFlagBool(cmd, "validate") {
|
||||
@@ -135,6 +145,9 @@ func NewFactory() *Factory {
|
||||
}
|
||||
return validation.NullSchema{}, nil
|
||||
},
|
||||
DefaultNamespace: func(cmd *cobra.Command) (string, error) {
|
||||
return clientConfig.Namespace()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,8 +171,6 @@ func (f *Factory) BindFlags(flags *pflag.FlagSet) {
|
||||
// TODO Add a verbose flag that turns on glog logging. Probably need a way
|
||||
// to do that automatically for every subcommand.
|
||||
flags.BoolVar(&f.clients.matchVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")
|
||||
flags.String("ns-path", os.Getenv("HOME")+"/.kubernetes_ns", "Path to the namespace info file that holds the namespace context to use for CLI requests.")
|
||||
flags.StringP("namespace", "n", "", "If present, the namespace scope for this CLI request.")
|
||||
flags.Bool("validate", false, "If true, use a schema to validate the input before sending it")
|
||||
}
|
||||
|
||||
@@ -193,6 +204,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
||||
cmds.AddCommand(f.NewCmdResize(out))
|
||||
|
||||
cmds.AddCommand(f.NewCmdRunContainer(out))
|
||||
cmds.AddCommand(f.NewCmdStop(out))
|
||||
|
||||
return cmds
|
||||
}
|
||||
@@ -269,38 +281,6 @@ func runHelp(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
}
|
||||
|
||||
// GetKubeNamespace returns the value of the namespace a
|
||||
// user provided on the command line or use the default
|
||||
// namespace.
|
||||
func GetKubeNamespace(cmd *cobra.Command) string {
|
||||
result := api.NamespaceDefault
|
||||
if ns := GetFlagString(cmd, "namespace"); len(ns) > 0 {
|
||||
result = ns
|
||||
glog.V(2).Infof("Using namespace from -ns flag")
|
||||
} else {
|
||||
nsPath := GetFlagString(cmd, "ns-path")
|
||||
nsInfo, err := kubectl.LoadNamespaceInfo(nsPath)
|
||||
if err != nil {
|
||||
glog.Fatalf("Error loading current namespace: %v", err)
|
||||
}
|
||||
result = nsInfo.Namespace
|
||||
}
|
||||
glog.V(2).Infof("Using namespace %s", result)
|
||||
return result
|
||||
}
|
||||
|
||||
// GetExplicitKubeNamespace returns the value of the namespace a
|
||||
// user explicitly provided on the command line, or false if no
|
||||
// such namespace was specified.
|
||||
func GetExplicitKubeNamespace(cmd *cobra.Command) (string, bool) {
|
||||
if ns := GetFlagString(cmd, "namespace"); len(ns) > 0 {
|
||||
return ns, true
|
||||
}
|
||||
// TODO: determine when --ns-path is set but equal to the default
|
||||
// value and return its value and true.
|
||||
return "", false
|
||||
}
|
||||
|
||||
type clientSwaggerSchema struct {
|
||||
c *client.Client
|
||||
t runtime.ObjectTyper
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
. "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
|
||||
@@ -92,13 +93,15 @@ func (t *testDescriber) Describe(namespace, name string) (output string, err err
|
||||
}
|
||||
|
||||
type testFactory struct {
|
||||
Mapper meta.RESTMapper
|
||||
Typer runtime.ObjectTyper
|
||||
Client kubectl.RESTClient
|
||||
Describer kubectl.Describer
|
||||
Printer kubectl.ResourcePrinter
|
||||
Validator validation.Schema
|
||||
Err error
|
||||
Mapper meta.RESTMapper
|
||||
Typer runtime.ObjectTyper
|
||||
Client kubectl.RESTClient
|
||||
Describer kubectl.Describer
|
||||
Printer kubectl.ResourcePrinter
|
||||
Validator validation.Schema
|
||||
Namespace string
|
||||
ClientConfig *client.Config
|
||||
Err error
|
||||
}
|
||||
|
||||
func NewTestFactory() (*Factory, *testFactory, runtime.Codec) {
|
||||
@@ -124,6 +127,12 @@ func NewTestFactory() (*Factory, *testFactory, runtime.Codec) {
|
||||
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
|
||||
return t.Validator, t.Err
|
||||
},
|
||||
DefaultNamespace: func(cmd *cobra.Command) (string, error) {
|
||||
return t.Namespace, t.Err
|
||||
},
|
||||
ClientConfig: func(cmd *cobra.Command) (*client.Config, error) {
|
||||
return t.ClientConfig, t.Err
|
||||
},
|
||||
}, t, codec
|
||||
}
|
||||
|
||||
@@ -147,6 +156,12 @@ func NewAPIFactory() (*Factory, *testFactory, runtime.Codec) {
|
||||
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
|
||||
return t.Validator, t.Err
|
||||
},
|
||||
DefaultNamespace: func(cmd *cobra.Command) (string, error) {
|
||||
return t.Namespace, t.Err
|
||||
},
|
||||
ClientConfig: func(cmd *cobra.Command) (*client.Config, error) {
|
||||
return t.ClientConfig, t.Err
|
||||
},
|
||||
}, t, latest.Codec
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,13 @@ Examples:
|
||||
schema, err := f.Validator(cmd)
|
||||
checkErr(err)
|
||||
|
||||
cmdNamespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
|
||||
mapper, typer := f.Object(cmd)
|
||||
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
|
||||
ContinueOnError().
|
||||
NamespaceParam(GetKubeNamespace(cmd)).RequireNamespace().
|
||||
NamespaceParam(cmdNamespace).RequireNamespace().
|
||||
FilenameParam(flags.Filenames...).
|
||||
Flatten().
|
||||
Do()
|
||||
|
||||
@@ -41,10 +41,10 @@ func TestCreateObject(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdCreate(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
@@ -73,10 +73,10 @@ func TestCreateMultipleObject(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdCreate(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
|
||||
cmd.Run(cmd, []string{})
|
||||
@@ -107,10 +107,10 @@ func TestCreateDirectory(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdCreate(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
|
||||
@@ -58,10 +58,13 @@ Examples:
|
||||
$ kubectl delete pod 1234-56-7890-234234-456456
|
||||
<delete a pod with ID 1234-56-7890-234234-456456>`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdNamespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
|
||||
mapper, typer := f.Object(cmd)
|
||||
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
|
||||
ContinueOnError().
|
||||
NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
FilenameParam(flags.Filenames...).
|
||||
SelectorParam(GetFlagString(cmd, "selector")).
|
||||
ResourceTypeOrNameArgs(args...).
|
||||
@@ -69,7 +72,7 @@ Examples:
|
||||
Do()
|
||||
|
||||
found := 0
|
||||
err := r.IgnoreErrors(errors.IsNotFound).Visit(func(r *resource.Info) error {
|
||||
err = r.IgnoreErrors(errors.IsNotFound).Visit(func(r *resource.Info) error {
|
||||
found++
|
||||
if err := resource.NewHelper(r.Client, r.Mapping).Delete(r.Namespace, r.Name); err != nil {
|
||||
return err
|
||||
|
||||
@@ -43,10 +43,10 @@ func TestDeleteObject(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdDelete(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
@@ -71,10 +71,10 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdDelete(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
@@ -98,12 +98,12 @@ func TestDeleteNoObjects(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
stderr := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdDelete(buf)
|
||||
cmd.SetOutput(stderr)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"pods"})
|
||||
|
||||
if buf.String() != "" {
|
||||
@@ -133,10 +133,10 @@ func TestDeleteMultipleObject(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdDelete(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
|
||||
cmd.Run(cmd, []string{})
|
||||
@@ -165,10 +165,10 @@ func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdDelete(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
|
||||
cmd.Run(cmd, []string{})
|
||||
@@ -199,10 +199,10 @@ func TestDeleteDirectory(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdDelete(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
@@ -240,10 +240,10 @@ func TestDeleteMultipleSelector(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdDelete(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().Set("selector", "a=b")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
|
||||
|
||||
@@ -32,8 +32,11 @@ func (f *Factory) NewCmdDescribe(out io.Writer) *cobra.Command {
|
||||
This command joins many API calls together to form a detailed description of a
|
||||
given resource.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdNamespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
|
||||
mapper, _ := f.Object(cmd)
|
||||
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper)
|
||||
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
|
||||
|
||||
describer, err := f.Describer(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
@@ -34,14 +34,13 @@ func TestDescribeUnknownSchemaObject(t *testing.T) {
|
||||
Codec: codec,
|
||||
Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &internalType{Name: "foo"})},
|
||||
}
|
||||
tf.Namespace = "non-default"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdDescribe(buf)
|
||||
cmd.Flags().String("api-version", "default", "")
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"type", "foo"})
|
||||
|
||||
if d.Name != "foo" || d.Namespace != "test" {
|
||||
if d.Name != "foo" || d.Namespace != "non-default" {
|
||||
t.Errorf("unexpected describer: %#v", d)
|
||||
}
|
||||
|
||||
|
||||
@@ -73,11 +73,14 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
|
||||
selector := 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")
|
||||
if isWatch || isWatchOnly {
|
||||
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
|
||||
NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
SelectorParam(selector).
|
||||
ResourceTypeOrNameArgs(args...).
|
||||
SingleResourceType().
|
||||
@@ -113,7 +116,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
b := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
|
||||
NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
SelectorParam(selector).
|
||||
ResourceTypeOrNameArgs(args...).
|
||||
Latest()
|
||||
@@ -121,8 +124,12 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
|
||||
checkErr(err)
|
||||
|
||||
if generic {
|
||||
clientConfig, err := f.ClientConfig(cmd)
|
||||
checkErr(err)
|
||||
defaultVersion := clientConfig.Version
|
||||
|
||||
// the outermost object will be converted to the output-version
|
||||
version := outputVersion(cmd)
|
||||
version := 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).
|
||||
|
||||
@@ -69,12 +69,12 @@ func TestGetUnknownSchemaObject(t *testing.T) {
|
||||
Codec: codec,
|
||||
Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &internalType{Name: "foo"})},
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &client.Config{Version: latest.Version}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("api-version", "default", "")
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"type", "foo"})
|
||||
|
||||
expected := &internalType{Name: "foo"}
|
||||
@@ -98,11 +98,11 @@ func TestGetSchemaObject(t *testing.T) {
|
||||
Codec: codec,
|
||||
Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &api.ReplicationController{ObjectMeta: api.ObjectMeta{Name: "foo"}})},
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &client.Config{Version: "v1beta3"}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.Flags().String("api-version", "v1beta3", "")
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"replicationcontrollers", "foo"})
|
||||
|
||||
if !strings.Contains(buf.String(), "\"foo\"") {
|
||||
@@ -119,11 +119,11 @@ func TestGetObjects(t *testing.T) {
|
||||
Codec: codec,
|
||||
Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])},
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"pods", "foo"})
|
||||
|
||||
expected := []runtime.Object{&pods.Items[0]}
|
||||
@@ -145,11 +145,11 @@ func TestGetListObjects(t *testing.T) {
|
||||
Codec: codec,
|
||||
Resp: &http.Response{StatusCode: 200, Body: objBody(codec, pods)},
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"pods"})
|
||||
|
||||
expected := []runtime.Object{pods}
|
||||
@@ -181,11 +181,11 @@ func TestGetMultipleTypeObjects(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
|
||||
expected := []runtime.Object{pods, svc}
|
||||
@@ -217,12 +217,12 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &client.Config{Version: "v1beta1"}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().String("api-version", "v1beta1", "")
|
||||
|
||||
cmd.Flags().Set("output", "json")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
@@ -269,11 +269,11 @@ func TestGetMultipleTypeObjectsWithSelector(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
|
||||
cmd.Flags().Set("selector", "a=b")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
@@ -345,11 +345,11 @@ func TestWatchSelector(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
|
||||
cmd.Flags().Set("watch", "true")
|
||||
cmd.Flags().Set("selector", "a=b")
|
||||
@@ -384,11 +384,11 @@ func TestWatchResource(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
|
||||
cmd.Flags().Set("watch", "true")
|
||||
cmd.Run(cmd, []string{"pods", "foo"})
|
||||
@@ -422,11 +422,11 @@ func TestWatchOnlyResource(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
|
||||
cmd.Flags().Set("watch-only", "true")
|
||||
cmd.Run(cmd, []string{"pods", "foo"})
|
||||
|
||||
@@ -43,7 +43,8 @@ Examples:
|
||||
usageError(cmd, "log <pod> [<container>]")
|
||||
}
|
||||
|
||||
namespace := GetKubeNamespace(cmd)
|
||||
namespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
client, err := f.Client(cmd)
|
||||
checkErr(err)
|
||||
|
||||
|
||||
@@ -56,10 +56,10 @@ func PrintObject(cmd *cobra.Command, obj runtime.Object, f *Factory, out io.Writ
|
||||
}
|
||||
|
||||
// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
|
||||
func outputVersion(cmd *cobra.Command) string {
|
||||
func outputVersion(cmd *cobra.Command, defaultVersion string) string {
|
||||
outputVersion := GetFlagString(cmd, "output-version")
|
||||
if len(outputVersion) == 0 {
|
||||
outputVersion = GetFlagString(cmd, "api-version")
|
||||
outputVersion = defaultVersion
|
||||
}
|
||||
return outputVersion
|
||||
}
|
||||
@@ -84,7 +84,11 @@ func PrinterForMapping(f *Factory, cmd *cobra.Command, mapping *meta.RESTMapping
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
version := outputVersion(cmd)
|
||||
clientConfig, err := f.ClientConfig(cmd)
|
||||
checkErr(err)
|
||||
defaultVersion := clientConfig.Version
|
||||
|
||||
version := outputVersion(cmd, defaultVersion)
|
||||
if len(version) == 0 {
|
||||
version = mapping.APIVersion
|
||||
}
|
||||
|
||||
@@ -48,8 +48,12 @@ Examples:
|
||||
if len(args) != 2 || count < 0 {
|
||||
usageError(cmd, "--replicas=<count> <resource> <id>")
|
||||
}
|
||||
|
||||
cmdNamespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
|
||||
mapper, _ := f.Object(cmd)
|
||||
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper)
|
||||
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
|
||||
|
||||
resizer, err := f.Resizer(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
@@ -41,18 +41,20 @@ func ResourcesFromArgsOrFile(
|
||||
clientBuilder func(cmd *cobra.Command, mapping *meta.RESTMapping) (resource.RESTClient, error),
|
||||
schema validation.Schema,
|
||||
requireNames bool,
|
||||
cmdNamespace,
|
||||
cmdVersion string,
|
||||
) resource.Visitor {
|
||||
|
||||
// handling filename & resource id
|
||||
if len(selector) == 0 {
|
||||
if requireNames || len(filename) > 0 {
|
||||
mapping, namespace, name := ResourceFromArgsOrFile(cmd, args, filename, typer, mapper, schema)
|
||||
mapping, namespace, name := ResourceFromArgsOrFile(cmd, args, filename, typer, mapper, schema, cmdNamespace, cmdVersion)
|
||||
client, err := clientBuilder(cmd, mapping)
|
||||
checkErr(err)
|
||||
return resource.NewInfo(client, mapping, namespace, name)
|
||||
}
|
||||
if len(args) == 2 {
|
||||
mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, mapper)
|
||||
mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, mapper, cmdNamespace, cmdVersion)
|
||||
client, err := clientBuilder(cmd, mapping)
|
||||
checkErr(err)
|
||||
return resource.NewInfo(client, mapping, namespace, name)
|
||||
@@ -62,7 +64,7 @@ func ResourcesFromArgsOrFile(
|
||||
labelSelector, err := labels.ParseSelector(selector)
|
||||
checkErr(err)
|
||||
|
||||
namespace := GetKubeNamespace(cmd)
|
||||
namespace := cmdNamespace
|
||||
visitors := resource.VisitorList{}
|
||||
|
||||
if len(args) < 1 {
|
||||
@@ -94,7 +96,7 @@ func ResourcesFromArgsOrFile(
|
||||
// ResourceFromArgsOrFile expects two arguments or a valid file with a given type, and extracts
|
||||
// the fields necessary to uniquely locate a resource. Displays a usageError if that contract is
|
||||
// not satisfied, or a generic error if any other problems occur.
|
||||
func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper, schema validation.Schema) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper, schema validation.Schema, cmdNamespace, cmdVersion string) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
// If command line args are passed in, use those preferentially.
|
||||
if len(args) > 0 && len(args) != 2 {
|
||||
usageError(cmd, "If passing in command line parameters, must be resource and name")
|
||||
@@ -102,7 +104,7 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
|
||||
|
||||
if len(args) == 2 {
|
||||
resource := args[0]
|
||||
namespace = GetKubeNamespace(cmd)
|
||||
namespace = cmdNamespace
|
||||
name = args[1]
|
||||
if len(name) == 0 || len(resource) == 0 {
|
||||
usageError(cmd, "Must specify filename or command line params")
|
||||
@@ -113,8 +115,7 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
|
||||
// The error returned by mapper is "no resource defined", which is a usage error
|
||||
usageError(cmd, err.Error())
|
||||
}
|
||||
version := GetFlagString(cmd, "api-version")
|
||||
mapping, err = mapper.RESTMapping(kind, version, defaultVersion)
|
||||
mapping, err = mapper.RESTMapping(kind, cmdVersion, defaultVersion)
|
||||
checkErr(err)
|
||||
return
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
|
||||
usageError(cmd, "Must specify filename or command line params")
|
||||
}
|
||||
|
||||
mapping, namespace, name, _ = ResourceFromFile(cmd, filename, typer, mapper, schema)
|
||||
mapping, namespace, name, _ = ResourceFromFile(filename, typer, mapper, schema, cmdVersion)
|
||||
if len(name) == 0 {
|
||||
checkErr(fmt.Errorf("the resource in the provided file has no name (or ID) defined"))
|
||||
}
|
||||
@@ -134,13 +135,13 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
|
||||
// ResourceFromArgs expects two arguments with a given type, and extracts the fields necessary
|
||||
// to uniquely locate a resource. Displays a usageError if that contract is not satisfied, or
|
||||
// a generic error if any other problems occur.
|
||||
func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper, cmdNamespace string) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
if len(args) != 2 {
|
||||
usageError(cmd, "Must provide resource and name command line params")
|
||||
}
|
||||
|
||||
resource := args[0]
|
||||
namespace = GetKubeNamespace(cmd)
|
||||
namespace = cmdNamespace
|
||||
name = args[1]
|
||||
if len(name) == 0 || len(resource) == 0 {
|
||||
usageError(cmd, "Must provide resource and name command line params")
|
||||
@@ -157,7 +158,7 @@ func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper)
|
||||
// ResourceFromArgs expects two arguments with a given type, and extracts the fields necessary
|
||||
// to uniquely locate a resource. Displays a usageError if that contract is not satisfied, or
|
||||
// a generic error if any other problems occur.
|
||||
func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper, cmdNamespace, cmdVersion string) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
if len(args) == 0 || len(args) > 2 {
|
||||
usageError(cmd, "Must provide resource or a resource and name as command line params")
|
||||
}
|
||||
@@ -167,7 +168,7 @@ func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTM
|
||||
usageError(cmd, "Must provide resource or a resource and name as command line params")
|
||||
}
|
||||
|
||||
namespace = GetKubeNamespace(cmd)
|
||||
namespace = cmdNamespace
|
||||
if len(args) == 2 {
|
||||
name = args[1]
|
||||
if len(name) == 0 {
|
||||
@@ -178,8 +179,7 @@ func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTM
|
||||
defaultVersion, kind, err := mapper.VersionAndKindForResource(resource)
|
||||
checkErr(err)
|
||||
|
||||
version := GetFlagString(cmd, "api-version")
|
||||
mapping, err = mapper.RESTMapping(kind, version, defaultVersion)
|
||||
mapping, err = mapper.RESTMapping(kind, cmdVersion, defaultVersion)
|
||||
checkErr(err)
|
||||
|
||||
return
|
||||
@@ -188,7 +188,7 @@ func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTM
|
||||
// ResourceFromFile retrieves the name and namespace from a valid file. If the file does not
|
||||
// resolve to a known type an error is returned. The returned mapping can be used to determine
|
||||
// the correct REST endpoint to modify this resource with.
|
||||
func ResourceFromFile(cmd *cobra.Command, filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper, schema validation.Schema) (mapping *meta.RESTMapping, namespace, name string, data []byte) {
|
||||
func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper, schema validation.Schema, cmdVersion string) (mapping *meta.RESTMapping, namespace, name string, data []byte) {
|
||||
configData, err := ReadConfigData(filename)
|
||||
checkErr(err)
|
||||
data = configData
|
||||
@@ -218,20 +218,18 @@ func ResourceFromFile(cmd *cobra.Command, filename string, typer runtime.ObjectT
|
||||
checkErr(err)
|
||||
|
||||
// if the preferred API version differs, get a different mapper
|
||||
version := GetFlagString(cmd, "api-version")
|
||||
if version != objVersion {
|
||||
mapping, err = mapper.RESTMapping(kind, version)
|
||||
if cmdVersion != objVersion {
|
||||
mapping, err = mapper.RESTMapping(kind, cmdVersion)
|
||||
checkErr(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CompareNamespaceFromFile returns an error if the namespace the user has provided on the CLI
|
||||
// CompareNamespace returns an error if the namespace the user has provided on the CLI
|
||||
// or via the default namespace file does not match the namespace of an input file. This
|
||||
// prevents a user from unintentionally updating the wrong namespace.
|
||||
func CompareNamespaceFromFile(cmd *cobra.Command, namespace string) error {
|
||||
defaultNamespace := GetKubeNamespace(cmd)
|
||||
func CompareNamespace(defaultNamespace, namespace string) error {
|
||||
if len(namespace) > 0 {
|
||||
if defaultNamespace != namespace {
|
||||
return fmt.Errorf("the namespace from the provided file %q does not match the namespace %q. You must pass '--namespace=%s' to perform this operation.", namespace, defaultNamespace, namespace)
|
||||
|
||||
@@ -61,8 +61,13 @@ $ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f -
|
||||
oldName := args[0]
|
||||
schema, err := f.Validator(cmd)
|
||||
checkErr(err)
|
||||
|
||||
clientConfig, err := f.ClientConfig(cmd)
|
||||
checkErr(err)
|
||||
cmdApiVersion := clientConfig.Version
|
||||
|
||||
mapper, typer := f.Object(cmd)
|
||||
mapping, namespace, newName, data := ResourceFromFile(cmd, filename, typer, mapper, schema)
|
||||
mapping, namespace, newName, data := ResourceFromFile(filename, typer, mapper, schema, cmdApiVersion)
|
||||
if mapping.Kind != "ReplicationController" {
|
||||
usageError(cmd, "%s does not specify a valid ReplicationController", filename)
|
||||
}
|
||||
@@ -70,7 +75,10 @@ $ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f -
|
||||
usageError(cmd, "%s cannot have the same name as the existing ReplicationController %s",
|
||||
filename, oldName)
|
||||
}
|
||||
err = CompareNamespaceFromFile(cmd, namespace)
|
||||
|
||||
cmdNamespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
err = CompareNamespace(cmdNamespace, namespace)
|
||||
checkErr(err)
|
||||
|
||||
client, err := f.Client(cmd)
|
||||
|
||||
@@ -49,7 +49,9 @@ Examples:
|
||||
usageError(cmd, "<name> is required for run")
|
||||
}
|
||||
|
||||
namespace := GetKubeNamespace(cmd)
|
||||
namespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
|
||||
client, err := f.Client(cmd)
|
||||
checkErr(err)
|
||||
|
||||
|
||||
56
pkg/kubectl/cmd/stop.go
Normal file
56
pkg/kubectl/cmd/stop.go
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func (f *Factory) NewCmdStop(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "stop <resource> <id>",
|
||||
Short: "Gracefully shutdown a resource",
|
||||
Long: `Gracefully shutdown a resource
|
||||
|
||||
Attempts to shutdown and delete a resource that supports graceful termination.
|
||||
If the resource is resizable it will be resized to 0 before deletion.
|
||||
|
||||
Examples:
|
||||
$ kubectl stop replicationcontroller foo
|
||||
foo stopped
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
usageError(cmd, "<resource> <id>")
|
||||
}
|
||||
cmdNamespace, err := f.DefaultNamespace(cmd)
|
||||
mapper, _ := f.Object(cmd)
|
||||
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
|
||||
|
||||
reaper, err := f.Reaper(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
s, err := reaper.Stop(namespace, name)
|
||||
checkErr(err)
|
||||
fmt.Fprintf(out, "%s\n", s)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -56,6 +56,7 @@ Examples:
|
||||
} else {
|
||||
name = updateWithPatch(cmd, args, f, patch)
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "%s\n", name)
|
||||
},
|
||||
}
|
||||
@@ -65,8 +66,11 @@ Examples:
|
||||
}
|
||||
|
||||
func updateWithPatch(cmd *cobra.Command, args []string, f *Factory, patch string) string {
|
||||
cmdNamespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
|
||||
mapper, _ := f.Object(cmd)
|
||||
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper)
|
||||
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
|
||||
client, err := f.RESTClient(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
@@ -89,12 +93,18 @@ func updateWithFile(cmd *cobra.Command, f *Factory, filename string) string {
|
||||
checkErr(err)
|
||||
mapper, typer := f.Object(cmd)
|
||||
|
||||
mapping, namespace, name, data := ResourceFromFile(cmd, filename, typer, mapper, schema)
|
||||
clientConfig, err := f.ClientConfig(cmd)
|
||||
checkErr(err)
|
||||
cmdApiVersion := clientConfig.Version
|
||||
|
||||
mapping, namespace, name, data := ResourceFromFile(filename, typer, mapper, schema, cmdApiVersion)
|
||||
|
||||
client, err := f.RESTClient(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
err = CompareNamespaceFromFile(cmd, namespace)
|
||||
cmdNamespace, err := f.DefaultNamespace(cmd)
|
||||
checkErr(err)
|
||||
err = CompareNamespace(cmdNamespace, namespace)
|
||||
checkErr(err)
|
||||
|
||||
err = resource.NewHelper(client, mapping).Update(namespace, name, true, data)
|
||||
|
||||
@@ -58,12 +58,12 @@ type Resizer interface {
|
||||
Resize(namespace, name string, preconditions *ResizePrecondition, newSize uint) (string, error)
|
||||
}
|
||||
|
||||
func ResizerFor(kind string, c *client.Client) (Resizer, bool) {
|
||||
func ResizerFor(kind string, c client.Interface) (Resizer, error) {
|
||||
switch kind {
|
||||
case "ReplicationController":
|
||||
return &ReplicationControllerResizer{c}, true
|
||||
return &ReplicationControllerResizer{c}, nil
|
||||
}
|
||||
return nil, false
|
||||
return nil, fmt.Errorf("no resizer has been implemented for %q", kind)
|
||||
}
|
||||
|
||||
type ReplicationControllerResizer struct {
|
||||
|
||||
@@ -26,18 +26,18 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
)
|
||||
|
||||
type customFake struct {
|
||||
type updaterFake struct {
|
||||
*client.Fake
|
||||
ctrl client.ReplicationControllerInterface
|
||||
}
|
||||
|
||||
func (c *customFake) ReplicationControllers(namespace string) client.ReplicationControllerInterface {
|
||||
func (c *updaterFake) ReplicationControllers(namespace string) client.ReplicationControllerInterface {
|
||||
return c.ctrl
|
||||
}
|
||||
|
||||
func fakeClientFor(namespace string, responses []fakeResponse) client.Interface {
|
||||
fake := client.Fake{}
|
||||
return &customFake{
|
||||
return &updaterFake{
|
||||
&fake,
|
||||
&fakeRc{
|
||||
&client.FakeReplicationControllers{
|
||||
|
||||
110
pkg/kubectl/stop.go
Normal file
110
pkg/kubectl/stop.go
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 kubectl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
interval = time.Second * 3
|
||||
timeout = time.Minute * 5
|
||||
)
|
||||
|
||||
// A Reaper handles terminating an object as gracefully as possible.
|
||||
type Reaper interface {
|
||||
Stop(namespace, name string) (string, error)
|
||||
}
|
||||
|
||||
func ReaperFor(kind string, c client.Interface) (Reaper, error) {
|
||||
switch kind {
|
||||
case "ReplicationController":
|
||||
return &ReplicationControllerReaper{c, interval, timeout}, nil
|
||||
case "Pod":
|
||||
return &PodReaper{c}, nil
|
||||
case "Service":
|
||||
return &ServiceReaper{c}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no reaper has been implemented for %q", kind)
|
||||
}
|
||||
|
||||
type ReplicationControllerReaper struct {
|
||||
client.Interface
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
type PodReaper struct {
|
||||
client.Interface
|
||||
}
|
||||
type ServiceReaper struct {
|
||||
client.Interface
|
||||
}
|
||||
|
||||
type objInterface interface {
|
||||
Delete(name string) error
|
||||
Get(name string) (meta.Interface, error)
|
||||
}
|
||||
|
||||
func (reaper *ReplicationControllerReaper) Stop(namespace, name string) (string, error) {
|
||||
rc := reaper.ReplicationControllers(namespace)
|
||||
controller, err := rc.Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
controller.Spec.Replicas = 0
|
||||
// TODO: do retry on 409 errors here?
|
||||
if _, err := rc.Update(controller); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := wait.Poll(reaper.pollInterval, reaper.timeout,
|
||||
client.ControllerHasDesiredReplicas(reaper, controller)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := rc.Delete(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s stopped", name), nil
|
||||
}
|
||||
|
||||
func (reaper *PodReaper) Stop(namespace, name string) (string, error) {
|
||||
pods := reaper.Pods(namespace)
|
||||
_, err := pods.Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := pods.Delete(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s stopped", name), nil
|
||||
}
|
||||
|
||||
func (reaper *ServiceReaper) Stop(namespace, name string) (string, error) {
|
||||
services := reaper.Services(namespace)
|
||||
_, err := services.Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := services.Delete(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s stopped", name), nil
|
||||
}
|
||||
168
pkg/kubectl/stop_test.go
Normal file
168
pkg/kubectl/stop_test.go
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
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 kubectl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
)
|
||||
|
||||
func TestReplicationControllerStop(t *testing.T) {
|
||||
fake := &client.Fake{
|
||||
Ctrl: api.ReplicationController{
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Replicas: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
reaper := ReplicationControllerReaper{fake, time.Millisecond, time.Millisecond}
|
||||
name := "foo"
|
||||
s, err := reaper.Stop("default", name)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
expected := "foo stopped"
|
||||
if s != expected {
|
||||
t.Errorf("expected %s, got %s", expected, s)
|
||||
}
|
||||
if len(fake.Actions) != 4 {
|
||||
t.Errorf("unexpected actions: %v, expected 4 actions (get, update, get, delete)", fake.Actions)
|
||||
}
|
||||
for i, action := range []string{"get", "update", "get", "delete"} {
|
||||
if fake.Actions[i].Action != action+"-controller" {
|
||||
t.Errorf("unexpected action: %v, expected %s-controller", fake.Actions[i], action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type noSuchPod struct {
|
||||
*client.FakePods
|
||||
}
|
||||
|
||||
func (c *noSuchPod) Get(name string) (*api.Pod, error) {
|
||||
return nil, fmt.Errorf("%s does not exist", name)
|
||||
}
|
||||
|
||||
type noDeleteService struct {
|
||||
*client.FakeServices
|
||||
}
|
||||
|
||||
func (c *noDeleteService) Delete(service string) error {
|
||||
return fmt.Errorf("I'm afraid I can't do that, Dave")
|
||||
}
|
||||
|
||||
type reaperFake struct {
|
||||
*client.Fake
|
||||
noSuchPod, noDeleteService bool
|
||||
}
|
||||
|
||||
func (c *reaperFake) Pods(namespace string) client.PodInterface {
|
||||
pods := &client.FakePods{c.Fake, namespace}
|
||||
if c.noSuchPod {
|
||||
return &noSuchPod{pods}
|
||||
}
|
||||
return pods
|
||||
}
|
||||
|
||||
func (c *reaperFake) Services(namespace string) client.ServiceInterface {
|
||||
services := &client.FakeServices{c.Fake, namespace}
|
||||
if c.noDeleteService {
|
||||
return &noDeleteService{services}
|
||||
}
|
||||
return services
|
||||
}
|
||||
|
||||
func TestSimpleStop(t *testing.T) {
|
||||
tests := []struct {
|
||||
fake *reaperFake
|
||||
kind string
|
||||
actions []string
|
||||
expectError bool
|
||||
test string
|
||||
}{
|
||||
{
|
||||
fake: &reaperFake{
|
||||
Fake: &client.Fake{},
|
||||
},
|
||||
kind: "Pod",
|
||||
actions: []string{"get-pod", "delete-pod"},
|
||||
expectError: false,
|
||||
test: "stop pod succeeds",
|
||||
},
|
||||
{
|
||||
fake: &reaperFake{
|
||||
Fake: &client.Fake{},
|
||||
},
|
||||
kind: "Service",
|
||||
actions: []string{"get-service", "delete-service"},
|
||||
expectError: false,
|
||||
test: "stop service succeeds",
|
||||
},
|
||||
{
|
||||
fake: &reaperFake{
|
||||
Fake: &client.Fake{},
|
||||
noSuchPod: true,
|
||||
},
|
||||
kind: "Pod",
|
||||
actions: []string{},
|
||||
expectError: true,
|
||||
test: "stop pod fails, no pod",
|
||||
},
|
||||
{
|
||||
fake: &reaperFake{
|
||||
Fake: &client.Fake{},
|
||||
noDeleteService: true,
|
||||
},
|
||||
kind: "Service",
|
||||
actions: []string{"get-service"},
|
||||
expectError: true,
|
||||
test: "stop service fails, can't delete",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
fake := test.fake
|
||||
reaper, err := ReaperFor(test.kind, fake)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v (%s)", err, test.test)
|
||||
}
|
||||
s, err := reaper.Stop("default", "foo")
|
||||
if err != nil && !test.expectError {
|
||||
t.Errorf("unexpected error: %v (%s)", err, test.test)
|
||||
}
|
||||
if err == nil {
|
||||
if test.expectError {
|
||||
t.Errorf("unexpected non-error: %v (%s)", err, test.test)
|
||||
}
|
||||
if s != "foo stopped" {
|
||||
t.Errorf("unexpected return: %s (%s)", s, test.test)
|
||||
}
|
||||
}
|
||||
if len(test.actions) != len(fake.Actions) {
|
||||
t.Errorf("unexpected actions: %v; expected %v (%s)", fake.Actions, test.actions, test.test)
|
||||
}
|
||||
for i, action := range fake.Actions {
|
||||
testAction := test.actions[i]
|
||||
if action.Action != testAction {
|
||||
t.Errorf("unexpected action: %v; expected %v (%s)", action, testAction, test.test)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,6 +168,12 @@ func extractFromFile(filename string) (api.BoundPod, error) {
|
||||
pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:]))
|
||||
glog.V(5).Infof("Generated UID %q for pod %q from file %s", pod.UID, pod.Name, filename)
|
||||
}
|
||||
// This is required for backward compatibility, and should be removed once we
|
||||
// completely deprecate ContainerManifest.
|
||||
if len(pod.Name) == 0 {
|
||||
pod.Name = string(pod.UID)
|
||||
glog.V(5).Infof("Generated Name %q for UID %q from file %s", pod.Name, pod.UID, filename)
|
||||
}
|
||||
if len(pod.Namespace) == 0 {
|
||||
hasher := adler32.New()
|
||||
fmt.Fprint(hasher, filename)
|
||||
|
||||
@@ -151,6 +151,12 @@ func applyDefaults(pod *api.BoundPod, url string) {
|
||||
pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:]))
|
||||
glog.V(5).Infof("Generated UID %q for pod %q from URL %s", pod.UID, pod.Name, url)
|
||||
}
|
||||
// This is required for backward compatibility, and should be removed once we
|
||||
// completely deprecate ContainerManifest.
|
||||
if len(pod.Name) == 0 {
|
||||
pod.Name = string(pod.UID)
|
||||
glog.V(5).Infof("Generate Name %q from UID %q from URL %s", pod.Name, pod.UID, url)
|
||||
}
|
||||
if len(pod.Namespace) == 0 {
|
||||
hasher := adler32.New()
|
||||
fmt.Fprint(hasher, url)
|
||||
|
||||
@@ -138,6 +138,23 @@ func TestExtractFromHTTP(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
desc: "Single manifest without ID",
|
||||
manifests: api.ContainerManifest{Version: "v1beta1", UUID: "111"},
|
||||
expected: CreatePodUpdate(kubelet.SET,
|
||||
kubelet.HTTPSource,
|
||||
api.BoundPod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
UID: "111",
|
||||
Name: "111",
|
||||
Namespace: "foobar",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
desc: "Multiple manifests",
|
||||
manifests: []api.ContainerManifest{
|
||||
|
||||
@@ -36,8 +36,8 @@ package version
|
||||
var (
|
||||
// TODO: Deprecate gitMajor and gitMinor, use only gitVersion instead.
|
||||
gitMajor string = "0" // major version, always numeric
|
||||
gitMinor string = "9.0" // minor version, numeric possibly followed by "+"
|
||||
gitVersion string = "v0.9.0" // version from git, output of $(git describe)
|
||||
gitMinor string = "9.1+" // minor version, numeric possibly followed by "+"
|
||||
gitVersion string = "v0.9.1-dev" // version from git, output of $(git describe)
|
||||
gitCommit string = "" // sha1 from git, output of $(git rev-parse HEAD)
|
||||
gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user